diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+dist
+*.nix
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,82 @@
+# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
+language: c
+sudo: false
+
+cache:
+  directories:
+    - $HOME/.cabsnap
+    - $HOME/.cabal/packages
+
+before_cache:
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
+
+matrix:
+  include:
+    - env: CABALVER=1.22 GHCVER=7.10.2
+      compiler: ": #GHC 7.10.2"
+      addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}}
+    - env: CABALVER=1.22 GHCVER=7.10.3
+      compiler: ": #GHC 7.10.3"
+      addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
+    - env: CABALVER=1.24 GHCVER=8.0.1
+      compiler: ": #GHC 8.0.1"
+      addons: {apt: {packages: [cabal-install-1.24,ghc-8.0.1], sources: [hvr-ghc]}}
+
+before_install:
+ - unset CC
+ - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
+
+install:
+ - cabal --version
+ - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
+ - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
+   then
+     zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
+          $HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
+   fi
+ - travis_retry cabal update -v
+ - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
+ - cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt
+ - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
+
+# check whether current requested install-plan matches cached package-db snapshot
+ - if diff -u $HOME/.cabsnap/installplan.txt installplan.txt;
+   then
+     echo "cabal build-cache HIT";
+     rm -rfv .ghc;
+     cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
+     cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
+   else
+     echo "cabal build-cache MISS";
+     rm -rf $HOME/.cabsnap;
+     mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
+     cabal install --only-dependencies --enable-tests --enable-benchmarks;
+   fi
+
+# snapshot package-db on cache miss
+ - if [ ! -d $HOME/.cabsnap ];
+   then
+      echo "snapshotting package-db to build-cache";
+      mkdir $HOME/.cabsnap;
+      cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
+      cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
+   fi
+
+# Here starts the actual work to be performed for the package under test;
+# any command which exits with a non-zero exit code causes the build to fail.
+script:
+ - if [ -f configure.ac ]; then autoreconf -i; fi
+ - cabal configure --enable-tests --enable-benchmarks -v2  # -v2 provides useful information for debugging
+ - cabal build   # this builds all libraries and executables (including tests/benchmarks)
+ - cabal test
+ - cabal check
+ - cabal sdist   # tests that a source-distribution can be generated
+
+# Check that the resulting source distribution can be built & installed.
+# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
+# `cabal install --force-reinstalls dist/*-*.tar.gz`
+ - SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
+   (cd dist && cabal install --force-reinstalls "$SRC_TGZ")
+
+# EOF
diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for dwergaz
+
+## 0.1.0.0  -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017, Henry Till
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Henry Till nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# dwergaz
+
+[![Build Status](https://travis-ci.org/xngns/dwergaz.svg?branch=master)](https://travis-ci.org/xngns/dwergaz)
+
+`dwergaz` is a minimal testing library with no external dependencies.
+
+See the [tests](tests/Main.hs) for a usage example.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/dwergaz.cabal b/dwergaz.cabal
new file mode 100644
--- /dev/null
+++ b/dwergaz.cabal
@@ -0,0 +1,45 @@
+-- Initial dwergaz.cabal generated by cabal init.  For further
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                dwergaz
+version:             0.2.0.0
+synopsis:            A minimal testing library
+description:         A minimal testing library
+license:             BSD3
+license-file:        LICENSE
+author:              Henry Till
+maintainer:          henrytill@gmail.com
+homepage:            https://github.com/xngns/dwergaz
+copyright:           Copyright (c) 2017, Henry Till
+category:            Testing
+build-type:          Simple
+cabal-version:       >=1.10
+tested-with:         GHC == 7.10.2, GHC == 7.10.3, GHC == 8.0.1
+
+extra-source-files:
+  .gitignore
+  .travis.yml
+  ChangeLog.md
+  README.md
+
+source-repository head
+  type:     git
+  location: https://github.com/xngns/dwergaz.git
+
+library
+  exposed-modules:     Test.Dwergaz
+  -- other-modules:
+  -- other-extensions:
+  build-depends:       base >=4.8 && <4.10
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+
+test-suite tests
+  type:                exitcode-stdio-1.0
+  main-is:             Main.hs
+  build-depends:       base >=4.8 && <4.10
+                     , dwergaz
+  hs-source-dirs:      tests
+  default-language:    Haskell2010
+  ghc-options:         -Wall
diff --git a/src/Test/Dwergaz.hs b/src/Test/Dwergaz.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/Dwergaz.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE GADTs #-}
+
+-- |
+-- Module      : Test.Dwergaz
+-- Description : A minimal testing library
+-- Copyright   : (c) 2017, Henry Till
+-- License     : BSD3
+-- Maintainer  : henrytill@gmail.com
+-- Stability   : experimental
+--
+-- = Usage:
+--
+-- See the <https://github.com/xngns/dwergaz/blob/master/tests/Main.hs tests> for a usage example.
+--
+
+module Test.Dwergaz
+  ( Test(..)
+  , Result
+  , isPassed
+  , runTest
+  ) where
+
+data Test where
+  Predicate :: (Eq a, Show a) => String -> (a -> Bool) -> a -> Test
+  Expect    :: (Eq a, Show a) => String -> (a -> a -> Bool) -> a -> a -> Test
+
+data Result where
+  Passed :: String -> Result
+  Failed :: Show a => String -> a -> a -> Result
+
+instance Show Result where
+  show (Failed n e a) =
+    "FAILED:   "     ++ n      ++
+    "\nEXPECTED:   " ++ show e ++
+    "\nACTUAL:     " ++ show a
+  show (Passed n) =
+    "PASSED:   "     ++ n
+
+isPassed :: Result -> Bool
+isPassed (Passed _) = True
+isPassed _          = False
+
+runTest :: Test -> Result
+runTest (Predicate n p v)
+  | p v                   = Passed n
+  | otherwise             = Failed n True False
+runTest (Expect n f e a)
+  | f e a                 = Passed n
+  | otherwise             = Failed n e a
diff --git a/tests/Main.hs b/tests/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/Main.hs
@@ -0,0 +1,42 @@
+module Main where
+
+import Control.Monad (unless)
+import Data.Either (isLeft, isRight)
+import System.Exit (exitFailure)
+import Test.Dwergaz
+
+testFun01 :: a -> String
+testFun01 = const "quux"
+
+testFun02 :: a -> Int
+testFun02 = const 42
+
+testFun03 :: a -> Either String String
+testFun03 = const (Left "quux")
+
+testFun04 :: a -> Either String Int
+testFun04 = const (Right 42)
+
+expectExample01, expectExample02 :: Test
+expectExample01 = Expect "Strings are equal" (==) "quux" (testFun01 "quux")
+expectExample02 = Expect "Ints are equal"    (==) 42     (testFun02 "quux")
+
+predicateExample01, predicateExample02 :: Test
+predicateExample01 = Predicate "Value is a Left"  isLeft  (testFun03 "quux")
+predicateExample02 = Predicate "Value is a Right" isRight (testFun04 "quux")
+
+exampleTests :: [Test]
+exampleTests =
+  [ expectExample01
+  , expectExample02
+  , predicateExample01
+  , predicateExample02
+  ]
+
+results :: [Result]
+results = fmap runTest exampleTests
+
+main :: IO ()
+main =  do
+  _ <- mapM_ print results
+  unless (all isPassed results) exitFailure
