hegel-0.1.0: test/Hegel/IntegrationSpec.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Hegel.IntegrationSpec (spec) where
import Control.Exception (SomeException, displayException, try)
import Control.Monad (when)
import Data.List (isInfixOf)
import Test.Hspec
import Hegel
captureFailureText :: IO () -> IO String
captureFailureText action = do
result <- try action :: IO (Either SomeException ())
case result of
Left e -> pure (displayException e)
Right () -> expectationFailure "expected property test to fail" >> pure ""
withTestCases :: Int -> Settings -> Settings
withTestCases n stgs = stgs { settingsTestCases = n }
spec :: Spec
spec = do
describe "E2E: simple passing test" $ do
it "integers are self-equal" $ do
runHegelTest (withTestCases 10) $ \tc -> do
n <- draw tc (integers def)
when (n /= n) $
error "integer not equal to itself"
describe "E2E: simple failing test" $ do
it "catches HegelTestFailure" $ do
let action = runHegelTest (withTestCases 10) $ \tc -> do
_ <- draw tc (integers def { rangeMin = Just 0, rangeMax = Just 100 })
error "deliberate failure"
action `shouldThrow` anyException
it "reports the source location for Hegel.assert failures" $ do
let expectedFile = __FILE__
let expectedLine = show ((__LINE__ + 3) :: Int)
msg <- captureFailureText $
runHegelTest (withTestCases 10) $ \_tc ->
assert False "deliberate failure with location"
msg `shouldSatisfy` isInfixOf expectedFile
msg `shouldSatisfy` isInfixOf (":" ++ expectedLine ++ ":")
it "reports only the smallest failing example for a stable origin" $ do
msg <- captureFailureText $
runHegelTest
( \stgs ->
stgs
{ settingsTestCases = 200
, settingsDerandomize = True
, settingsDatabase = Disabled
}
)
$ \tc -> do
xs <-
draw
tc
( lists
(integers def { rangeMin = Just 0, rangeMax = Just 0 })
def
{ rangeMin = Just 0
, rangeMax = Just 12
}
)
assert (length xs < 10) ("expected list shorter than 10, got xs = " ++ show xs)
msg `shouldSatisfy` isInfixOf "expected list shorter than 10, got xs = [0,0,0,0,0,0,0,0,0,0]"
msg `shouldSatisfy` not . isInfixOf "got xs = [0,0,0,0,0,0,0,0,0,0,0]"
describe "E2E: assume" $ do
it "assume True does not reject" $ do
runHegelTest (withTestCases 10) $ \tc -> do
n <- draw tc (integers def)
assume tc True
when (n /= n) $
error "unreachable"
it "assume False rejects the test case" $ do
-- All test cases are rejected, but the test should still complete
-- (it just won't find any valid cases, which is fine for low counts)
runHegelTest (withTestCases 5) $ \tc -> do
n <- draw tc (integers def { rangeMin = Just 0, rangeMax = Just 100 })
assume tc (n > 50)
-- If we get here, n > 50 holds
pure ()
describe "E2E: draw multiple types" $ do
it "draws booleans, integers, floats, text, and binary" $ do
runHegelTest (withTestCases 10) $ \tc -> do
_ <- draw tc booleans
_ <- draw tc (integers def { rangeMin = Just 0, rangeMax = Just 100 })
_ <-
draw
tc
( floats def
{ floatBounds =
RangeOpts
{ rangeMin = Just 0.0,
rangeMax = Just 1.0
}
}
)
_ <- draw tc (text def { rangeMax = Just 50 })
_ <- draw tc (binary def { rangeMax = Just 50 })
pure ()
describe "E2E: note and target" $ do
it "note records a message without failure" $ do
runHegelTest (withTestCases 5) $ \tc -> do
n <- draw tc (integers def)
note tc ("Generated value: " ++ show n)
pure ()
it "target guides the search" $ do
runHegelTest (withTestCases 10) $ \tc -> do
n <- draw tc (integers def { rangeMin = Just 0, rangeMax = Just 1000 })
target tc (fromIntegral n) "maximize n"
pure ()