{-# OPTIONS_GHC -fno-warn-orphans #-}
module Test.Hspec.Core.Runner.EvalSpec (spec) where

import           Prelude ()
import           Helper

import           Data.List.NonEmpty (fromList)

import           Test.Hspec.Core.Spec (FailureReason(..), Result(..), ResultStatus(..), Location(..))

import           Test.Hspec.Core.Runner.Eval

instance Arbitrary ResultStatus where
  arbitrary = oneof [
      pure Success
    , Pending <$> arbitrary <*> arbitrary
    , failure
    ]

instance Arbitrary FailureReason where
  arbitrary = oneof [
      pure NoReason
    , ExpectedButGot <$> arbitrary <*> (show <$> positive) <*> (show <$> positive)
    , Error <$> arbitrary <*> pure (toException DivideByZero)
    ]

instance Arbitrary Location where
  arbitrary = Location <$> elements ["src/Foo.hs", "src/Bar.hs", "src/Baz.hs"] <*> positive <*> positive

positive :: Gen Int
positive = getPositive <$> arbitrary

failureResult :: Gen Result
failureResult = Result <$> arbitrary <*> failure

pendingResult :: Gen Result
pendingResult = Result <$> arbitrary <*> pending

successResult :: Gen Result
successResult = Result <$> arbitrary <*> pure Success

pending :: Gen ResultStatus
pending = Pending <$> arbitrary <*> arbitrary

failure :: Gen ResultStatus
failure = Failure <$> arbitrary <*> arbitrary

spec :: Spec
spec = do
  describe "mergeResults" $ do
    it "gives failures from items precedence" $ do
      forAll failureResult $ \ item hook -> do
        mergeResults Nothing item hook `shouldBe` item

    it "gives failures from hooks precedence over succeeding items" $ do
      forAll successResult $ \ item@(Result info _) -> forAll failure $ \ hook -> do
        mergeResults Nothing item hook `shouldBe` Result info hook

    it "gives failures from hooks precedence over pending items" $ do
      forAll pendingResult $ \ item@(Result info _) -> forAll failure $ \ hook -> do
        mergeResults Nothing item hook `shouldBe` Result info hook

    it "gives pending items precedence over pending hooks" $ do
      forAll pendingResult $ \ item -> forAll pending $ \ hook -> do
        mergeResults Nothing item hook `shouldBe` item

  describe "traverse" $ do
    context "when used with Tree" $ do
      let
        tree :: Tree () Int
        tree = Node "" $ fromList [Node "" $ fromList [Leaf 1, Node "" $ fromList [Leaf 2, Leaf 3]], Leaf 4]
      it "walks the tree left-to-right, depth-first" $ do
        ref <- newIORef []
        traverse_ (modifyIORef ref . (:) ) tree
        reverse <$> readIORef ref `shouldReturn` [1 .. 4]
