Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit tests #32

Merged
merged 1 commit into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.cabal linguist-generated=true
7 changes: 7 additions & 0 deletions cabal.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
packages:
hspec-hedgehog.cabal

package hspec-hedgehog
ghc-options: -Werror

tests: True
34 changes: 23 additions & 11 deletions hspec-hedgehog.cabal

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions package.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
spec-version: 0.36.0

name: hspec-hedgehog
version: 0.1.1.0
synopsis: Integrate Hedgehog and Hspec!
description: Please see the README on GitHub at <https://github.com/parsonsmatt/hspec-hedgehog#readme>
category: Testing
author: Matt Parsons
maintainer: [email protected]
copyright: 2020 Matt Parsons

extra-source-files:
- README.md
- ChangeLog.md

github: parsonsmatt/hspec-hedgehog

dependencies:
- base >= 4.7 && < 5
- hspec >= 2.11.0 && < 3
- hspec-core >= 2.11.0 && < 3
- hedgehog >= 1.0.2 && < 2

library:
source-dirs: src
dependencies:
- QuickCheck >= 2.9.2 && < 3
- splitmix >= 0.0.1 && < 1

tests:
hspec-hedgehog-test:
main: Spec.hs
source-dirs: test
build-tools: hspec-discover
dependencies:
- HUnit
- QuickCheck
- hspec-hedgehog
ghc-options: -threaded -rtsopts -with-rtsopts=-N
59 changes: 59 additions & 0 deletions test/ExampleSpec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module ExampleSpec (spec) where
import Test.Hspec
import Control.Concurrent (threadDelay)
import Control.Monad.IO.Class (liftIO)
import Data.IORef (atomicModifyIORef', readIORef, newIORef)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Test.Hspec.Hedgehog (PropertyT, diff, forAll, hedgehog,
(/==), (===))
import Test.Hspec.QuickCheck (modifyMaxSuccess)

spec :: Spec
spec = do
describe "regular tests" $ do
it "works" $ do
True `shouldBe` True

describe "hedgehog" $ do
it "is useful if you get an ambiguous error" $ hedgehog $ do
"no ambiguity" === "no ambiguity"

describe "hedgehog tests" $ do
it "lets you use PropertyT directly" $ hedgehog $ do
x <- forAll $ Gen.integral (Range.linear 0 1000)
y <- forAll $ Gen.integral (Range.linear 0 5000)
diff (x + y) (>=) (x :: Integer)

it "lets you use PropertyT directly without forcing the type" $ do
x <- forAll $ Gen.integral (Range.linear 0 1000)
y <- forAll $ Gen.integral (Range.linear 0 5000)
diff (x + y) (>=) (x :: Integer)

it "renders a progress bit" $ hedgehog $ do
x <- forAll $ Gen.integral (Range.linear 0 1000)
y <- forAll $ Gen.integral (Range.linear 1 5000)
liftIO $ threadDelay (100 * x + y)

describe "with hooks" $ do
before (pure "Hello!") $ do
it "has functions" $ \str -> hedgehog $
str === "Hello!"

it "goes before or after" $ \str -> do
pure () :: PropertyT IO ()
str === "Hello!"

it "generates" $ \str -> hedgehog $ do
wrongLen <- forAll $ Gen.integral (Range.linear 0 3)
length str /== wrongLen

describe "modifyMaxSuccess" $ do
modifyMaxSuccess (\_ -> 10) $ do
beforeAll (newIORef (0 :: Integer)) $ do
it "counts to 10" $ \ref -> hedgehog $ do
liftIO $ atomicModifyIORef' ref (\a -> (a + 1, ()))
True === True
it "works" $ \ref -> do
val <- readIORef ref
val `shouldBe` 10
59 changes: 1 addition & 58 deletions test/Spec.hs
Original file line number Diff line number Diff line change
@@ -1,58 +1 @@
import Control.Concurrent (threadDelay)
import Control.Monad.IO.Class (liftIO)
import Data.IORef (atomicModifyIORef', readIORef, newIORef)
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Test.Hspec (before, beforeAll, describe, hspec, it, shouldBe)
import Test.Hspec.Hedgehog (PropertyT, diff, forAll, hedgehog,
(/==), (===))
import Test.Hspec.QuickCheck (modifyMaxSuccess)

main :: IO ()
main = hspec $ do
describe "regular tests" $ do
it "works" $ do
True `shouldBe` True

describe "hedgehog" $ do
it "is useful if you get an ambiguous error" $ hedgehog $ do
"no ambiguity" === "no ambiguity"

describe "hedgehog tests" $ do
it "lets you use PropertyT directly" $ hedgehog $ do
x <- forAll $ Gen.integral (Range.linear 0 1000)
y <- forAll $ Gen.integral (Range.linear 0 5000)
diff (x + y) (>=) (x :: Integer)

it "lets you use PropertyT directly without forcing the type" $ do
x <- forAll $ Gen.integral (Range.linear 0 1000)
y <- forAll $ Gen.integral (Range.linear 0 5000)
diff (x + y) (>=) (x :: Integer)

it "renders a progress bit" $ hedgehog $ do
x <- forAll $ Gen.integral (Range.linear 0 1000)
y <- forAll $ Gen.integral (Range.linear 1 5000)
liftIO $ threadDelay (100 * x + y)

describe "with hooks" $ do
before (pure "Hello!") $ do
it "has functions" $ \str -> hedgehog $
str === "Hello!"

it "goes before or after" $ \str -> do
pure () :: PropertyT IO ()
str === "Hello!"

it "generates" $ \str -> hedgehog $ do
wrongLen <- forAll $ Gen.integral (Range.linear 0 3)
length str /== wrongLen

describe "modifyMaxSuccess" $ do
modifyMaxSuccess (\_ -> 10) $ do
beforeAll (newIORef (0 :: Integer)) $ do
it "counts to 10" $ \ref -> hedgehog $ do
liftIO $ atomicModifyIORef' ref (\a -> (a + 1, ()))
True === True
it "works" $ \ref -> do
val <- readIORef ref
val `shouldBe` 10
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
90 changes: 90 additions & 0 deletions test/Test/Hspec/HedgehogSpec.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE NegativeLiterals #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE StandaloneDeriving #-}
module Test.Hspec.HedgehogSpec (spec) where

import Test.Hspec
import Test.Hspec.Core.Spec
import Test.Hspec.Core.Util (formatException, stripAnsi)

import Data.List
import Control.Exception

import qualified Test.HUnit.Lang as HUnit
import Test.QuickCheck (stdArgs, replay)
import Test.QuickCheck.Random (mkQCGen)

import Test.Hspec.Hedgehog hiding (eval)

deriving instance Eq Result
deriving instance Eq ResultStatus
deriving instance Eq FailureReason

instance Eq SomeException where
(==) = exceptionEq

exceptionEq :: SomeException -> SomeException -> Bool
exceptionEq a b
| Just ea <- fromException a, Just eb <- fromException b = ea == (eb :: ErrorCall)
| Just ea <- fromException a, Just eb <- fromException b = ea == (eb :: ArithException)
| otherwise = throw (HUnit.HUnitFailure Nothing $ HUnit.ExpectedButGot Nothing (formatException b) (formatException a))

progressCallback :: ProgressCallback
progressCallback _ = return ()

params :: Params
params = defaultParams {paramsQuickCheckArgs = stdArgs {replay = Just (mkQCGen 23, 0)}}

joinLines :: [String] -> String
joinLines = intercalate "\n"

failingProperty :: PropertyT IO ()
failingProperty = failure

failingPropertyLine :: Int
failingPropertyLine = __LINE__ - 3

spec :: Spec
spec = do
describe "evaluateExample" $ do
let
eval :: PropertyT IO () -> IO Result
eval p = evaluateExample p params ($ ()) progressCallback

context "on Success" $ do
it "includes the number of passed tests" $ do
eval success `shouldReturn` Result
" ✓ property passed 100 tests."
Success

it "includes classification" $ do
eval (label "foo" >> success) `shouldReturn` Result (joinLines [
" ✓ property passed 100 tests."
, " foo 100% ████████████████████"
]) Success

context "on Failure" $ do
it "includes the number of discarded tests" $ do
eval discard `shouldReturn` Result "" (Failure Nothing (Reason
" ⚐ property gave up after 10 discards, passed 0 tests."
))

it "provides a detailed failure message" $ do
Result "" (Failure (Just _loc) (ColorizedReason reason)) <- eval failingProperty
let line delta = " " <> show (failingPropertyLine + delta)
stripAnsi reason `shouldBe` joinLines [
" ✗ property failed at test/Test/Hspec/HedgehogSpec.hs:" <> show failingPropertyLine <> ":19"
, " after 1 test."
, " shrink path: 1:"
, " "
, " ┏━━ test/Test/Hspec/HedgehogSpec.hs ━━━"
, line -1 <> " ┃ failingProperty :: PropertyT IO ()"
, line 0 <> " ┃ failingProperty = failure"
, " ┃ ^^^^^^^^^^^^^^^^^^^^^^^^^"
, " "
, " This failure can be reproduced by running:"
, " > recheckAt (Seed 14375056955115587481 16778118630780010967) \"1:\" property"
, " "
]
Loading