content: {-# LANGUAGE CPP #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE TypeFamilies #-}
  {-# LANGUAGE TypeOperators #-}
  
  module Test.Hspec.Core.ExampleSpec (spec) where
  
  import           Prelude ()
  import           Helper
  
  import           Mock
  import           Test.HUnit (assertFailure, assertEqual)
  
  import           Test.Hspec.Core.Example (Result(..), ResultStatus(..), FailureReason(..))
  import qualified Test.Hspec.Expectations as H
  import qualified Test.Hspec.Core.Example as H
  import qualified Test.Hspec.Core.Spec as H
  import qualified Test.Hspec.Core.Runner as H
  
  safeEvaluateExample :: (H.Example e,  H.Arg e ~ ()) => e -> IO Result
  safeEvaluateExample e = H.safeEvaluateExample e defaultParams ($ ()) noOpProgressCallback
  
  evaluateExample :: (H.Example e,  H.Arg e ~ ()) => e -> IO Result
  evaluateExample e = H.evaluateExample e defaultParams ($ ()) noOpProgressCallback
  
  evaluateExampleWith :: (H.Example e, H.Arg e ~ ()) => (IO () -> IO ()) -> e -> IO Result
  evaluateExampleWith action e = H.evaluateExample e defaultParams (action . ($ ())) noOpProgressCallback
  
  evaluateExampleWithArgument :: H.Example e => (ActionWith (H.Arg e) -> IO ()) -> e -> IO Result
  evaluateExampleWithArgument action e = H.evaluateExample e defaultParams action noOpProgressCallback
  
  bottom :: a
  bottom = throw DivideByZero
  
  spec :: Spec
  spec = do
    describe "safeEvaluate" $ do
      let
        status :: ResultStatus
        status = Failure Nothing (Error Nothing $ toException DivideByZero)
  
        err :: Result
        err = Result "" status
  
      it "forces Result" $ do
        H.safeEvaluate (return $ Result "" bottom) `shouldReturn` err
  
      it "handles ResultStatus exceptions" $ do
        H.safeEvaluate (throwIO status) `shouldReturn` err
  
      it "forces ResultStatus exceptions" $ do
        H.safeEvaluate (throwIO $ Failure Nothing bottom) `shouldReturn` err
  
      it "handles other exceptions" $ do
        H.safeEvaluate (throwIO DivideByZero) `shouldReturn` err
  
      it "forces other exceptions" $ do
        H.safeEvaluate (throwIO $ ErrorCall bottom) `shouldReturn` err
  
    describe "safeEvaluateResultStatus" $ do
      let
        err :: ResultStatus
        err = Failure Nothing (Error Nothing $ toException DivideByZero)
  
      it "forces ResultStatus" $ do
        H.safeEvaluateResultStatus (return $ Failure Nothing bottom) `shouldReturn` err
  
      it "handles ResultStatus exceptions" $ do
        H.safeEvaluateResultStatus (throwIO err) `shouldReturn` err
  
      it "forces ResultStatus exceptions" $ do
        H.safeEvaluateResultStatus (throwIO $ Failure Nothing bottom) `shouldReturn` err
  
      it "handles other exceptions" $ do
        H.safeEvaluateResultStatus (throwIO DivideByZero) `shouldReturn` err
  
      it "forces other exceptions" $ do
        H.safeEvaluateResultStatus (throwIO $ ErrorCall bottom) `shouldReturn` err
  
    describe "safeEvaluateExample" $ do
      context "for Expectation" $ do
        it "returns Failure if an expectation does not hold" $ do
          Result "" (Failure _ msg) <- safeEvaluateExample (23 `H.shouldBe` (42 :: Int))
          msg `shouldBe` ExpectedButGot Nothing "42" "23"
  
        context "when used with `pending`" $ do
          it "returns Pending" $ do
            result <- safeEvaluateExample (H.pending)
            let location = mkLocation __FILE__ (pred __LINE__) 42
            result `shouldBe` Result "" (Pending location Nothing)
  
        context "when used with `pendingWith`" $ do
          it "includes the optional reason" $ do
            result <- safeEvaluateExample (H.pendingWith "foo")
            let location = mkLocation __FILE__ (pred __LINE__) 42
            result `shouldBe` Result "" (Pending location $ Just "foo")
  
    describe "evaluateExample" $ do
      context "for Result" $ do
        it "propagates exceptions" $ do
          evaluateExample (error "foobar" :: Result) `shouldThrow` errorCall "foobar"
  
        it "runs around-action" $ do
          ref <- newIORef (0 :: Int)
          let action :: IO () -> IO ()
              action e = do
                e
                modifyIORef ref succ
  
              result = Result "" (Failure Nothing NoReason)
          evaluateExampleWith action result `shouldReturn` result
          readIORef ref `shouldReturn` 1
  
        it "accepts arguments" $ do
          ref <- newIORef (0 :: Int)
          let action :: (Integer -> IO ()) -> IO ()
              action e = do
                e 42
                modifyIORef ref succ
          evaluateExampleWithArgument action (Result "" . Failure Nothing . Reason . show) `shouldReturn` Result "" (Failure Nothing $ Reason "42")
          readIORef ref `shouldReturn` 1
  
      context "for Bool" $ do
        it "returns Success on True" $ do
          evaluateExample True `shouldReturn` Result "" Success
  
        it "returns Failure on False" $ do
          evaluateExample False `shouldReturn` Result "" (Failure Nothing NoReason)
  
        it "propagates exceptions" $ do
          evaluateExample (error "foobar" :: Bool) `shouldThrow` errorCall "foobar"
  
        it "runs around-action" $ do
          ref <- newIORef (0 :: Int)
          let action :: IO () -> IO ()
              action e = do
                e
                modifyIORef ref succ
          evaluateExampleWith action False `shouldReturn` Result "" (Failure Nothing NoReason)
          readIORef ref `shouldReturn` 1
  
        it "accepts arguments" $ do
          ref <- newIORef (0 :: Int)
          let action :: (Integer -> IO ()) -> IO ()
              action e = do
                e 42
                modifyIORef ref succ
          evaluateExampleWithArgument action (== (23 :: Integer)) `shouldReturn` Result "" (Failure Nothing NoReason)
          readIORef ref `shouldReturn` 1
  
      context "for Expectation" $ do
        it "returns Success if all expectations hold" $ do
          evaluateExample (23 `shouldBe` (23 :: Int)) `shouldReturn` Result "" Success
  
        it "propagates exceptions" $ do
          evaluateExample (error "foobar" :: Expectation) `shouldThrow` errorCall "foobar"
  
        it "runs around-action" $ do
          ref <- newIORef (0 :: Int)
          let action :: IO () -> IO ()
              action e = do
                n <- readIORef ref
                e
                readIORef ref `shouldReturn` succ n
                modifyIORef ref succ
          evaluateExampleWith action (modifyIORef ref succ) `shouldReturn` Result "" Success
          readIORef ref `shouldReturn` 2
  
      context "for Property" $ do
        it "returns Success if property holds" $ do
          evaluateExample (property $ \n -> n == (n :: Int)) `shouldReturn` Result "+++ OK, passed 1000 tests." Success
  
        it "shows the collected labels" $ do
          Result info Success <- evaluateExample $ property $ \ () -> label "unit" True
          info `shouldBe` "+++ OK, passed 1000 tests (100.0% unit)."
  
        it "returns Failure if property does not hold" $ do
          Result "" (Failure _ _) <- evaluateExample $ property $ \n -> n /= (n :: Int)
          pass
  
        it "shows what falsified it" $ do
          Result "" (Failure _ r) <- evaluateExample $ property $ \ (x :: Int) (y :: Int) -> (x == 0 && y == 1) ==> False
          r `shouldBe` (Reason . intercalate "\n")  [
              "Falsified (after 1 test):"
            , "  0"
            , "  1"
            ]
  
        it "runs around-action for each single check of the property" $ do
          ref <- newIORef (0 :: Int)
          let action :: IO () -> IO ()
              action e = do
                n <- readIORef ref
                e
                readIORef ref `shouldReturn` succ n
                modifyIORef ref succ
          Result _ Success <- evaluateExampleWith action (property $ \(_ :: Int) -> modifyIORef ref succ)
          readIORef ref `shouldReturn` 2000
  
        it "pretty-prints exceptions" $ do
          Result "" (Failure _ r) <- evaluateExample $ property (\ (x :: Int) -> (x == 0) ==> (throw (ErrorCall "foobar") :: Bool))
          r `shouldBe` (Reason . intercalate "\n") [
              "uncaught exception: ErrorCall"
            , "foobar"
            , "(after 1 test)"
            , "  0"
            ]
  
        context "when used with Expectation" $ do
          let prop p = property $ \ (x :: Int) (y :: Int) -> (x == 0 && y == 1) ==> p
          context "when used with shouldBe" $ do
            it "shows what falsified it" $ do
              Result "" (Failure _ err) <- evaluateExample $ prop $ 23 `H.shouldBe` (42 :: Int)
              err `shouldBe` ExpectedButGot (Just "Falsifiable (after 1 test):\n  0\n  1") "42" "23"
  
          context "when used with assertEqual" $ do
            it "includes prefix" $ do
              Result "" (Failure _ err) <- evaluateExample $ prop $ assertEqual "foobar" (42 :: Int) 23
              err `shouldBe` ExpectedButGot (Just "Falsifiable (after 1 test):\n  0\n  1\nfoobar") "42" "23"
  
          context "when used with assertFailure" $ do
            it "includes reason" $ do
              Result "" (Failure _ err) <- evaluateExample $ prop (assertFailure "foobar" :: IO ())
              err `shouldBe` Reason "Falsifiable (after 1 test):\n  0\n  1\nfoobar"
  
          context "when used with verbose" $ do
            it "includes verbose output" $ do
              Result info (Failure _ err) <- evaluateExample $ verbose $ (`H.shouldBe` (23 :: Int))
              info `shouldBe` "Failed:\n0"
              err `shouldBe` ExpectedButGot (Just "Falsifiable (after 1 test):\n  0") "23" "0"
  
        context "when used with `pending`" $ do
          it "returns Pending" $ do
            let location = mkLocation __FILE__ (succ __LINE__) 37
            evaluateExample (property H.pending) `shouldReturn` Result "" (Pending location Nothing)
  
        context "when used with `pendingWith`" $ do
          it "includes the optional reason" $ do
            let location = mkLocation __FILE__ (succ __LINE__) 39
            evaluateExample (property $ H.pendingWith "foo") `shouldReturn` Result "" (Pending location $ Just "foo")
  
    describe "Expectation" $ do
      context "as a QuickCheck property" $ do
        it "can be quantified" $ do
          e <- newMock
          hspecSilent $ do
            H.it "some behavior" $ property $ \xs -> do
              mockAction e
              (reverse . reverse) xs `shouldBe` (xs :: [Int])
          mockCounter e `shouldReturn` 100
  
        it "can be used with expectations/HUnit assertions" $ do
          hspecResultSilent $ do
            H.describe "readIO" $ do
              H.it "is inverse to show" $ property $ \x -> do
                (readIO . show) x `shouldReturn` (x :: Int)
          `shouldReturn` H.Summary 1 0
  