HMock-0.2.0.0: test/Extras.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module Extras where
import Data.List (isPrefixOf)
import Data.Typeable (Typeable)
import Test.HMock
import Test.Hspec
import Test.QuickCheck hiding (once)
instance Arbitrary Multiplicity where
arbitrary =
between
<$> (fromInteger <$> arbitrary)
<*> (fromInteger . getNonNegative <$> arbitrary)
multiplicityTests :: SpecWith ()
multiplicityTests = do
describe "Multiplicity" $ do
it "describes itself" $
example $ do
show once `shouldBe` "once"
show anyMultiplicity `shouldBe` "any number of times"
show (2 :: Multiplicity) `shouldBe` "twice"
show (3 :: Multiplicity) `shouldBe` "3 times"
show (atLeast 1) `shouldBe` "at least once"
show (atLeast 2) `shouldBe` "at least twice"
show (atLeast 3) `shouldBe` "at least 3 times"
show (atMost 1) `shouldBe` "at most once"
show (atMost 2) `shouldBe` "at most twice"
show (atMost 3) `shouldBe` "at most 3 times"
show (between 2 3) `shouldBe` "2 or 3 times"
show (between 2 5) `shouldBe` "2 to 5 times"
it "correctly implements sum" $
property $
\m1 m2 (NonNegative n) ->
let natSums k = [(i, k - i) | i <- [0 .. k]]
in meetsMultiplicity (m1 + m2) n
== any
(\(j, k) -> meetsMultiplicity m1 j && meetsMultiplicity m2 k)
(natSums n)
predicateTests :: SpecWith ()
predicateTests = do
describe "Predicate" $ do
it "describes itself" $
example $ do
show anything `shouldBe` "anything"
show (eq "foo") `shouldBe` "\"foo\""
show (neq "foo") `shouldBe` "≠ \"foo\""
show (gt "foo") `shouldBe` "> \"foo\""
show (geq "foo") `shouldBe` "≥ \"foo\""
show (lt "foo") `shouldBe` "< \"foo\""
show (leq "foo") `shouldBe` "≤ \"foo\""
show (just (gt "foo")) `shouldBe` "Just (> \"foo\")"
show (left (gt "foo")) `shouldBe` "Left (> \"foo\")"
show (right (gt "foo")) `shouldBe` "Right (> \"foo\")"
show (zipP (eq 1) (eq 2) :: Predicate (Int, Int)) `shouldBe` "(1,2)"
show (zip3P (eq 1) (eq 2) (eq 3) :: Predicate (Int, Int, Int))
`shouldBe` "(1,2,3)"
show
( zip4P (eq 1) (eq 2) (eq 3) (eq 4) ::
Predicate (Int, Int, Int, Int)
)
`shouldBe` "(1,2,3,4)"
show
( zip5P (eq 1) (eq 2) (eq 3) (eq 4) (eq 5) ::
Predicate (Int, Int, Int, Int, Int)
)
`shouldBe` "(1,2,3,4,5)"
show (lt "foo" `andP` gt "bar")
`shouldBe` "< \"foo\" and > \"bar\""
show (lt "bar" `orP` gt "foo")
`shouldBe` "< \"bar\" or > \"foo\""
show (notP (gt "foo")) `shouldBe` "not > \"foo\""
show (startsWith "fun") `shouldBe` "starts with \"fun\""
show (endsWith "ing") `shouldBe` "ends with \"ing\""
show (hasSubstr "i") `shouldBe` "has substring \"i\""
show (hasSubsequence "abc") `shouldBe` "has subsequence \"abc\""
show (caseInsensitive eq "foo") `shouldBe` "(case insensitive) \"foo\""
show (caseInsensitive startsWith "foo")
`shouldBe` "(case insensitive) starts with \"foo\""
show (caseInsensitive endsWith "foo")
`shouldBe` "(case insensitive) ends with \"foo\""
show (matchesRegex "foo" :: Predicate String)
`shouldBe` "/foo/"
show (matchesCaseInsensitiveRegex "foo" :: Predicate String)
`shouldBe` "/foo/i"
show (containsRegex "foo" :: Predicate String)
`shouldBe` "contains /foo/"
show (containsCaseInsensitiveRegex "foo" :: Predicate String)
`shouldBe` "contains /foo/i"
show (isEmpty :: Predicate [()]) `shouldBe` "empty"
show (nonEmpty :: Predicate [()]) `shouldBe` "nonempty"
show (sizeIs (gt 5) :: Predicate [()]) `shouldBe` "size > 5"
show (elemsAre [gt 5, eq 5] :: Predicate [Int]) `shouldBe` "[> 5,5]"
show (unorderedElemsAre [gt 5, eq 5] :: Predicate [Int])
`shouldBe` "(any order) [> 5,5]"
show (each (gt 5) :: Predicate [Int]) `shouldBe` "each (> 5)"
show (contains (gt 5) :: Predicate [Int]) `shouldBe` "contains (> 5)"
show (containsAll [gt 5] :: Predicate [Int])
`shouldBe` "contains all of [> 5]"
show (containsOnly [gt 5] :: Predicate [Int])
`shouldBe` "contains only [> 5]"
show (containsKey (eq "foo") :: Predicate [(String, String)])
`shouldBe` "contains key \"foo\""
show
( containsEntry (eq "foo") (eq "bar") ::
Predicate [(String, String)]
)
`shouldBe` "contains entry (\"foo\",\"bar\")"
show (keysAre [eq 1, eq 2, eq 3] :: Predicate [(Int, String)])
`shouldBe` "keys are [1,2,3]"
show
( entriesAre [(eq 1, eq "one"), (eq 2, eq "two")] ::
Predicate [(Int, String)]
)
`shouldBe` "entries are [(1,\"one\"),(2,\"two\")]"
show (approxEq 1.0 :: Predicate Double) `shouldBe` "≈ 1.0"
show (finite :: Predicate Double) `shouldBe` "finite"
show (infinite :: Predicate Double) `shouldBe` "infinite"
show (nAn :: Predicate Double) `shouldBe` "NaN"
show (is even :: Predicate Int)
`shouldSatisfy` ("custom predicate at " `isPrefixOf`)
show ($(qIs [|even|]) :: Predicate Int) `shouldBe` "even"
show (with length (gt 5) :: Predicate String)
`shouldSatisfy` ("property at " `isPrefixOf`)
show ($(qWith [|length|]) (gt 5) :: Predicate String)
`shouldBe` "length: > 5"
it "matches patterns" $
example $ do
let p = $(qMatch [p|Just (Left _)|])
show p `shouldBe` "Just (Left _)"
accept p (Just (Left "foo")) `shouldBe` True
accept p (Just (Right "foo")) `shouldBe` False
accept p Nothing `shouldBe` False
it "checks types" $
example $ do
let p1 :: Typeable a => Predicate a
p1 = typed @String anything
p2 :: Typeable a => Predicate a
p2 = typed @String (eq "foo")
show (p1 :: Predicate String)
`shouldBe` "anything :: [Char]"
show (p1 :: Predicate Int)
`shouldBe` "anything :: [Char]"
accept p1 "foo" `shouldBe` True
accept p1 "bar" `shouldBe` True
accept p1 () `shouldBe` False
accept p1 (5 :: Int) `shouldBe` False
show (p2 :: Predicate String)
`shouldBe` "\"foo\" :: [Char]"
show (p2 :: Predicate Int)
`shouldBe` "\"foo\" :: [Char]"
accept p2 "foo" `shouldBe` True
accept p2 "bar" `shouldBe` False
accept p2 (5 :: Int) `shouldBe` False