diff --git a/Specs.hs b/Specs.hs
--- a/Specs.hs
+++ b/Specs.hs
@@ -2,7 +2,7 @@
 module Specs where
 
 import Test.Hspec
-import Test.Hspec.Runner (hHspecWithFormat)
+import Test.Hspec.Runner (hHspecWithFormat, toExitCode)
 import Test.Hspec.Core (Spec(..),Result(..),quantify,failedCount)
 import Test.Hspec.Formatters (specdoc)
 import Test.Hspec.QuickCheck
@@ -10,13 +10,9 @@
 import System.IO
 import System.IO.Silently
 import System.Environment
-import System.Exit
+import System.Exit (exitWith)
 import Data.List (isPrefixOf)
 import qualified Test.HUnit as HUnit
-
-toExitCode :: Bool -> ExitCode
-toExitCode True  = ExitSuccess
-toExitCode False = ExitFailure 1
 
 main :: IO ()
 main = do
diff --git a/Test/Hspec.hs b/Test/Hspec.hs
new file mode 100644
--- /dev/null
+++ b/Test/Hspec.hs
@@ -0,0 +1,85 @@
+
+-- | Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is an approach
+-- to software development that combines Test-Driven Development, Domain Driven Design, and
+-- Acceptance Test-Driven Planning. Hspec helps you do the TDD part of that equation, focusing
+-- on the documentation and design aspects of TDD.
+--
+-- Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of what applies to
+-- RSpec also applies to Hspec. Hspec ties together /descriptions/ of behavior and /examples/ of
+-- that behavior. The examples can also be run as tests and the output summarises what needs to
+-- be implemented.
+--
+-- The three functions you'll use the most are 'hspec', 'describe', and 'it'. Here is an
+-- example of functions that format and unformat phone numbers and the specs for them.
+--
+-- > import Test.Hspec
+-- > import Test.Hspec.QuickCheck
+-- > import Test.Hspec.HUnit
+-- > import Test.QuickCheck hiding (property)
+-- > import Test.HUnit
+-- >
+-- > main = hspec mySpecs
+--
+-- Since the specs are often used to tell you what to implement, it's best to start with
+-- undefined functions. Once we have some specs, then you can implement each behavior
+-- one at a time, ensuring that each behavior is met and there is no undocumented behavior.
+--
+-- > unformatPhoneNumber :: String -> String
+-- > unformatPhoneNumber number = undefined
+-- >
+-- > formatPhoneNumber :: String -> String
+-- > formatPhoneNumber number = undefined
+--
+-- The "describe" function takes a list of behaviors and examples bound together with the "it" function
+--
+-- > mySpecs = describe "unformatPhoneNumber" [
+--
+-- A boolean expression can act as a behavior's example.
+--
+-- >   it "removes dashes, spaces, and parenthesies"
+-- >       (unformatPhoneNumber "(555) 555-1234" == "5555551234"),
+--
+-- The "pending" function marks a behavior as pending an example. The example doesn't count as failing.
+--
+-- >   it "handles non-US phone numbers"
+-- >       (pending "need to look up how other cultures format phone numbers"),
+--
+-- An HUnit "Test" can act as a behavior's example. (must import @Test.Hspec.HUnit@)
+--
+-- >   it "removes the \"ext\" prefix of the extension"
+-- >       (TestCase $ let expected = "5555551234135"
+-- >                       actual   = unformatPhoneNumber "(555) 555-1234 ext 135"
+-- >                   in assertEqual "remove extension" expected actual),
+--
+-- An @IO()@ action is treated like an HUnit "TestCase". (must import @Test.Hspec.HUnit@)
+--
+-- >   it "converts letters to numbers"
+-- >       (do
+-- >         let expected = "6862377"
+-- >         let actual   = unformatPhoneNumber "NUMBERS"
+-- >         assertEqual "letters to numbers" expected actual),
+--
+-- The "property" function allows a QuickCheck property to act as an example. (must import @Test.Hspec.HUnit@)
+--
+-- >   it "can add and remove formatting without changing the number"
+-- >       (property $ forAll phoneNumber $
+-- >         \ n -> unformatPhoneNumber (formatPhoneNumber n) == n)
+-- >   ]
+-- >
+-- > phoneNumber :: Gen String
+-- > phoneNumber = do
+-- >   nums <- elements [7,10,11,12,13,14,15]
+-- >   vectorOf nums (elements "0123456789")
+--
+module Test.Hspec (
+  -- types
+  Spec(), Result(),Specs,
+  -- the main api
+  describe, it, hspec, hspecB, hspecX, pending, descriptions,
+  -- alternate "runner" functions
+  hHspec
+) where
+
+import Test.Hspec.Core
+import Test.Hspec.Runner
+
diff --git a/Test/Hspec/Core.hs b/Test/Hspec/Core.hs
new file mode 100644
--- /dev/null
+++ b/Test/Hspec/Core.hs
@@ -0,0 +1,114 @@
+{-# OPTIONS -XFlexibleInstances #-}
+
+-- | This module contains the core types, constructors, classes,
+-- instances, and utility functions common to hspec.
+--
+module Test.Hspec.Core where
+
+import System.IO
+import System.IO.Silently
+import Control.Exception
+import Control.Monad (liftM)
+
+-- | The result of running an example.
+data Result = Success | Fail String | Pending String
+  deriving Eq
+
+-- | Everything needed to specify and show a specific behavior.
+data Spec = Spec {
+                 -- | What is being tested, usually the name of a type.
+                 name::String,
+                 -- | The specific behavior being tested.
+                 requirement::String,
+                 -- | The status of this behavior.
+                 result::Result }
+
+data Formatter = Formatter { exampleGroupStarted :: Handle -> Spec -> IO (),
+                             examplePassed   :: Handle -> Spec -> [String] -> IO (),
+                             exampleFailed   :: Handle -> Spec -> [String] -> IO (),
+                             examplePending  :: Handle -> Spec -> [String] -> IO (),
+                             errorsFormatter :: Handle -> [String] -> IO (),
+                             footerFormatter :: Handle -> [Spec] -> Double -> IO () }
+
+
+-- | Create a set of specifications for a specific type being described.
+-- Once you know what you want specs for, use this.
+--
+-- > describe "abs" [
+-- >   it "returns a positive number given a negative number"
+-- >     (abs (-1) == 1)
+-- >   ]
+--
+describe :: String                -- ^ The name of what is being described, usually a function or type.
+         -> [IO (String, Result)] -- ^ A list of behaviors and examples, created by a list of 'it'.
+         -> IO [IO Spec]
+describe n = return . map (>>= \ (req, res) -> return (Spec n req res))
+
+-- | Combine a list of descriptions.
+descriptions :: [IO [IO Spec]] -> IO [IO Spec]
+descriptions = liftM concat . sequence
+
+-- | Evaluate a Result. Any exceptions (undefined, etc.) are treated as failures.
+safely :: Result -> IO Result
+safely f = Control.Exception.catch ok failed
+  where ok = silence $ f `seq` return f
+        failed e = return $ Fail (show (e :: SomeException))
+
+-- | Anything that can be used as an example of a behavior.
+class SpecVerifier a where
+  -- | Create a description and example of a behavior, a list of these
+  -- is used by 'describe'. Once you know what you want to specify, use this.
+  --
+  -- > describe "closeEnough" [
+  -- >   it "is true if two numbers are almost the same"
+  -- >     (1.001 `closeEnough` 1.002),
+  -- >
+  -- >   it "is false if two numbers are not almost the same"
+  -- >     (not $ 1.001 `closeEnough` 1.003)
+  -- >   ]
+  --
+  it :: String           -- ^ A description of this behavior.
+     -> a                -- ^ An example for this behavior.
+     -> IO (String, Result)
+
+instance SpecVerifier Bool where
+  it description example = do
+    r <- safely (if example then Success else Fail "")
+    return (description, r)
+
+instance SpecVerifier Result where
+  it description example = do
+    r <- safely example
+    return (description, r)
+
+-- | Declare an example as not successful or failing but pending some other work.
+-- If you want to report on a behavior but don't have an example yet, use this.
+--
+-- > describe "fancyFormatter" [
+-- >   it "can format text in a way that everyone likes"
+-- >     (pending "waiting for clarification from the designers")
+-- >   ]
+--
+pending :: String  -- ^ An explanation for why this behavior is pending.
+        -> Result
+pending = Pending
+
+
+failedCount :: [Spec] -> Int
+failedCount ss = length $ filter (isFailure.result) ss
+
+failure :: [Spec] -> Bool
+failure = any (isFailure.result)
+
+success :: [Spec] -> Bool
+success = not . failure
+
+
+isFailure :: Result -> Bool
+isFailure (Fail _) = True
+isFailure _        = False
+
+-- | Create a more readable display of a quantity of something.
+quantify :: Num a => a -> String -> String
+quantify 1 s = "1 " ++ s
+quantify n s = show n ++ " " ++ s ++ "s"
diff --git a/Test/Hspec/Formatters.hs b/Test/Hspec/Formatters.hs
new file mode 100644
--- /dev/null
+++ b/Test/Hspec/Formatters.hs
@@ -0,0 +1,59 @@
+-- | This module contains formaatters that take a set of specs and write to a given handle.
+-- They follow a structure similar to RSpec formatters.
+--
+module Test.Hspec.Formatters (
+  specdoc
+) where
+
+import Test.Hspec.Core
+import System.IO
+import Data.List (intersperse)
+import Text.Printf
+import Control.Monad (when)
+import System.Console.ANSI
+
+specdoc :: Bool -> Formatter
+specdoc useColor = Formatter {
+  exampleGroupStarted = \ h spec -> do
+    when useColor (normalColor h)
+    hPutStr h ('\n' : name spec ++ "\n"),
+
+  examplePassed = \ h spec _ -> do
+    when useColor (passColor h)
+    hPutStrLn h $ " - " ++ requirement spec,
+
+  exampleFailed = \ h spec errors -> do
+    when useColor (failColor h)
+    hPutStrLn h $ " x " ++ requirement spec ++ " FAILED [" ++ (show $ (length errors) + 1) ++ "]",
+
+  examplePending = \ h spec _ -> do
+    when useColor (pendingColor h)
+    let (Pending s) = result spec
+    hPutStrLn h $ " - " ++ requirement spec ++ "\n     # " ++ s,
+
+  errorsFormatter = \ h errors -> do
+    when useColor (failColor h)
+    mapM_ (hPutStrLn h) ("" : intersperse "" errors)
+    when (not $ null errors) (hPutStrLn h ""),
+
+  footerFormatter = \ h specs time -> do
+    when useColor (if failedCount specs == 0 then passColor h else failColor h)
+    hPutStrLn h $ printf "Finished in %1.4f seconds" time
+    hPutStrLn h ""
+    hPutStr   h $ quantify (length specs) "example" ++ ", "
+    hPutStrLn h $ quantify (failedCount specs) "failure"
+    when useColor (normalColor h)
+  }
+
+
+failColor :: Handle -> IO()
+failColor h = hSetSGR h [ SetColor Foreground Dull Red ]
+
+passColor :: Handle -> IO()
+passColor h = hSetSGR h [ SetColor Foreground Dull Green ]
+
+pendingColor :: Handle -> IO()
+pendingColor h = hSetSGR h [ SetColor Foreground Dull Yellow ]
+
+normalColor :: Handle -> IO()
+normalColor h = hSetSGR h [ Reset ]
diff --git a/Test/Hspec/HUnit.hs b/Test/Hspec/HUnit.hs
new file mode 100644
--- /dev/null
+++ b/Test/Hspec/HUnit.hs
@@ -0,0 +1,36 @@
+{-# OPTIONS -XFlexibleInstances -fno-warn-orphans #-}
+
+-- | Importing this module allows you to use an @HUnit@ test case as an example
+-- for a behavior. You can use an explicit @TestCase@ data constructor or
+-- use an @IO()@ action. For an @IO()@ action, any exception means the example
+-- failed; otherwise, it's successfull.
+--
+-- > describe "cutTheDeck" [
+-- >   it "puts the first half of a list after the last half"
+-- >      (TestCase $ assertEqual "cut the deck" [3,4,1,2] (cutTheDeck [1,2,3,4])),
+-- >
+-- >   it "restores an even sized list when cut twice"
+-- >      (assertEqual "cut the deck twice" [3,4,1,2] (cutTheDeck (cutTheDeck [1,2,3,4]))),
+-- >   ]
+-- >
+module Test.Hspec.HUnit (
+) where
+
+import System.IO.Silently
+import Test.Hspec.Core
+import qualified Test.HUnit as HU
+import Data.List (intersperse)
+
+instance SpecVerifier (IO ()) where
+  it description example = it description (HU.TestCase example)
+
+instance SpecVerifier HU.Test where
+  it description example = do
+    (counts, fails) <- silence $ HU.runTestText HU.putTextToShowS example
+    let r' = if HU.errors counts + HU.failures counts == 0
+             then Success
+             else Fail (details $ fails "")
+    return (description, r')
+
+details :: String -> String
+details = concat . intersperse "\n" . tail . init . lines
diff --git a/Test/Hspec/Monadic.hs b/Test/Hspec/Monadic.hs
new file mode 100644
--- /dev/null
+++ b/Test/Hspec/Monadic.hs
@@ -0,0 +1,115 @@
+
+-- | This module contains the runners that take a set of specs, specified in a monadic style, evaluate their examples, and
+-- report to a given handle.
+--
+-- The three functions you'll use the most are 'hspec', 'describe', and 'it'. Here is an
+-- example of functions that format and unformat phone numbers and the specs for them.
+--
+-- > import Test.Hspec
+-- > import Test.Hspec.QuickCheck
+-- > import Test.Hspec.HUnit
+-- > import Test.QuickCheck hiding (property)
+-- > import Test.HUnit
+-- >
+-- > main = hspecX $ do
+--
+-- Since the specs are often used to tell you what to implement, it's best to start with
+-- undefined functions. Once we have some specs, then you can implement each behavior
+-- one at a time, ensuring that each behavior is met and there is no undocumented behavior.
+--
+-- > unformatPhoneNumber :: String -> String
+-- > unformatPhoneNumber number = undefined
+-- >
+-- > formatPhoneNumber :: String -> String
+-- > formatPhoneNumber number = undefined
+--
+-- The "describe" function takes a list of behaviors and examples bound together with the "it" function
+--
+-- > describe "unformatPhoneNumber" $ do
+--
+-- A boolean expression can act as a behavior's example.
+--
+-- >   it "removes dashes, spaces, and parenthesies"
+-- >       (unformatPhoneNumber "(555) 555-1234" == "5555551234")
+--
+-- The "pending" function marks a behavior as pending an example. The example doesn't count as failing.
+--
+-- >   it "handles non-US phone numbers"
+-- >       (pending "need to look up how other cultures format phone numbers")
+--
+-- An HUnit "Test" can act as a behavior's example. (must import @Test.Hspec.HUnit@)
+--
+-- >   it "removes the \"ext\" prefix of the extension"
+-- >       (TestCase $ let expected = "5555551234135"
+-- >                       actual   = unformatPhoneNumber "(555) 555-1234 ext 135"
+-- >                   in assertEqual "remove extension" expected actual)
+--
+-- An @IO()@ action is treated like an HUnit "TestCase". (must import @Test.Hspec.HUnit@)
+--
+-- >   it "converts letters to numbers"
+-- >       (do
+-- >         let expected = "6862377"
+-- >         let actual   = unformatPhoneNumber "NUMBERS"
+-- >         assertEqual "letters to numbers" expected actual)
+--
+-- The "property" function allows a QuickCheck property to act as an example. (must import @Test.Hspec.HUnit@)
+--
+-- >   it "can add and remove formatting without changing the number"
+-- >       (property $ forAll phoneNumber $
+-- >         \ n -> unformatPhoneNumber (formatPhoneNumber n) == n)
+-- >
+-- > phoneNumber :: Gen String
+-- > phoneNumber = do
+-- >   nums <- elements [7,10,11,12,13,14,15]
+-- >   vectorOf nums (elements "0123456789")
+--
+module Test.Hspec.Monadic (
+  -- types
+  Spec(), Result(),Specs,
+  -- the main api
+  describe, it, hspec, hspecB, hspecX, pending, descriptions,
+  -- alternate "runner" functions
+  hHspec,
+  -- this is just for internal use
+  ItSpec
+
+) where
+
+import System.IO
+import Test.Hspec.Core hiding (describe,it)
+import qualified Test.Hspec.Core as Core
+import qualified Test.Hspec.Runner as Runner
+
+import Control.Monad.Trans.Writer (Writer, execWriter, tell)
+
+type ItSpec = IO (String, Result)
+
+type Specs = Writer [IO [IO Spec]] ()
+
+-- | Create a document of the given specs and write it to stdout.
+hspec :: Specs -> IO [Spec]
+hspec = Runner.hspec . runSpecM
+
+-- | Use in place of @hspec@ to also exit the program with an @ExitCode@
+hspecX :: Specs -> IO a
+hspecX = Runner.hspecX . runSpecM
+
+-- | Use in place of hspec to also give a @Bool@ success indication
+hspecB :: Specs -> IO Bool
+hspecB = Runner.hspecB . runSpecM
+
+-- | Create a document of the given specs and write it to the given handle.
+--
+-- > writeReport filename specs = withFile filename WriteMode (\ h -> hHspec h specs)
+--
+hHspec :: Handle -> Specs -> IO [Spec]
+hHspec h = Runner.hHspec h . runSpecM
+
+runSpecM :: Specs -> IO [IO Spec]
+runSpecM specs = descriptions $ execWriter specs
+
+describe :: String -> Writer [ItSpec] () -> Specs
+describe label action = tell [Core.describe label (execWriter action)]
+
+it :: SpecVerifier v => String -> v -> Writer [ItSpec] ()
+it label action = tell [Core.it label action]
diff --git a/Test/Hspec/QuickCheck.hs b/Test/Hspec/QuickCheck.hs
new file mode 100644
--- /dev/null
+++ b/Test/Hspec/QuickCheck.hs
@@ -0,0 +1,46 @@
+
+-- | Importing this module allows you to use a QuickCheck property as an example
+-- for a behavior. Use the 'property' function to indicate a QuickCkeck property.
+--
+-- > describe "cutTheDeck" [
+-- >   it "puts the first half of a list after the last half"
+-- >      (property $ \ xs -> let top = take (length xs `div` 2) xs
+-- >                              bot = drop (length xs `div` 2) xs
+-- >                          in cutTheDeck xs == bot ++ top),
+-- >
+-- >   it "restores an even sized list when cut twice"
+-- >      (property $ \ xs -> even (length xs) ==> cutTheDeck (cutTheDeck xs) == xs)
+-- >   ]
+--
+module Test.Hspec.QuickCheck (
+  property,
+  -- shortcut for the Monadic DSL
+  prop
+) where
+
+import System.IO.Silently
+import Test.Hspec.Core
+import qualified Test.QuickCheck as QC
+
+-- just for the prop shortcut
+import qualified Test.Hspec.Monadic as DSL
+import Control.Monad.Trans.Writer (Writer)
+
+data QuickCheckProperty a = QuickCheckProperty a
+
+property :: QC.Testable a => a -> QuickCheckProperty a
+property = QuickCheckProperty
+
+-- | Monadic DSL shortcut, use this instead of @it@
+prop :: QC.Testable t => String -> t -> Writer [DSL.ItSpec] ()
+prop n p = DSL.it n (QuickCheckProperty p)
+
+instance QC.Testable t => SpecVerifier (QuickCheckProperty t) where
+  it description (QuickCheckProperty p) = do
+    r <- silence $ QC.quickCheckResult p
+    let r' = case r of
+              QC.Success {}           -> Success
+              f@(QC.Failure {})       -> Fail (QC.output f)
+              g@(QC.GaveUp {})        -> Fail ("Gave up after " ++ quantify (QC.numTests g) "test" )
+              QC.NoExpectedFailure {} -> Fail ("No expected failure")
+    return (description, r')
diff --git a/Test/Hspec/Runner.hs b/Test/Hspec/Runner.hs
new file mode 100644
--- /dev/null
+++ b/Test/Hspec/Runner.hs
@@ -0,0 +1,75 @@
+
+-- | This module contains the runners that take a set of specs, evaluate their examples, and
+-- report to a given handle.
+--
+module Test.Hspec.Runner (
+  Specs, hspec, hspecX, hspecB, hHspec, hHspecWithFormat, describe, it, toExitCode
+) where
+
+import Test.Hspec.Core
+import Test.Hspec.Formatters
+import System.IO
+import System.CPUTime (getCPUTime)
+import Control.Monad (when)
+import System.Exit
+
+type Specs = [IO Spec]
+
+-- | Evaluate and print the result of checking the spec examples.
+runFormatter :: Formatter -> Handle -> String -> [String] -> Specs -> IO [Spec]
+runFormatter formatter h _     errors []     = do
+  errorsFormatter formatter h (reverse errors)
+  return []
+runFormatter formatter h group errors (iospec:ioss) = do
+  spec <- iospec
+  when (group /= name spec) (exampleGroupStarted formatter h spec)
+  case result spec of
+    (Success  ) -> examplePassed formatter h spec errors
+    (Fail _   ) -> exampleFailed formatter h spec errors
+    (Pending _) -> examplePending formatter h spec errors
+  let errors' = if isFailure (result spec)
+                then errorDetails spec (length errors) : errors
+                else errors
+  specs <- runFormatter formatter h (name spec) errors' ioss
+  return $ spec : specs
+
+errorDetails :: Spec -> Int -> String
+errorDetails spec i = case result spec of
+  (Fail s   ) -> concat [ show (i + 1), ") ", name spec, " ",  requirement spec, " FAILED", if null s then "" else "\n" ++ s ]
+  _           -> ""
+
+-- | Use in place of @hspec@ to also exit the program with an @ExitCode@
+hspecX :: IO Specs -> IO a
+hspecX ss = hspecB ss >>= exitWith . toExitCode
+
+-- | Use in place of hspec to also give a @Bool@ success indication
+hspecB :: IO Specs -> IO Bool
+hspecB ss = hspec ss >>= return . success
+
+-- | Create a document of the given specs and write it to stdout.
+hspec :: IO Specs -> IO [Spec]
+hspec ss = hHspec stdout ss
+
+-- | Create a document of the given specs and write it to the given handle.
+--
+-- > writeReport filename specs = withFile filename WriteMode (\ h -> hHspec h specs)
+--
+hHspec :: Handle -> IO Specs -> IO [Spec]
+hHspec h = hHspecWithFormat (specdoc $ h == stdout) h
+
+-- | Create a document of the given specs and write it to the given handle.
+-- THIS IS LIKELY TO CHANGE
+hHspecWithFormat :: Formatter -> Handle -> IO Specs -> IO [Spec]
+hHspecWithFormat formatter h ss = do
+  t0 <- getCPUTime
+  ioSpecList <- ss
+  specList <- (runFormatter formatter) h "" [] ioSpecList
+  t1 <- getCPUTime
+  let runTime = ((fromIntegral $ t1 - t0) / (10.0^(12::Integer)) :: Double)
+  (footerFormatter formatter) h specList runTime
+  return specList
+
+toExitCode :: Bool -> ExitCode
+toExitCode True  = ExitSuccess
+toExitCode False = ExitFailure 1
+
diff --git a/hspec.cabal b/hspec.cabal
--- a/hspec.cabal
+++ b/hspec.cabal
@@ -1,55 +1,40 @@
-name: hspec
-version: 0.4.3
-cabal-version: -any
-build-type: Custom
-license: BSD3
-license-file: LICENSE
-copyright: (c) 2011 Trystan Spangler
-maintainer: trystan.s@comcast.net
-build-depends: HUnit >=1 && <=2, QuickCheck >=2.4.0.1 && <=2.5,
-               base >=4 && <=5, silently == 1.1.1,
-               ansi-terminal == 0.5.5, transformers >= 0.2.0 && < 0.3.0
-stability: experimental
-homepage: https://github.com/trystan/hspec
-package-url: https://github.com/trystan/hspec
-bug-reports: https://github.com/trystan/hspec/issues
-synopsis: Behavior Driven Development for Haskell
+name:           hspec
+version:        0.5.0
+cabal-version:  -any
+build-type:     Custom
+license:        BSD3
+license-file:   LICENSE
+copyright:      (c) 2011 Trystan Spangler
+category:       Testing
+author:         Trystan Spangler
+maintainer:     trystan.s@comcast.net
+stability:      experimental
+homepage:       https://github.com/trystan/hspec
+package-url:    https://github.com/trystan/hspec
+bug-reports:    https://github.com/trystan/hspec/issues
+synopsis:       Behavior Driven Development for Haskell
 description: Behavior Driven Development for Haskell
              .
-             Hspec is based on the Ruby library RSpec - so much of what applies to RSpec also applies to Hspec. Hspec ties together /descriptions/ of behavior and /examples/ of that behavior. The examples can then be run as tests and the output summarises what needs to be implemented.
-category: Testing
-author: Trystan Spangler
-tested-with:
-data-files:
-data-dir: ""
-extra-source-files: ./Specs.hs ./src/Test/Hspec/HUnit.hs
-                    ./src/Test/Hspec/Runner.hs ./src/Test/Hspec/Monadic.hs
-                    ./src/Test/Hspec/Formatters.hs
-                    ./src/Test/Hspec/Core.hs ./src/Test/Hspec/QuickCheck.hs
-extra-tmp-files:
+             Hspec is roughly based on the Ruby library RSpec. However, Hspec is just a framework for running HUnit and QuickCheck tests. Compared to other options, it provides a much nicer syntax that makes tests very easy to read.
+
+exposed:        True
+buildable:      True
+extensions:     FlexibleInstances
+other-modules: Specs
+ghc-options: -Wall
+
+build-depends: HUnit >=1 && <=2,
+               QuickCheck >=2.4.0.1 && <=2.5,
+               base >=4 && <=5,
+               silently == 1.1.1,
+               ansi-terminal == 0.5.5,
+               transformers >= 0.2.0 && < 0.3.0
+
+extra-source-files: ./Specs.hs ./Test/Hspec/HUnit.hs
+                    ./Test/Hspec/Runner.hs ./Test/Hspec/Monadic.hs
+                    ./Test/Hspec/Formatters.hs
+                    ./Test/Hspec/Core.hs ./Test/Hspec/QuickCheck.hs
+
 exposed-modules: Test.Hspec Test.Hspec.HUnit Test.Hspec.Core
                  Test.Hspec.Runner Test.Hspec.Monadic
                  Test.Hspec.Formatters Test.Hspec.QuickCheck
-exposed: True
-buildable: True
-build-tools:
-cpp-options:
-cc-options:
-ld-options:
-pkgconfig-depends:
-frameworks:
-c-sources:
-extensions: FlexibleInstances
-extra-libraries:
-extra-lib-dirs:
-includes:
-install-includes:
-include-dirs:
-hs-source-dirs: . src
-other-modules: Specs
-ghc-prof-options:
-ghc-shared-options:
-ghc-options: -Wall
-hugs-options:
-nhc98-options:
-jhc-options:
diff --git a/src/Test/Hspec.hs b/src/Test/Hspec.hs
deleted file mode 100644
--- a/src/Test/Hspec.hs
+++ /dev/null
@@ -1,85 +0,0 @@
-
--- | Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is an approach
--- to software development that combines Test-Driven Development, Domain Driven Design, and
--- Acceptance Test-Driven Planning. Hspec helps you do the TDD part of that equation, focusing
--- on the documentation and design aspects of TDD.
---
--- Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of what applies to
--- RSpec also applies to Hspec. Hspec ties together /descriptions/ of behavior and /examples/ of
--- that behavior. The examples can also be run as tests and the output summarises what needs to
--- be implemented.
---
--- The three functions you'll use the most are 'hspec', 'describe', and 'it'. Here is an
--- example of functions that format and unformat phone numbers and the specs for them.
---
--- > import Test.Hspec
--- > import Test.Hspec.QuickCheck
--- > import Test.Hspec.HUnit
--- > import Test.QuickCheck hiding (property)
--- > import Test.HUnit
--- >
--- > main = hspec mySpecs
---
--- Since the specs are often used to tell you what to implement, it's best to start with
--- undefined functions. Once we have some specs, then you can implement each behavior
--- one at a time, ensuring that each behavior is met and there is no undocumented behavior.
---
--- > unformatPhoneNumber :: String -> String
--- > unformatPhoneNumber number = undefined
--- >
--- > formatPhoneNumber :: String -> String
--- > formatPhoneNumber number = undefined
---
--- The "describe" function takes a list of behaviors and examples bound together with the "it" function
---
--- > mySpecs = describe "unformatPhoneNumber" [
---
--- A boolean expression can act as a behavior's example.
---
--- >   it "removes dashes, spaces, and parenthesies"
--- >       (unformatPhoneNumber "(555) 555-1234" == "5555551234"),
---
--- The "pending" function marks a behavior as pending an example. The example doesn't count as failing.
---
--- >   it "handles non-US phone numbers"
--- >       (pending "need to look up how other cultures format phone numbers"),
---
--- An HUnit "Test" can act as a behavior's example. (must import @Test.Hspec.HUnit@)
---
--- >   it "removes the \"ext\" prefix of the extension"
--- >       (TestCase $ let expected = "5555551234135"
--- >                       actual   = unformatPhoneNumber "(555) 555-1234 ext 135"
--- >                   in assertEqual "remove extension" expected actual),
---
--- An @IO()@ action is treated like an HUnit "TestCase". (must import @Test.Hspec.HUnit@)
---
--- >   it "converts letters to numbers"
--- >       (do
--- >         let expected = "6862377"
--- >         let actual   = unformatPhoneNumber "NUMBERS"
--- >         assertEqual "letters to numbers" expected actual),
---
--- The "property" function allows a QuickCheck property to act as an example. (must import @Test.Hspec.HUnit@)
---
--- >   it "can add and remove formatting without changing the number"
--- >       (property $ forAll phoneNumber $
--- >         \ n -> unformatPhoneNumber (formatPhoneNumber n) == n)
--- >   ]
--- >
--- > phoneNumber :: Gen String
--- > phoneNumber = do
--- >   nums <- elements [7,10,11,12,13,14,15]
--- >   vectorOf nums (elements "0123456789")
---
-module Test.Hspec (
-  -- types
-  Spec(), Result(),Specs,
-  -- the main api
-  describe, it, hspec, pending, descriptions,
-  -- alternate "runner" functions
-  hHspec
-) where
-
-import Test.Hspec.Core
-import Test.Hspec.Runner
-
diff --git a/src/Test/Hspec/Core.hs b/src/Test/Hspec/Core.hs
deleted file mode 100644
--- a/src/Test/Hspec/Core.hs
+++ /dev/null
@@ -1,108 +0,0 @@
-{-# OPTIONS -XFlexibleInstances #-}
-
--- | This module contains the core types, constructors, classes,
--- instances, and utility functions common to hspec.
---
-module Test.Hspec.Core where
-
-import System.IO
-import System.IO.Silently
-import Control.Exception
-import Control.Monad (liftM)
-
--- | The result of running an example.
-data Result = Success | Fail String | Pending String
-  deriving Eq
-
--- | Everything needed to specify and show a specific behavior.
-data Spec = Spec {
-                 -- | What is being tested, usually the name of a type.
-                 name::String,
-                 -- | The specific behavior being tested.
-                 requirement::String,
-                 -- | The status of this behavior.
-                 result::Result }
-
-data Formatter = Formatter { exampleGroupStarted :: Handle -> Spec -> IO (),
-                             examplePassed   :: Handle -> Spec -> [String] -> IO (),
-                             exampleFailed   :: Handle -> Spec -> [String] -> IO (),
-                             examplePending  :: Handle -> Spec -> [String] -> IO (),
-                             errorsFormatter :: Handle -> [String] -> IO (),
-                             footerFormatter :: Handle -> [Spec] -> Double -> IO () }
-
-
--- | Create a set of specifications for a specific type being described.
--- Once you know what you want specs for, use this.
---
--- > describe "abs" [
--- >   it "returns a positive number given a negative number"
--- >     (abs (-1) == 1)
--- >   ]
---
-describe :: String                -- ^ The name of what is being described, usually a function or type.
-         -> [IO (String, Result)] -- ^ A list of behaviors and examples, created by a list of 'it'.
-         -> IO [IO Spec]
-describe n = return . map (>>= \ (req, res) -> return (Spec n req res))
-
--- | Combine a list of descriptions.
-descriptions :: [IO [IO Spec]] -> IO [IO Spec]
-descriptions = liftM concat . sequence
-
--- | Evaluate a Result. Any exceptions (undefined, etc.) are treated as failures.
-safely :: Result -> IO Result
-safely f = Control.Exception.catch ok failed
-  where ok = silence $ f `seq` return f
-        failed e = return $ Fail (show (e :: SomeException))
-
--- | Anything that can be used as an example of a behavior.
-class SpecVerifier a where
-  -- | Create a description and example of a behavior, a list of these
-  -- is used by 'describe'. Once you know what you want to specify, use this.
-  --
-  -- > describe "closeEnough" [
-  -- >   it "is true if two numbers are almost the same"
-  -- >     (1.001 `closeEnough` 1.002),
-  -- >
-  -- >   it "is false if two numbers are not almost the same"
-  -- >     (not $ 1.001 `closeEnough` 1.003)
-  -- >   ]
-  --
-  it :: String           -- ^ A description of this behavior.
-     -> a                -- ^ An example for this behavior.
-     -> IO (String, Result)
-
-instance SpecVerifier Bool where
-  it description example = do
-    r <- safely (if example then Success else Fail "")
-    return (description, r)
-
-instance SpecVerifier Result where
-  it description example = do
-    r <- safely example
-    return (description, r)
-
--- | Declare an example as not successful or failing but pending some other work.
--- If you want to report on a behavior but don't have an example yet, use this.
---
--- > describe "fancyFormatter" [
--- >   it "can format text in a way that everyone likes"
--- >     (pending "waiting for clarification from the designers")
--- >   ]
---
-pending :: String  -- ^ An explanation for why this behavior is pending.
-        -> Result
-pending = Pending
-
-
-
-failedCount :: [Spec] -> Int
-failedCount ss = length $ filter (isFailure.result) ss
-
-isFailure :: Result -> Bool
-isFailure (Fail _) = True
-isFailure _        = False
-
--- | Create a more readable display of a quantity of something.
-quantify :: Num a => a -> String -> String
-quantify 1 s = "1 " ++ s
-quantify n s = show n ++ " " ++ s ++ "s"
diff --git a/src/Test/Hspec/Formatters.hs b/src/Test/Hspec/Formatters.hs
deleted file mode 100644
--- a/src/Test/Hspec/Formatters.hs
+++ /dev/null
@@ -1,59 +0,0 @@
--- | This module contains formaatters that take a set of specs and write to a given handle.
--- They follow a structure similar to RSpec formatters.
---
-module Test.Hspec.Formatters (
-  specdoc
-) where
-
-import Test.Hspec.Core
-import System.IO
-import Data.List (intersperse)
-import Text.Printf
-import Control.Monad (when)
-import System.Console.ANSI
-
-specdoc :: Bool -> Formatter
-specdoc useColor = Formatter {
-  exampleGroupStarted = \ h spec -> do
-    when useColor (normalColor h)
-    hPutStr h ('\n' : name spec ++ "\n"),
-
-  examplePassed = \ h spec _ -> do
-    when useColor (passColor h)
-    hPutStrLn h $ " - " ++ requirement spec,
-
-  exampleFailed = \ h spec errors -> do
-    when useColor (failColor h)
-    hPutStrLn h $ " x " ++ requirement spec ++ " FAILED [" ++ (show $ (length errors) + 1) ++ "]",
-
-  examplePending = \ h spec _ -> do
-    when useColor (pendingColor h)
-    let (Pending s) = result spec
-    hPutStrLn h $ " - " ++ requirement spec ++ "\n     # " ++ s,
-
-  errorsFormatter = \ h errors -> do
-    when useColor (failColor h)
-    mapM_ (hPutStrLn h) ("" : intersperse "" errors)
-    when (not $ null errors) (hPutStrLn h ""),
-
-  footerFormatter = \ h specs time -> do
-    when useColor (if failedCount specs == 0 then passColor h else failColor h)
-    hPutStrLn h $ printf "Finished in %1.4f seconds" time
-    hPutStrLn h ""
-    hPutStr   h $ quantify (length specs) "example" ++ ", "
-    hPutStrLn h $ quantify (failedCount specs) "failure"
-    when useColor (normalColor h)
-  }
-
-
-failColor :: Handle -> IO()
-failColor h = hSetSGR h [ SetColor Foreground Dull Red ]
-
-passColor :: Handle -> IO()
-passColor h = hSetSGR h [ SetColor Foreground Dull Green ]
-
-pendingColor :: Handle -> IO()
-pendingColor h = hSetSGR h [ SetColor Foreground Dull Yellow ]
-
-normalColor :: Handle -> IO()
-normalColor h = hSetSGR h [ Reset ]
diff --git a/src/Test/Hspec/HUnit.hs b/src/Test/Hspec/HUnit.hs
deleted file mode 100644
--- a/src/Test/Hspec/HUnit.hs
+++ /dev/null
@@ -1,36 +0,0 @@
-{-# OPTIONS -XFlexibleInstances -fno-warn-orphans #-}
-
--- | Importing this module allows you to use an @HUnit@ test case as an example
--- for a behavior. You can use an explicit @TestCase@ data constructor or
--- use an @IO()@ action. For an @IO()@ action, any exception means the example
--- failed; otherwise, it's successfull.
---
--- > describe "cutTheDeck" [
--- >   it "puts the first half of a list after the last half"
--- >      (TestCase $ assertEqual "cut the deck" [3,4,1,2] (cutTheDeck [1,2,3,4])),
--- >
--- >   it "restores an even sized list when cut twice"
--- >      (assertEqual "cut the deck twice" [3,4,1,2] (cutTheDeck (cutTheDeck [1,2,3,4]))),
--- >   ]
--- >
-module Test.Hspec.HUnit (
-) where
-
-import System.IO.Silently
-import Test.Hspec.Core
-import qualified Test.HUnit as HU
-import Data.List (intersperse)
-
-instance SpecVerifier (IO ()) where
-  it description example = it description (HU.TestCase example)
-
-instance SpecVerifier HU.Test where
-  it description example = do
-    (counts, fails) <- silence $ HU.runTestText HU.putTextToShowS example
-    let r' = if HU.errors counts + HU.failures counts == 0
-             then Success
-             else Fail (details $ fails "")
-    return (description, r')
-
-details :: String -> String
-details = concat . intersperse "\n" . tail . init . lines
diff --git a/src/Test/Hspec/Monadic.hs b/src/Test/Hspec/Monadic.hs
deleted file mode 100644
--- a/src/Test/Hspec/Monadic.hs
+++ /dev/null
@@ -1,104 +0,0 @@
-
--- | This module contains the runners that take a set of specs, specified in a monadic style, evaluate their examples, and
--- report to a given handle.
---
--- The three functions you'll use the most are 'hspec', 'describe', and 'it'. Here is an
--- example of functions that format and unformat phone numbers and the specs for them.
---
--- > import Test.Hspec
--- > import Test.Hspec.QuickCheck
--- > import Test.Hspec.HUnit
--- > import Test.QuickCheck hiding (property)
--- > import Test.HUnit
--- >
--- > main = hspec mySpecs
---
--- Since the specs are often used to tell you what to implement, it's best to start with
--- undefined functions. Once we have some specs, then you can implement each behavior
--- one at a time, ensuring that each behavior is met and there is no undocumented behavior.
---
--- > unformatPhoneNumber :: String -> String
--- > unformatPhoneNumber number = undefined
--- >
--- > formatPhoneNumber :: String -> String
--- > formatPhoneNumber number = undefined
---
--- The "describe" function takes a list of behaviors and examples bound together with the "it" function
---
--- > mySpecs = describe "unformatPhoneNumber" $ do
---
--- A boolean expression can act as a behavior's example.
---
--- >   it "removes dashes, spaces, and parenthesies"
--- >       (unformatPhoneNumber "(555) 555-1234" == "5555551234")
---
--- The "pending" function marks a behavior as pending an example. The example doesn't count as failing.
---
--- >   it "handles non-US phone numbers"
--- >       (pending "need to look up how other cultures format phone numbers")
---
--- An HUnit "Test" can act as a behavior's example. (must import @Test.Hspec.HUnit@)
---
--- >   it "removes the \"ext\" prefix of the extension"
--- >       (TestCase $ let expected = "5555551234135"
--- >                       actual   = unformatPhoneNumber "(555) 555-1234 ext 135"
--- >                   in assertEqual "remove extension" expected actual)
---
--- An @IO()@ action is treated like an HUnit "TestCase". (must import @Test.Hspec.HUnit@)
---
--- >   it "converts letters to numbers"
--- >       (do
--- >         let expected = "6862377"
--- >         let actual   = unformatPhoneNumber "NUMBERS"
--- >         assertEqual "letters to numbers" expected actual)
---
--- The "property" function allows a QuickCheck property to act as an example. (must import @Test.Hspec.HUnit@)
---
--- >   it "can add and remove formatting without changing the number"
--- >       (property $ forAll phoneNumber $
--- >         \ n -> unformatPhoneNumber (formatPhoneNumber n) == n)
--- >
--- > phoneNumber :: Gen String
--- > phoneNumber = do
--- >   nums <- elements [7,10,11,12,13,14,15]
--- >   vectorOf nums (elements "0123456789")
---
-module Test.Hspec.Monadic (
-  -- types
-  Spec(), Result(),Specs,
-  -- the main api
-  describe, it, hspec, pending, descriptions,
-  -- alternate "runner" functions
-  hHspec
-) where
-
-import System.IO
-import Test.Hspec.Core hiding (describe,it)
-import qualified Test.Hspec.Core as Core
-import qualified Test.Hspec.Runner as Runner
-
-import Control.Monad.Trans.Writer (Writer, execWriter, tell)
-
-type ItSpec = IO (String, Result)
-
-type Specs = Writer [IO [IO Spec]] ()
-
--- | Create a document of the given specs and write it to stdout.
-hspec :: Specs -> IO [Spec]
-hspec = Runner.hspec . runSpecM
-
--- | Create a document of the given specs and write it to the given handle.
---
--- > writeReport filename specs = withFile filename WriteMode (\ h -> hHspec h specs)
---
-hHspec :: Handle -> Specs -> IO [Spec]
-hHspec h = Runner.hHspec h . runSpecM
-
-runSpecM :: Specs -> IO [IO Spec]
-runSpecM specs = descriptions $ execWriter specs
-
-describe :: String -> Writer [ItSpec] () -> Specs
-describe label action = tell [Core.describe label (execWriter action)]
-
-it :: SpecVerifier v => String -> v -> Writer [ItSpec] ()
-it label action = tell [Core.it label action]
diff --git a/src/Test/Hspec/QuickCheck.hs b/src/Test/Hspec/QuickCheck.hs
deleted file mode 100644
--- a/src/Test/Hspec/QuickCheck.hs
+++ /dev/null
@@ -1,36 +0,0 @@
-
--- | Importing this module allows you to use a QuickCheck property as an example
--- for a behavior. Use the 'property' function to indicate a QuickCkeck property.
---
--- > describe "cutTheDeck" [
--- >   it "puts the first half of a list after the last half"
--- >      (property $ \ xs -> let top = take (length xs `div` 2) xs
--- >                              bot = drop (length xs `div` 2) xs
--- >                          in cutTheDeck xs == bot ++ top),
--- >
--- >   it "restores an even sized list when cut twice"
--- >      (property $ \ xs -> even (length xs) ==> cutTheDeck (cutTheDeck xs) == xs)
--- >   ]
---
-module Test.Hspec.QuickCheck (
-  property
-) where
-
-import System.IO.Silently
-import Test.Hspec.Core
-import qualified Test.QuickCheck as QC
-
-data QuickCheckProperty a = QuickCheckProperty a
-
-property :: QC.Testable a => a -> QuickCheckProperty a
-property = QuickCheckProperty
-
-instance QC.Testable t => SpecVerifier (QuickCheckProperty t) where
-  it description (QuickCheckProperty prop) = do
-    r <- silence $ QC.quickCheckResult prop
-    let r' = case r of
-              QC.Success {}           -> Success
-              f@(QC.Failure {})       -> Fail (QC.output f)
-              g@(QC.GaveUp {})        -> Fail ("Gave up after " ++ quantify (QC.numTests g) "test" )
-              QC.NoExpectedFailure {} -> Fail ("No expected failure")
-    return (description, r')
diff --git a/src/Test/Hspec/Runner.hs b/src/Test/Hspec/Runner.hs
deleted file mode 100644
--- a/src/Test/Hspec/Runner.hs
+++ /dev/null
@@ -1,63 +0,0 @@
-
--- | This module contains the runners that take a set of specs, evaluate their examples, and
--- report to a given handle.
---
-module Test.Hspec.Runner (
-  Specs, hspec, hHspec, hHspecWithFormat, describe, it
-) where
-
-import Test.Hspec.Core
-import Test.Hspec.Formatters
-import System.IO
-import System.CPUTime (getCPUTime)
-import Control.Monad (when)
-
-type Specs = [IO Spec]
-
--- | Evaluate and print the result of checking the spec examples.
-runFormatter :: Formatter -> Handle -> String -> [String] -> Specs -> IO [Spec]
-runFormatter formatter h _     errors []     = do
-  errorsFormatter formatter h (reverse errors)
-  return []
-runFormatter formatter h group errors (iospec:ioss) = do
-  spec <- iospec
-  when (group /= name spec) (exampleGroupStarted formatter h spec)
-  case result spec of
-    (Success  ) -> examplePassed formatter h spec errors
-    (Fail _   ) -> exampleFailed formatter h spec errors
-    (Pending _) -> examplePending formatter h spec errors
-  let errors' = if isFailure (result spec)
-                then errorDetails spec (length errors) : errors
-                else errors
-  specs <- runFormatter formatter h (name spec) errors' ioss
-  return $ spec : specs
-
-errorDetails :: Spec -> Int -> String
-errorDetails spec i = case result spec of
-  (Fail s   ) -> concat [ show (i + 1), ") ", name spec, " ",  requirement spec, " FAILED", if null s then "" else "\n" ++ s ]
-  _           -> ""
-
-
--- | Create a document of the given specs and write it to stdout.
-hspec :: IO Specs -> IO [Spec]
-hspec ss = hHspec stdout ss
-
--- | Create a document of the given specs and write it to the given handle.
---
--- > writeReport filename specs = withFile filename WriteMode (\ h -> hHspec h specs)
---
-hHspec :: Handle -> IO Specs -> IO [Spec]
-hHspec h = hHspecWithFormat (specdoc $ h == stdout) h
-
--- | Create a document of the given specs and write it to the given handle.
--- THIS IS LIKELY TO CHANGE
-hHspecWithFormat :: Formatter -> Handle -> IO Specs -> IO [Spec]
-hHspecWithFormat formatter h ss = do
-  t0 <- getCPUTime
-  ioSpecList <- ss
-  specList <- (runFormatter formatter) h "" [] ioSpecList
-  t1 <- getCPUTime
-  let runTime = ((fromIntegral $ t1 - t0) / (10.0^(12::Integer)) :: Double)
-  (footerFormatter formatter) h specList runTime
-  return specList
-
