diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011, 2012 Simon Hengel <sol@typeful.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/src/Test/HUnit/ShouldBe.hs b/src/Test/HUnit/ShouldBe.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/HUnit/ShouldBe.hs
@@ -0,0 +1,94 @@
+-- |
+-- Introductory documentation: <https://github.com/sol/test-shouldbe#readme>
+module Test.HUnit.ShouldBe (
+
+-- * Making assertions
+  shouldBe
+, shouldSatisfy
+
+-- * Checking for exceptions
+, shouldThrow
+
+-- ** Selecting exceptions
+, Selector
+
+-- ** Predefined type-based selectors
+-- |
+-- There are predefined selectors for some standard exceptions.  Each selector
+-- is just @const True@ with an appropriate type.
+, anyException
+, anyErrorCall
+, anyIOException
+, anyArithException
+
+-- ** Combinators for defining value-based selectors
+-- |
+-- Some exceptions (most prominently `ErrorCall`) have no `Eq` instance.
+-- Selecting a specific value would require pattern matching.
+--
+-- For such exceptions, combinators that construct selectors are provided.
+-- Each combinator corresponds to a constructor; it takes the same arguments,
+-- and has the same name (but starting with a lower-case letter).
+, errorCall
+) where
+
+import           Prelude hiding (catch)
+import           Test.HUnit
+import           Control.Exception
+import           Data.Typeable
+import           Control.Arrow ((&&&))
+
+infix 1 `shouldBe`, `shouldSatisfy`, `shouldThrow`
+
+-- |
+-- @actual \`shouldBe\` expected@ asserts that @actual@ is equal to @expected@
+-- (this is just an alias for `@?=`).
+shouldBe :: (Show a, Eq a) => a -> a -> Assertion
+actual `shouldBe` expected = actual @?= expected
+
+-- |
+-- @v \`shouldSatisfy\` p@ asserts that @p v@ is @True@.
+shouldSatisfy :: (Show a) => a -> (a -> Bool) -> Assertion
+v `shouldSatisfy` p = assertBool (show v ++ " did not satisfy predicate!") (p v)
+
+
+-- |
+-- A @Selector@ is a predicate; it can simultaneously constrain the type and
+-- value of an exception.
+type Selector a = (a -> Bool)
+
+-- |
+-- @action \`shouldThrow\` selector@ asserts that @action@ throws an exception.
+-- The precise nature of that exception is described with a 'Selector'.
+shouldThrow :: Exception e => IO a -> Selector e -> Assertion
+action `shouldThrow` p = do
+  m <- (action >> return Nothing) `catch` (return . Just . (p &&& id))
+  case m of
+    Nothing ->
+      assertFailure msgNothing
+    Just (r, e) ->
+      assertBool (msgFailure e) r
+  where
+    msgNothing = "did not get expected exception: "
+            ++ (show . typeOf . instanceOf $ p)
+
+    msgFailure e = "predicate failed on expected exception: "
+            ++ (show . typeOf $ e) ++ " (" ++ show e ++ ")"
+
+    instanceOf :: Selector a -> a
+    instanceOf _ = error "Test.HUnit.ShouldBe.shouldThrow: brocken Typeable instance"
+
+anyException :: Selector SomeException
+anyException = const True
+
+anyErrorCall :: Selector ErrorCall
+anyErrorCall = const True
+
+errorCall :: String -> Selector ErrorCall
+errorCall s (ErrorCall msg) = s == msg
+
+anyIOException :: Selector IOException
+anyIOException = const True
+
+anyArithException :: Selector ArithException
+anyArithException = const True
diff --git a/src/Test/HUnit/ShouldBe/Contrib.hs b/src/Test/HUnit/ShouldBe/Contrib.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/HUnit/ShouldBe/Contrib.hs
@@ -0,0 +1,22 @@
+-- |
+-- Experimental combinators, that may become part of the main distribution, if
+-- they turn out to be useful for a wider audience.
+module Test.HUnit.ShouldBe.Contrib (
+
+  module Test.HUnit.ShouldBe
+
+-- * Predicates
+-- | (useful in combination with `shouldSatisfy`)
+, isLeft
+, isRight
+) where
+
+import Test.HUnit.ShouldBe
+
+isLeft :: Either a b -> Bool
+isLeft (Left  _) = True
+isLeft (Right _) = False
+
+isRight :: Either a b -> Bool
+isRight (Left  _) = False
+isRight (Right _) = True
diff --git a/test-shouldbe.cabal b/test-shouldbe.cabal
new file mode 100644
--- /dev/null
+++ b/test-shouldbe.cabal
@@ -0,0 +1,28 @@
+name:             test-shouldbe
+version:          0.1.0
+synopsis:         Catchy combinators for HUnit
+description:      Catchy combinators for HUnit: <https://github.com/sol/test-shouldbe#readme>
+license:          MIT
+license-file:     LICENSE
+copyright:        (c) 2011, 2012 Simon Hengel
+author:           Simon Hengel <sol@typeful.net>
+maintainer:       Simon Hengel <sol@typeful.net>
+build-type:       Simple
+category:         Testing
+cabal-version:    >= 1.6
+
+source-repository head
+  type: git
+  location: https://github.com/sol/test-shouldbe
+
+library
+  ghc-options:
+      -Wall
+  build-depends:
+      base < 4.6
+    , HUnit
+  hs-source-dirs:
+      src
+  exposed-modules:
+      Test.HUnit.ShouldBe
+    , Test.HUnit.ShouldBe.Contrib
