packages feed

hspec 0.4.3 → 0.5.0

raw patch · 16 files changed

+567/−547 lines, 16 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Test.Hspec: hspecB :: IO Specs -> IO Bool
+ Test.Hspec: hspecX :: IO Specs -> IO a
+ Test.Hspec.Core: failure :: [Spec] -> Bool
+ Test.Hspec.Core: success :: [Spec] -> Bool
+ Test.Hspec.Monadic: hspecB :: Specs -> IO Bool
+ Test.Hspec.Monadic: hspecX :: Specs -> IO a
+ Test.Hspec.Monadic: type ItSpec = IO (String, Result)
+ Test.Hspec.QuickCheck: prop :: Testable t => String -> t -> Writer [ItSpec] ()
+ Test.Hspec.Runner: hspecB :: IO Specs -> IO Bool
+ Test.Hspec.Runner: hspecX :: IO Specs -> IO a
+ Test.Hspec.Runner: toExitCode :: Bool -> ExitCode

Files

Specs.hs view
@@ -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
+ Test/Hspec.hs view
@@ -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+
+ Test/Hspec/Core.hs view
@@ -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"
+ Test/Hspec/Formatters.hs view
@@ -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 ]
+ Test/Hspec/HUnit.hs view
@@ -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
+ Test/Hspec/Monadic.hs view
@@ -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]
+ Test/Hspec/QuickCheck.hs view
@@ -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')
+ Test/Hspec/Runner.hs view
@@ -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+
hspec.cabal view
@@ -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:
− src/Test/Hspec.hs
@@ -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-
− src/Test/Hspec/Core.hs
@@ -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"
− src/Test/Hspec/Formatters.hs
@@ -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 ]
− src/Test/Hspec/HUnit.hs
@@ -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
− src/Test/Hspec/Monadic.hs
@@ -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]
− src/Test/Hspec/QuickCheck.hs
@@ -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')
− src/Test/Hspec/Runner.hs
@@ -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-