packages feed

tinycheck-0.1.0.0: test/Test/Generic.hs

{- HLINT ignore "Avoid reverse" -}
{- HLINT ignore "Evaluate" -}
{- HLINT ignore "Redundant not" -}
module Test.Generic (tests) where

-- base
import Data.List (isPrefixOf, tails)
import GHC.Generics (Generic, Generically (..))

-- tasty
import Test.Tasty
import Test.Tasty.TinyCheck

-- tinycheck
import Data.TestCases

{- | A deeply-nested record type used to exercise the generic 'Arbitrary'
instance and to verify that tinycheck eventually reaches large inputs.
-}
data LargeRecord = LargeRecord
  { field1 :: Bool
  , field2 :: String
  , field3 :: Int
  , field4 :: Maybe LargeRecord
  , field5 :: [Bool]
  , field6 :: [Int]
  , field7 :: Maybe LargeRecord
  , field8 :: Either String LargeRecord
  }
  deriving stock (Show, Generic)
  deriving (Arbitrary) via Generically LargeRecord

-- | Count non-overlapping occurrences of a substring.
countOccurrences :: String -> String -> Int
countOccurrences needle haystack =
  length $ filter (isPrefixOf needle) (tails haystack)

tests :: TestTree
tests =
  testGroup
    "Generic"
    [ testGroup
        "LargeRecord (Generic)"
        [ testProperty "show ends with }" $
            \(r :: LargeRecord) -> last (show r) == '}'
        ]
    , testGroup
        "Preconditions"
        [ testProperty "n > 0 ==> n * 2 > 0" $
            \(n :: Int) -> (n > 0) ==> property (n * 2 > 0)
        ]
    , testGroup
        "Expected failures"
        [ expectFailureWith
            "falsified"
            "False is always falsified"
            False
        , expectFailureWith "falsified" "const False always falsified" $
            \(_ :: Int) -> False
        , expectFailureWith "0" "reverse [0] /= [0] is falsified with 0" $
            \(x :: Int) -> reverse [x] /= [x]
        , expectFailureWith "True" "even True falsified: input shown" $
            \(b :: Bool) -> not b
        , expectFailureWithN 1_000_000_0 "LargeRecord" "LargeRecord appears at most 6 times in show" $
            \(r :: LargeRecord) -> countOccurrences "LargeRecord" (show r) <= 6
        ]
    ]