hspec 1.4.2 → 1.4.2.1
raw patch · 34 files changed
+1012/−33 lines, 34 filesdep −hspec-metadep ~HUnitdep ~QuickCheckPVP ok
version bump matches the API change (PVP)
Dependencies removed: hspec-meta
Dependency ranges changed: HUnit, QuickCheck
API changes (from Hackage documentation)
Files
- hspec-discover/example/Spec.hs +0/−1
- hspec-discover/integration-test/empty/Spec.hs +0/−1
- hspec-discover/test-data/empty-dir/Foo/Bar/Baz/.placeholder +0/−0
- hspec-discover/test-data/nested-spec/Foo/Bar/BazSpec.hs +0/−0
- hspec-discover/test-data/nested-spec/Foo/BarSpec.hs +0/−0
- hspec-discover/test-data/nested-spec/FooSpec.hs +0/−0
- hspec-discover/test-data/no-intermediate-specs/Foo/Bar/BazSpec.hs +0/−0
- hspec-discover/test-data/prefix-name/Foo/BazSpec.hs +0/−0
- hspec-discover/test-data/prefix-name/FooBar/BazSpec.hs +0/−0
- hspec-discover/test-data/prefix-name/FooBarSpec.hs +0/−0
- hspec-discover/test-data/prefix-name/FooSpec.hs +0/−0
- hspec-discover/test-data/several-specs/BarSpec.hs +0/−0
- hspec-discover/test-data/several-specs/BazSpec.hs +0/−0
- hspec-discover/test-data/several-specs/FooSpec.hs +0/−0
- hspec-discover/test-data/single-spec-nested/Foo/BarSpec.hs +0/−0
- hspec-discover/test-data/single-spec/FooSpec.hs +0/−0
- hspec-discover/test-data/single-spec/Spec.hs +0/−0
- hspec-discover/test/RunSpec.hs +90/−0
- hspec-discover/test/Spec.hs +5/−1
- hspec-discover/test/Test/Hspec/Meta.hs +2/−0
- hspec.cabal +37/−29
- test/Mock.hs +15/−0
- test/Spec.hs +13/−1
- test/Test/Hspec/CompatSpec.hs +19/−0
- test/Test/Hspec/Core/TypeSpec.hs +65/−0
- test/Test/Hspec/FailureReportSpec.hs +40/−0
- test/Test/Hspec/FormattersSpec.hs +225/−0
- test/Test/Hspec/HUnitSpec.hs +83/−0
- test/Test/Hspec/Meta.hs +2/−0
- test/Test/Hspec/QuickCheckSpec.hs +19/−0
- test/Test/Hspec/RunnerSpec.hs +214/−0
- test/Test/Hspec/UtilSpec.hs +89/−0
- test/Test/HspecSpec.hs +64/−0
- test/Util.hs +30/−0
− hspec-discover/example/Spec.hs
@@ -1,1 +0,0 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
− hspec-discover/integration-test/empty/Spec.hs
@@ -1,1 +0,0 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ hspec-discover/test-data/empty-dir/Foo/Bar/Baz/.placeholder view
+ hspec-discover/test-data/nested-spec/Foo/Bar/BazSpec.hs view
+ hspec-discover/test-data/nested-spec/Foo/BarSpec.hs view
+ hspec-discover/test-data/nested-spec/FooSpec.hs view
+ hspec-discover/test-data/no-intermediate-specs/Foo/Bar/BazSpec.hs view
+ hspec-discover/test-data/prefix-name/Foo/BazSpec.hs view
+ hspec-discover/test-data/prefix-name/FooBar/BazSpec.hs view
+ hspec-discover/test-data/prefix-name/FooBarSpec.hs view
+ hspec-discover/test-data/prefix-name/FooSpec.hs view
+ hspec-discover/test-data/several-specs/BarSpec.hs view
+ hspec-discover/test-data/several-specs/BazSpec.hs view
+ hspec-discover/test-data/several-specs/FooSpec.hs view
+ hspec-discover/test-data/single-spec-nested/Foo/BarSpec.hs view
+ hspec-discover/test-data/single-spec/FooSpec.hs view
+ hspec-discover/test-data/single-spec/Spec.hs view
+ hspec-discover/test/RunSpec.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE OverloadedStrings #-}+module RunSpec (main, spec) where++import Test.Hspec.Meta++import Run++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "findSpecs" $ do+ context "when specs are not nested" $ do+ it "finds a single spec" $ do+ findSpecs "hspec-discover/test-data/single-spec/Spec.hs" `shouldReturn` [SpecNode "Foo" True []]++ it "finds several specs" $ do+ findSpecs "hspec-discover/test-data/several-specs/Spec.hs" `shouldReturn` [SpecNode "Bar" True [], SpecNode "Baz" True [], SpecNode "Foo" True []]++ context "when specs are nested" $ do+ it "finds a single spec" $ do+ findSpecs "hspec-discover/test-data/single-spec-nested/Spec.hs" `shouldReturn` [SpecNode "Foo" False [SpecNode "Bar" True []]]++ it "properly groups nested specs" $ do+ findSpecs "hspec-discover/test-data/nested-spec/Spec.hs" `shouldReturn` [SpecNode "Foo" True [SpecNode "Bar" True [SpecNode "Baz" True []]]]++ context "given a nested spec, without specs at the intermediate nodes" $ do+ it "finds a single spec" $ do+ findSpecs "hspec-discover/test-data/no-intermediate-specs/Spec.hs" `shouldReturn` [SpecNode "Foo" False [SpecNode "Bar" False [SpecNode "Baz" True []]]]++ context "given a nested specs, with specs at the intermediate nodes" $ do+ context "with two top-level specs, where one spec name is a prefix of the other" $ do+ it "properly sorts specs" $ do+ findSpecs "hspec-discover/test-data/prefix-name/Spec.hs" `shouldReturn`+ [SpecNode "Foo" True [SpecNode "Baz" True []], SpecNode "FooBar" True [SpecNode "Baz" True []]]++ context "when there are no specs" $ do+ it "returns an empty list" $ do+ findSpecs "hspec-discover/test-data/empty-dir/Spec.hs" `shouldReturn` []++ describe "formatSpec" $ do+ it "generates code for a spec" $+ formatSpec (SpecNode "Foo" True []) "" `shouldBe` "describe \"Foo\" FooSpec.spec"++ it "generates code for a nested spec" $+ formatSpec (SpecNode "Foo" True [SpecNode "Bar" True [SpecNode "Baz" True []]]) "" `shouldBe`+ "describe \"Foo\" FooSpec.spec >> describe \"Foo.Bar\" Foo.BarSpec.spec >> describe \"Foo.Bar.Baz\" Foo.Bar.BazSpec.spec"++ describe "formatSpecs" $ do+ it "generates code for a list of specs" $ do+ formatSpecs [SpecNode "Bar" True [], SpecNode "Baz" True [], SpecNode "Foo" True []] "" `shouldBe`+ "describe \"Bar\" BarSpec.spec >> describe \"Baz\" BazSpec.spec >> describe \"Foo\" FooSpec.spec"++ it "generates code for an empty list" $ do+ formatSpecs [] "" `shouldBe`+ "return ()"++ describe "formatSpecNested" $ do+ it "generates code for a spec" $+ formatSpecNested (SpecNode "Foo" True []) "" `shouldBe` "describe \"Foo\" (FooSpec.spec)"++ it "generates code for a nested spec" $+ formatSpecNested (SpecNode "Foo" True [SpecNode "Bar" True [SpecNode "Baz" True []]]) "" `shouldBe`+ "describe \"Foo\" (FooSpec.spec >> describe \"Bar\" (Foo.BarSpec.spec >> describe \"Baz\" (Foo.Bar.BazSpec.spec)))"++ describe "formatSpecsNested" $ do+ it "generates code for a list of specs" $ do+ formatSpecsNested [SpecNode "Bar" True [], SpecNode "Baz" True [], SpecNode "Foo" True []] "" `shouldBe`+ "describe \"Bar\" (BarSpec.spec) >> describe \"Baz\" (BazSpec.spec) >> describe \"Foo\" (FooSpec.spec)"++ it "generates code for an empty list" $ do+ formatSpecsNested [] "" `shouldBe`+ "return ()"++ describe "importList" $ do+ it "generates imports for a spec" $ do+ importList [SpecNode "Foo" True []] "" `shouldBe` "import qualified FooSpec\n"++ it "generates imports for a nested spec" $+ importList [SpecNode "Foo" True [SpecNode "Bar" True [SpecNode "Baz" True []]]] "" `shouldBe`+ "import qualified FooSpec\nimport qualified Foo.BarSpec\nimport qualified Foo.Bar.BazSpec\n"++ it "generates imports for a list of specs" $ do+ importList [SpecNode "Bar" True [], SpecNode "Baz" True [], SpecNode "Foo" True []] "" `shouldBe`+ "import qualified BarSpec\nimport qualified BazSpec\nimport qualified FooSpec\n"++ it "generates imports for a nested spec that has no specs at the intermediate nodes" $ do+ importList [SpecNode "Foo" False [SpecNode "Bar" False [SpecNode "Baz" True []]]] "" `shouldBe`+ "import qualified Foo.Bar.BazSpec\n"
hspec-discover/test/Spec.hs view
@@ -1,1 +1,5 @@-{-# OPTIONS_GHC -F -pgmF hspec-meta-discover #-}+{-# LINE 1 "hspec-discover/test/Spec.hs" #-}module Main where+import Test.Hspec+import qualified RunSpec+main :: IO ()+main = hspec $ describe "Run" RunSpec.spec
+ hspec-discover/test/Test/Hspec/Meta.hs view
@@ -0,0 +1,2 @@+module Test.Hspec.Meta (module Test.Hspec) where+import Test.Hspec
hspec.cabal view
@@ -1,5 +1,5 @@ name: hspec-version: 1.4.2+version: 1.4.2.1 license: BSD3 license-file: LICENSE copyright: (c) 2011-2012 Trystan Spangler, (c) 2011-2012 Simon Hengel, (c) 2011 Greg Weber@@ -20,6 +20,24 @@ . The Hspec Manual is at <http://hspec.github.com/>. +-- find hspec-discover/test-data/ -type f+extra-source-files:+ hspec-discover/test-data/single-spec/FooSpec.hs+ hspec-discover/test-data/single-spec/Spec.hs+ hspec-discover/test-data/single-spec-nested/Foo/BarSpec.hs+ hspec-discover/test-data/nested-spec/FooSpec.hs+ hspec-discover/test-data/nested-spec/Foo/Bar/BazSpec.hs+ hspec-discover/test-data/nested-spec/Foo/BarSpec.hs+ hspec-discover/test-data/several-specs/BarSpec.hs+ hspec-discover/test-data/several-specs/FooSpec.hs+ hspec-discover/test-data/several-specs/BazSpec.hs+ hspec-discover/test-data/empty-dir/Foo/Bar/Baz/.placeholder+ hspec-discover/test-data/prefix-name/FooSpec.hs+ hspec-discover/test-data/prefix-name/Foo/BazSpec.hs+ hspec-discover/test-data/prefix-name/FooBarSpec.hs+ hspec-discover/test-data/prefix-name/FooBar/BazSpec.hs+ hspec-discover/test-data/no-intermediate-specs/Foo/Bar/BazSpec.hs+ source-repository head type: git location: https://github.com/hspec/hspec@@ -63,6 +81,20 @@ src, test main-is: Spec.hs+ other-modules:+ Mock+ Util+ Test.Hspec.Meta+ -- actual spec files+ Test.HspecSpec+ Test.Hspec.CompatSpec+ Test.Hspec.Core.TypeSpec+ Test.Hspec.FailureReportSpec+ Test.Hspec.FormattersSpec+ Test.Hspec.HUnitSpec+ Test.Hspec.QuickCheckSpec+ Test.Hspec.RunnerSpec+ Test.Hspec.UtilSpec ghc-options: -Wall -Werror build-depends:@@ -76,7 +108,6 @@ , QuickCheck >= 2.4.0.1 && < 2.6 , hspec-expectations - , hspec-meta , process , ghc-paths @@ -132,35 +163,12 @@ , hspec-discover/test main-is: Spec.hs+ other-modules:+ Test.Hspec.Meta+ -- actual spec files+ RunSpec build-depends: base >= 4 && <= 5 , filepath , directory- , hspec-meta--test-suite hspec-discover-example- type:- exitcode-stdio-1.0- ghc-options:- -Wall -Werror- hs-source-dirs:- hspec-discover/example- main-is:- Spec.hs- build-depends:- base >= 4 && <= 5- , hspec- , QuickCheck--test-suite hspec-discover-integration-test-empty- type:- exitcode-stdio-1.0- ghc-options:- -Wall -Werror- hs-source-dirs:- hspec-discover/integration-test/empty- main-is:- Spec.hs- build-depends:- base >= 4 && <= 5 , hspec
+ test/Mock.hs view
@@ -0,0 +1,15 @@+module Mock where++import Control.Applicative+import Data.IORef++newtype Mock = Mock (IORef Int)++newMock :: IO Mock+newMock = Mock <$> newIORef 0++mockAction :: Mock -> IO ()+mockAction (Mock ref) = modifyIORef ref succ++mockCounter :: Mock -> IO Int+mockCounter (Mock ref) = readIORef ref
test/Spec.hs view
@@ -1,1 +1,13 @@-{-# OPTIONS_GHC -F -pgmF hspec-meta-discover #-}+{-# LINE 1 "test/Spec.hs" #-}module Main where+import Test.Hspec+import qualified Test.HspecSpec+import qualified Test.Hspec.CompatSpec+import qualified Test.Hspec.Core.TypeSpec+import qualified Test.Hspec.FailureReportSpec+import qualified Test.Hspec.FormattersSpec+import qualified Test.Hspec.HUnitSpec+import qualified Test.Hspec.QuickCheckSpec+import qualified Test.Hspec.RunnerSpec+import qualified Test.Hspec.UtilSpec+main :: IO ()+main = hspec $ describe "Test.Hspec" Test.HspecSpec.spec >> describe "Test.Hspec.Compat" Test.Hspec.CompatSpec.spec >> describe "Test.Hspec.Core.Type" Test.Hspec.Core.TypeSpec.spec >> describe "Test.Hspec.FailureReport" Test.Hspec.FailureReportSpec.spec >> describe "Test.Hspec.Formatters" Test.Hspec.FormattersSpec.spec >> describe "Test.Hspec.HUnit" Test.Hspec.HUnitSpec.spec >> describe "Test.Hspec.QuickCheck" Test.Hspec.QuickCheckSpec.spec >> describe "Test.Hspec.Runner" Test.Hspec.RunnerSpec.spec >> describe "Test.Hspec.Util" Test.Hspec.UtilSpec.spec
+ test/Test/Hspec/CompatSpec.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE DeriveDataTypeable #-}+module Test.Hspec.CompatSpec (main, spec) where++import Test.Hspec.Meta++import Test.Hspec.Compat+import Data.Typeable++data SomeType = SomeType+ deriving Typeable++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "showType" $ do+ it "shows fully qualified name of type" $ do+ showType SomeType `shouldBe` "Test.Hspec.CompatSpec.SomeType"
+ test/Test/Hspec/Core/TypeSpec.hs view
@@ -0,0 +1,65 @@+module Test.Hspec.Core.TypeSpec (main, spec) where++import Test.Hspec.Meta+import Test.QuickCheck++import Test.QuickCheck.Property (morallyDubiousIOProperty)+import Control.Exception (AsyncException(..), throwIO)+import qualified Test.Hspec.Core.Type as H+import qualified Test.Hspec.Pending as H (pending)++main :: IO ()+main = hspec spec++evaluateExample :: H.Example e => e -> IO H.Result+evaluateExample = H.evaluateExample H.defaultParams++spec :: Spec+spec = do+ describe "evaluateExample" $ do+ context "for Bool" $ do+ it "returns Success on True" $ do+ evaluateExample True `shouldReturn` H.Success++ it "returns Fail on False" $ do+ evaluateExample False `shouldReturn` H.Fail ""++ it "propagates exceptions" $ do+ evaluateExample (error "foobar" :: Bool) `shouldThrow` errorCall "foobar"++ context "for Expectation" $ do+ it "returns Success if all expectations hold" $ do+ evaluateExample (23 `shouldBe` (23 :: Int)) `shouldReturn` H.Success++ it "returns Fail if an expectation does not hold" $ do+ evaluateExample (23 `shouldBe` (42 :: Int)) `shouldReturn` H.Fail "expected: 42\n but got: 23"++ it "propagates exceptions" $ do+ evaluateExample (error "foobar" :: Expectation) `shouldThrow` errorCall "foobar"++ context "for Property" $ do+ it "returns Success if property holds" $ do+ evaluateExample (property $ \n -> n == (n :: Int)) `shouldReturn` H.Success++ it "returns Fail if property does not hold" $ do+ H.Fail _ <- evaluateExample $ property $ \n -> n /= (n :: Int)+ return ()++ it "shows what falsified it" $ do+ H.Fail r <- evaluateExample $ property $ \ n -> n == (n + 1 :: Int)+ lines r `shouldSatisfy` any (== "0")++ it "propagates UserInterrupt" $ do+ let p = morallyDubiousIOProperty (throwIO UserInterrupt >> return True)+ evaluateExample p `shouldThrow` (== UserInterrupt)++ it "propagates exceptions" $ do+ pending "this probaly needs a patch to QuickCheck"+ -- evaluateExample (property $ (error "foobar" :: Int -> Bool)) `shouldThrow` errorCall "foobar"++ context "for pending" $ do+ it "returns Pending" $ do+ evaluateExample (H.pending) `shouldReturn` H.Pending Nothing++ it "includes the optional reason" $ do+ evaluateExample (H.pending "foo") `shouldReturn` H.Pending (Just "foo")
+ test/Test/Hspec/FailureReportSpec.hs view
@@ -0,0 +1,40 @@+module Test.Hspec.FailureReportSpec (main, spec) where++import Test.Hspec.Meta++import System.IO+import System.IO.Silently+import Test.Hspec.FailureReport+import GHC.Paths (ghc)+import System.Process+import System.Exit++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "writeFailureReport" $ do+ it "prints a warning on unexpected exceptions" $ do+ (r, ()) <- hCapture [stderr] $ writeFailureReport (error "some error")+ r `shouldBe` "WARNING: Could not write environment variable HSPEC_FAILURES (some error)\n"++ -- GHCi needs to keep the environment on :reload, so that we can store+ -- failures there. Otherwise --re-run would not be very useful. So we add a+ -- test for that.+ describe "GHCi" $ do+ it "keeps environment variables on :reload" $ do+ let flags = ["-v0", "--interactive", "-ignore-dot-ghci"]+ (Just hIn, Just hOut, Nothing, processHandle) <- createProcess $ (proc ghc flags) {+ std_in = CreatePipe+ , std_out = CreatePipe+ }+ hPutStrLn hIn "import System.SetEnv"+ hPutStrLn hIn "setEnv \"FOO\" \"bar\""+ hPutStrLn hIn ":reload"+ hPutStrLn hIn "import System.Environment"+ hPutStrLn hIn "getEnv \"FOO\""+ hClose hIn+ r <- hGetContents hOut+ length r `seq` r `shouldBe` "\"bar\"\n"+ waitForProcess processHandle `shouldReturn` ExitSuccess
+ test/Test/Hspec/FormattersSpec.hs view
@@ -0,0 +1,225 @@+{-# LANGUAGE CPP #-}+module Test.Hspec.FormattersSpec (main, spec) where++import Test.Hspec.Meta++import Util+import System.IO.Silently (capture)+import qualified Test.Hspec as H+import qualified Test.Hspec.Core as H (Result(..))+import qualified Test.Hspec.Runner as H+import qualified Test.Hspec.Formatters as H++#ifndef mingw32_HOST_OS+import System.Console.ANSI+#endif++main :: IO ()+main = hspec spec++testSpec :: H.Spec+testSpec = do+ H.describe "Example" $ do+ H.it "success" (H.Success)+ H.it "fail 1" (H.Fail "fail message")+ H.it "pending" (H.pending "pending message")+ H.it "fail 2" (H.Fail "")+ H.it "exceptions" (undefined :: H.Result)+ H.it "fail 3" (H.Fail "")++spec :: Spec+spec = do+ describe "silent" $ do+ let runSpec = fmap fst . capture . H.hspecWith H.defaultConfig {H.configFormatter = H.silent}+ it "produces no output" $ do+ runSpec testSpec `shouldReturn` ""++ describe "failed_examples" $ do+ failed_examplesSpec H.failed_examples++ describe "progress" $ do+ let runSpec = capture__ . H.hspecWith H.defaultConfig {H.configFormatter = H.progress}++ it "produces '..F...FF.F' style output" $ do+ r <- runSpec testSpec+ head r `shouldBe` ".F.FFF"++ context "same as failed_examples" $ do+ failed_examplesSpec H.progress++ describe "specdoc" $ do+ let runSpec = capture__ . H.hspecWith H.defaultConfig {H.configFormatter = H.specdoc}++ it "displays a header for each thing being described" $ do+ _:x:_ <- runSpec testSpec+ x `shouldBe` "Example"++ it "displays one row for each behavior" $ do+ r <- runSpec $ do+ H.describe "List as a Monoid" $ do+ H.describe "mappend" $ do+ H.it "is associative" True+ H.describe "mempty" $ do+ H.it "is a left identity" True+ H.it "is a right identity" True+ H.describe "Maybe as a Monoid" $ do+ H.describe "mappend" $ do+ H.it "is associative" True+ H.describe "mempty" $ do+ H.it "is a left identity" True+ H.it "is a right identity" True+ normalizeSummary r `shouldBe` [+ ""+ , "List as a Monoid"+ , " mappend"+ , " - is associative"+ , ""+ , " mempty"+ , " - is a left identity"+ , " - is a right identity"+ , ""+ , "Maybe as a Monoid"+ , " mappend"+ , " - is associative"+ , ""+ , " mempty"+ , " - is a left identity"+ , " - is a right identity"+ , ""+ , "Finished in 0.0000 seconds"+ , "6 examples, 0 failures"+ ]++ it "prints an empty line before each group" $ do+ r <- runSpec $ do+ H.describe "foo" $ do+ H.it "example 1" True+ H.it "example 2" True+ H.describe "bar" $ do+ H.it "example 3" True+ H.it "example 4" True+ normalizeSummary r `shouldBe` [+ ""+ , "foo"+ , " - example 1"+ , " - example 2"+ , ""+ , " bar"+ , " - example 3"+ , " - example 4"+ , ""+ , "Finished in 0.0000 seconds"+ , "4 examples, 0 failures"+ ]++ it "prints an empty line after each group" $ do+ r <- runSpec $ do+ H.describe "foo" $ do+ H.describe "bar" $ do+ H.it "example 1" True+ H.it "example 2" True+ H.it "example 3" True+ H.it "example 4" True+ normalizeSummary r `shouldBe` [+ ""+ , "foo"+ , " bar"+ , " - example 1"+ , " - example 2"+ , ""+ , " - example 3"+ , " - example 4"+ , ""+ , "Finished in 0.0000 seconds"+ , "4 examples, 0 failures"+ ]++ it "outputs an empty line at the beginning (even for non-nested specs)" $ do+ r <- runSpec $ do+ H.it "example 1" True+ H.it "example 2" True+ normalizeSummary r `shouldBe` [+ ""+ , "- example 1"+ , "- example 2"+ , ""+ , "Finished in 0.0000 seconds"+ , "2 examples, 0 failures"+ ]++ it "displays a row for each successfull, failed, or pending example" $ do+ r <- runSpec testSpec+ r `shouldSatisfy` any (== " - fail 1 FAILED [1]")+ r `shouldSatisfy` any (== " - success")++ it "displays a '#' with an additional message for pending examples" $ do+ r <- runSpec testSpec+ r `shouldSatisfy` any (== " # PENDING: pending message")++ it "outputs failed examples in red, pending in yellow, and successful in green" $ do+ pending++ context "same as failed_examples" $ do+ failed_examplesSpec H.progress++failed_examplesSpec :: H.Formatter -> Spec+failed_examplesSpec formatter = do+ let runSpec = capture__ . H.hspecWith H.defaultConfig {H.configFormatter = formatter}++ it "summarizes the time it takes to finish" $ do+ r <- runSpec (return ())+ normalizeSummary r `shouldSatisfy` any (== "Finished in 0.0000 seconds")++ context "displays a detailed list of failures" $ do+ it "prints all requirements that are not met" $ do+ r <- runSpec testSpec+ r `shouldSatisfy` any (== "1) Example fail 1 FAILED")++ it "prints the exception type for requirements that fail due to an uncaught exception" $ do+ r <- runSpec $ do+ H.it "foobar" (undefined :: Bool)+ r `shouldContain` [+ "1) foobar FAILED (uncaught exception)"+ , "GHC.Exception.ErrorCall (Prelude.undefined)"+ ]++ it "prints all descriptions when a nested requirement fails" $ do+ r <- runSpec $+ H.describe "foo" $ do+ H.describe "bar" $ do+ H.it "baz" False+ r `shouldSatisfy` any (== "1) foo.bar baz FAILED")++ it "summarizes the number of examples and failures" $ do+ r <- runSpec testSpec+ r `shouldSatisfy` any (== "6 examples, 4 failures, 1 pending")++ -- Windows has no support for ANSI escape codes. The Console API is used for+ -- colorized output, hence the following tests do not work on Windows.+#ifndef mingw32_HOST_OS+ it "shows summary in green if there are no failures" $ do+ r <- capture__ $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlway} $ do+ H.it "foobar" True+ r `shouldSatisfy` any (== (green ++ "1 example, 0 failures" ++ reset))++ it "shows summary in yellow if there are pending examples" $ do+ r <- capture__ $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlway} $ do+ H.it "foobar" H.pending+ r `shouldSatisfy` any (== (yellow ++ "1 example, 0 failures, 1 pending" ++ reset))++ it "shows summary in red if there are failures" $ do+ r <- capture__ $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlway} $ do+ H.it "foobar" False+ r `shouldSatisfy` any (== (red ++ "1 example, 1 failure" ++ reset))++ it "shows summary in red if there are both failures and pending examples" $ do+ r <- capture__ $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlway} $ do+ H.it "foo" False+ H.it "bar" H.pending+ r `shouldSatisfy` any (== (red ++ "2 examples, 1 failure, 1 pending" ++ reset))+ where+ green = setSGRCode [SetColor Foreground Dull Green]+ yellow = setSGRCode [SetColor Foreground Dull Yellow]+ red = setSGRCode [SetColor Foreground Dull Red]+ reset = setSGRCode [Reset]+#endif
+ test/Test/Hspec/HUnitSpec.hs view
@@ -0,0 +1,83 @@+module Test.Hspec.HUnitSpec (main, spec) where++import Test.Hspec.Meta+import Util (capture__)+import Control.Applicative++import qualified Test.Hspec as H+import qualified Test.Hspec.Runner as H+import Test.Hspec.Core.Type (SpecTree(..), runSpecM)+import Test.Hspec.HUnit+import Test.HUnit++main :: IO ()+main = hspec spec++-- SpecTree does not have an Eq nor a Show instance, hence we map it to `Tree`.+data Tree = Group String [Tree] | Example String+ deriving (Eq, Show)++shouldYield :: Test -> [Tree] -> Expectation+a `shouldYield` b = (convert . runSpecM . fromHUnitTest) a `shouldBe` b+ where+ convert :: [SpecTree] -> [Tree]+ convert = map go+ where+ go :: SpecTree -> Tree+ go x = case x of+ SpecGroup s xs -> Group s (map go xs)+ SpecItem s _ -> Example s++spec :: Spec+spec = do+ describe "fromHUnitTest" $ do+ let e = TestCase $ pure ()++ it "works for a TestCase" $ do+ e `shouldYield` [Example "<unlabeled>"]++ it "works for a labeled TestCase" $ do+ TestLabel "foo" e+ `shouldYield` [Example "foo"]++ it "works for a TestCase with nested labels" $ do+ (TestLabel "foo" . TestLabel "bar") e+ `shouldYield` [Group "foo" [Example "bar"]]++ it "works for a flat TestList" $ do+ TestList [e, e, e]+ `shouldYield` [Example "<unlabeled>", Example "<unlabeled>", Example "<unlabeled>"]++ it "works for a nested TestList" $ do+ (TestLabel "foo" . TestLabel "bar" . TestList) [TestLabel "one" e, TestLabel "two" e, TestLabel "three" e]+ `shouldYield` [Group "foo" [Group "bar" [Example "one", Example "two", Example "three"]]]++ describe "HUnit TestCase as an example (deprecated!)" $ do+ it "is specified with the HUnit `TestCase` data constructor" $ TestCase $ do+ runSpec $ do+ H.it "some behavior" (TestCase $ "foo" @?= "bar")+ H.it "some behavior" (TestCase $ "foo" @?= "foo")+ `shouldReturn` H.Summary 2 1++ it "is the assumed example for IO() actions" $ do+ runSpec $ do+ H.it "some behavior" ("foo" @?= "bar")+ H.it "some behavior" ("foo" @?= "foo")+ `shouldReturn` H.Summary 2 1++ it "will show the failed assertion text if available (e.g. assertBool)" $ do+ let assertionText = "some assertion text"+ r <- capture__ . runSpec $ do+ H.describe "foo" $ do+ H.it "bar" (assertFailure assertionText)+ r `shouldSatisfy` any (== assertionText)++ it "will show the failed assertion expected and actual values if available (e.g. assertEqual)" $ do+ r <- capture__ . runSpec $ do+ H.describe "foo" $ do+ H.it "bar" (assertEqual "trivial" (1::Int) 2)+ assertBool "should find assertion text" $ any (=="trivial") r+ assertBool "should find 'expected: 1'" $ any (=="expected: 1") r+ assertBool "should find ' but got: 2'" $ any (==" but got: 2") r+ where+ runSpec = H.hspecWith H.defaultConfig
+ test/Test/Hspec/Meta.hs view
@@ -0,0 +1,2 @@+module Test.Hspec.Meta (module Test.Hspec) where+import Test.Hspec
+ test/Test/Hspec/QuickCheckSpec.hs view
@@ -0,0 +1,19 @@+module Test.Hspec.QuickCheckSpec (main, spec) where++import Test.Hspec.Meta++import qualified Test.Hspec as H+import qualified Test.Hspec.Runner as H+import qualified Test.Hspec.QuickCheck as H++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "prop" $ do+ it "is a shortcut to use properties as examples" $ do+ H.hspecWith H.defaultConfig $ do+ H.describe "read" $ do+ H.prop "is inverse to show" $ \x -> (read . show) x == (x :: Int)+ `shouldReturn` H.Summary 1 0
+ test/Test/Hspec/RunnerSpec.hs view
@@ -0,0 +1,214 @@+module Test.Hspec.RunnerSpec (main, spec) where++import Test.Hspec.Meta+import System.IO.Silently (hCapture, hSilence)+import System.IO (stderr)+import Control.Applicative+import System.Environment (withArgs, withProgName, getArgs)+import System.Exit+import qualified Control.Exception as E+import Util+import Mock+import System.SetEnv+import Test.Hspec.Util (getEnv)+import Test.QuickCheck.Property (morallyDubiousIOProperty)++import qualified Test.Hspec.Runner as H+import qualified Test.Hspec as H (describe, it, pending)+import qualified Test.Hspec.Formatters as H (silent)++ignoreExitCode :: IO () -> IO ()+ignoreExitCode action = action `E.catch` \e -> let _ = e :: ExitCode in return ()++main :: IO ()+main = hspec spec++spec :: Spec+spec = do++ describe "hspec" $ do+ it "runs a spec" $ do+ H.hspec $ do+ H.it "foobar" True+ `shouldReturn` ()++ it "exits with exitFailure if not all examples pass" $ do+ H.hspec $ do+ H.it "foobar" False+ `shouldThrow` (== ExitFailure 1)++ it "suppresses output to stdout when evaluating examples" $ do+ r <- capture__ . H.hspec $ do+ H.it "foobar" $ do+ putStrLn "baz"+ r `shouldSatisfy` notElem "baz"++ it "prints an error message on unrecognized command-line options" $ do+ withProgName "myspec" . withArgs ["--foo"] $ do+ hSilence [stderr] (H.hspec $ pure ()) `shouldThrow` (== ExitFailure 1)+ fst `fmap` hCapture [stderr] (ignoreExitCode (H.hspec $ pure ())) `shouldReturn` unlines [+ "myspec: unrecognized option `--foo'"+ , "Try `myspec --help' for more information."+ ]++ it "does not leak command-line flags to examples" $ do+ withArgs ["--verbose"] $ do+ H.hspec $ do+ H.it "foobar" $ do+ getArgs `shouldReturn` []+ `shouldReturn` ()++ context "with --help" $ do+ let printHelp = withProgName "spec" . withArgs ["--help"] . H.hspec $ pure ()+ it "prints help" $ do+ r <- (capture__ . ignoreExitCode) printHelp+ r `shouldStartWith` ["Usage: spec [OPTION]..."]+ printHelp `shouldThrow` (== ExitSuccess)++ it "constrains lines to 80 characters" $ do+ r <- (capture__ . ignoreExitCode) printHelp+ r `shouldSatisfy` all ((<= 80) . length)+ r `shouldSatisfy` any ((78 <=) . length)++ context "with --verbose" $ do+ it "does not suppress output to stdout" $ do+ r <- capture__ . withArgs ["--verbose"] . H.hspec $ do+ H.it "foobar" $ do+ putStrLn "baz"+ r `shouldSatisfy` elem "baz"++ context "with --match" $ do+ it "only runs examples that match a given pattern" $ do+ e1 <- newMock+ e2 <- newMock+ e3 <- newMock+ withArgs ["-m", "/bar/example"] . H.hspec $ do+ H.describe "foo" $ do+ H.describe "bar" $ do+ H.it "example 1" $ mockAction e1+ H.it "example 2" $ mockAction e2+ H.describe "baz" $ do+ H.it "example 3" $ mockAction e3+ (,,) <$> mockCounter e1 <*> mockCounter e2 <*> mockCounter e3 `shouldReturn` (1, 1, 0)++ it "can be given multiple times" $ do+ e1 <- newMock+ e2 <- newMock+ e3 <- newMock+ withArgs ["-m", "foo", "-m", "baz"] . H.hspec $ do+ H.describe "foo" $ do+ H.it "example 1" $ mockAction e1+ H.describe "bar" $ do+ H.it "example 2" $ mockAction e2+ H.describe "baz" $ do+ H.it "example 3" $ mockAction e3+ (,,) <$> mockCounter e1 <*> mockCounter e2 <*> mockCounter e3 `shouldReturn` (1, 0, 1)++ context "with --maximum-generated-tests=n" $ do+ it "tries QuickCheck properties n times" $ do+ m <- newMock+ withArgs ["--maximum-generated-tests=23"] . H.hspec $ do+ H.it "foo" $ do+ morallyDubiousIOProperty (mockAction m >> pure True)+ mockCounter m `shouldReturn` 23++ context "with --print-cpu-time" $ do+ it "includes used CPU time in summary" $ do+ r <- capture_ $ withArgs ["--print-cpu-time"] (H.hspec $ pure ())+ (normalizeSummary . lines) r `shouldContain` ["Finished in 0.0000 seconds, used 0.0000 seconds of CPU time"]++ context "with --html" $ do+ it "produces HTML output" $ do+ r <- capture_ . withArgs ["--html"] . H.hspec $ do+ H.it "foo" True+ r `shouldContain` "</span>"++ it "marks successful examples with CSS class hspec-success" $ do+ r <- capture_ . withArgs ["--html"] . H.hspec $ do+ H.it "foo" True+ r `shouldContain` "<span class=\"hspec-success\">- foo\n</span>"++ it "marks pending examples with CSS class hspec-pending" $ do+ r <- capture_ . withArgs ["--html"] . H.hspec $ do+ H.it "foo" H.pending+ r `shouldContain` "<span class=\"hspec-pending\">- foo"++ it "marks failed examples with CSS class hspec-failure" $ do+ r <- capture_ . ignoreExitCode . withArgs ["--html"] . H.hspec $ do+ H.it "foo" False+ r `shouldContain` "<span class=\"hspec-failure\">- foo"++ describe "hspec (experimental features)" $ do+ it "stores a failure report in environment HSPEC_FAILURES" $ do+ ignoreExitCode . H.hspec $ do+ H.describe "foo" $ do+ H.describe "bar" $ do+ H.it "example 1" True+ H.it "example 2" False+ H.describe "baz" $ do+ H.it "example 3" False+ getEnv "HSPEC_FAILURES" `shouldReturn` Just "[([\"foo\",\"bar\"],\"example 2\"),([\"baz\"],\"example 3\")]"++ describe "with --re-run" $ do+ let runSpec = (capture__ . ignoreExitCode . H.hspec) $ do+ H.it "example 1" True+ H.it "example 2" False+ H.it "example 3" False+ H.it "example 4" True+ H.it "example 5" False++ it "re-runs examples that previously failed" $ do+ r0 <- runSpec+ r0 `shouldSatisfy` elem "5 examples, 3 failures"++ r1 <- withArgs ["-r"] runSpec+ r1 `shouldSatisfy` elem "3 examples, 3 failures"++ context "when there is no failure report in the environment" $ do+ it "runs everything" $ do+ unsetEnv "HSPEC_FAILURES"+ r <- hSilence [stderr] $ withArgs ["-r"] runSpec+ r `shouldSatisfy` elem "5 examples, 3 failures"++ it "prints a warning to stderr" $ do+ unsetEnv "HSPEC_FAILURES"+ r <- hCapture [stderr] $ withArgs ["-r"] runSpec+ fst r `shouldBe` "WARNING: Could not read environment variable HSPEC_FAILURES; `--re-run' is ignored!\n"++ context "when parsing of failure report fails" $ do+ it "runs everything" $ do+ setEnv "HSPEC_FAILURES" "some invalid report"+ r <- hSilence [stderr] $ withArgs ["-r"] runSpec+ r `shouldSatisfy` elem "5 examples, 3 failures"++ it "prints a warning to stderr" $ do+ setEnv "HSPEC_FAILURES" "some invalid report"+ r <- hCapture [stderr] $ withArgs ["-r"] runSpec+ fst r `shouldBe` "WARNING: Could not read environment variable HSPEC_FAILURES; `--re-run' is ignored!\n"++ describe "hspecWith" $ do+ it "returns a summary of the test run" $ do+ H.hspecWith H.defaultConfig $ do+ H.it "foo" True+ H.it "foo" False+ H.it "foo" False+ H.it "foo" True+ H.it "foo" True+ `shouldReturn` H.Summary 5 2++ it "treats uncaught exceptions as failure" $ do+ H.hspecWith H.defaultConfig $ do+ H.it "foobar" (E.throwIO (E.ErrorCall "foobar") >> pure ())+ `shouldReturn` H.Summary 1 1++ it "uses the specdoc formatter by default" $ do+ _:r:_ <- capture__ . H.hspecWith H.defaultConfig $ do+ H.describe "Foo.Bar" $ do+ H.it "some example" True+ r `shouldBe` "Foo.Bar"++ it "can use a custom formatter" $ do+ r <- capture_ . H.hspecWith H.defaultConfig {H.configFormatter = H.silent} $ do+ H.describe "Foo.Bar" $ do+ H.it "some example" True+ r `shouldBe` ""
+ test/Test/Hspec/UtilSpec.hs view
@@ -0,0 +1,89 @@+module Test.Hspec.UtilSpec (main, spec) where++import Test.Hspec.Meta++import qualified Control.Exception as E+import Test.Hspec.Util++import System.SetEnv++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "quantify" $ do+ it "returns an amount and a word given an amount and word" $ do+ quantify 1 "thing" `shouldBe` "1 thing"++ it "returns a singular word given the number 1" $ do+ quantify 1 "thing" `shouldBe` "1 thing"++ it "returns a plural word given a number greater than 1" $ do+ quantify 2 "thing" `shouldBe` "2 things"++ it "returns a plural word given the number 0" $ do+ quantify 0 "thing" `shouldBe` "0 things"++ describe "safeEvaluate" $ do+ it "returns Right on success" $ do+ Right e <- safeEvaluate (return 23 :: IO Int)+ e `shouldBe` 23++ it "returns Left on exception" $ do+ Left e <- safeEvaluate (E.throwIO E.DivideByZero :: IO Int)+ show e `shouldBe` "divide by zero"++ it "evaluates result to weak head normal form" $ do+ Left e <- safeEvaluate (return undefined)+ show e `shouldBe` "Prelude.undefined"++ it "re-throws AsyncException" $ do+ safeEvaluate (E.throwIO E.UserInterrupt :: IO Int) `shouldThrow` (== E.UserInterrupt)++ describe "filterPredicate" $ do+ it "tries to match a pattern against a path" $ do+ let p = filterPredicate "foo/bar/example 1"+ p (["foo", "bar"], "example 1") `shouldBe` True+ p (["foo", "bar"], "example 2") `shouldBe` False++ it "is ambiguous" $ do+ let p = filterPredicate "foo/bar/baz"+ p (["foo", "bar"], "baz") `shouldBe` True+ p (["foo"], "bar/baz") `shouldBe` True++ it "succeeds on a partial match" $ do+ let p = filterPredicate "bar/baz"+ p (["foo", "bar", "baz"], "example 1") `shouldBe` True++ it "succeeds with a pattern that matches the message give in the failure list" $ do+ let p = filterPredicate "ModuleA.ModuleB.foo does something"+ p (["ModuleA", "ModuleB", "foo"], "does something") `shouldBe` True++ describe "formatRequirement" $ do+ it "creates a sentence from a subject and a requirement" $ do+ formatRequirement (["reverse"], "reverses a list") `shouldBe` "reverse reverses a list"++ it "creates a sentence from a subject and a requirement when the subject consits of multiple words" $ do+ formatRequirement (["The reverse function"], "reverses a list") `shouldBe` "The reverse function reverses a list"++ it "returns the requirement if no subject is given" $ do+ formatRequirement ([], "reverses a list") `shouldBe` "reverses a list"++ it "inserts context separated by commas" $ do+ formatRequirement (["reverse", "when applied twice"], "reverses a list") `shouldBe` "reverse, when applied twice, reverses a list"++ it "joins components of a subject with a dot" $ do+ formatRequirement (["Data", "List", "reverse"], "reverses a list") `shouldBe` "Data.List.reverse reverses a list"++ it "properly handles context after a subject that consists of several components" $ do+ formatRequirement (["Data", "List", "reverse", "when applied twice"], "reverses a list") `shouldBe` "Data.List.reverse, when applied twice, reverses a list"++ describe "getEnv" $ do+ it "returns value of specified environment variable" $ do+ setEnv "FOO" "bar"+ getEnv "FOO" `shouldReturn` Just "bar"++ it "returns Nothing if specified environment variable is not set" $ do+ unsetEnv "FOO"+ getEnv "FOO" `shouldReturn` Nothing
+ test/Test/HspecSpec.hs view
@@ -0,0 +1,64 @@+module Test.HspecSpec (main, spec) where++import Test.Hspec.Meta+import Util (capture__)+import Data.List (isPrefixOf)++import qualified Test.Hspec.Core.Type as H (defaultParams)+import Test.Hspec.Core (SpecTree(..), Result(..), runSpecM)+import qualified Test.Hspec as H+import qualified Test.Hspec.Runner as H (hspecWith)+import Test.Hspec.Runner (defaultConfig)++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+ describe "pending" $ do+ it "specifies a pending example" $ do+ r <- runSpec $ do+ H.it "foo" H.pending+ r `shouldSatisfy` any (== " # PENDING: No reason given")++ it "accepts an optional message, which is included in the report" $ do+ r <- runSpec $ do+ H.it "foo" $ do+ H.pending "for some reason"+ r `shouldSatisfy` any (== " # PENDING: for some reason")++ describe "describe" $ do+ let testSpec = do+ H.describe "some subject" $ do+ H.it "foo" True+ H.it "bar" True+ H.it "baz" True+ it "takes a description of what the behavior is for" $ do+ r <- runSpec testSpec+ r `shouldSatisfy` any (== "some subject")++ it "groups behaviors for what's being described" $ do+ r <- filter (isPrefixOf " - ") `fmap` runSpec testSpec+ length r `shouldBe` 3++ it "can be nested" $ do+ let [SpecGroup foo [SpecGroup bar [SpecItem baz _]]] = runSpecM $ do+ H.describe "foo" $ do+ H.describe "bar" $ do+ H.it "baz" True+ (foo, bar, baz) `shouldBe` ("foo", "bar", "baz")++ describe "it" $ do+ it "takes a description of a desired behavior" $ do+ let [SpecItem d _] = runSpecM (H.it "whatever" True)+ d `shouldBe` "whatever"++ it "takes an example of that behavior" $ do+ let [SpecItem _ e] = runSpecM (H.it "whatever" True)+ e H.defaultParams `shouldReturn` Success++ it "can use a Bool, HUnit Test, QuickCheck property, or `pending` as an example"+ pending+ where+ runSpec :: H.Spec -> IO [String]+ runSpec = capture__ . H.hspecWith defaultConfig
+ test/Util.hs view
@@ -0,0 +1,30 @@+module Util where++import Data.List+import Data.Char+import Test.Hspec.Meta+import qualified System.IO.Silently as S++capture_ :: IO a -> IO String+capture_ = fmap fst . S.capture++capture__ :: IO a -> IO [String]+capture__ = fmap lines . capture_++shouldContain :: (Eq a, Show a) => [a] -> [a] -> Expectation+x `shouldContain` y = x `shouldSatisfy` isInfixOf y++shouldStartWith :: (Eq a, Show a) => [a] -> [a] -> Expectation+x `shouldStartWith` y = x `shouldSatisfy` isPrefixOf y++shouldEndWith :: (Eq a, Show a) => [a] -> [a] -> Expectation+x `shouldEndWith` y = x `shouldSatisfy` isSuffixOf y++-- replace times in summary with zeroes+normalizeSummary :: [String] -> [String]+normalizeSummary xs = map f xs+ where+ f x | "Finished in " `isPrefixOf` x = map g x+ | otherwise = x+ g x | isNumber x = '0'+ | otherwise = x