Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| {-# LANGUAGE CPP #-} | |
| {-# LANGUAGE DataKinds #-} | |
| {-# LANGUAGE DefaultSignatures #-} | |
| {-# LANGUAGE DeriveFunctor #-} | |
| {-# LANGUAGE DeriveGeneric #-} | |
| {-# LANGUAGE FlexibleContexts #-} | |
| {-# LANGUAGE RankNTypes #-} | |
| {-# LANGUAGE ScopedTypeVariables #-} | |
| {-# LANGUAGE TypeFamilies #-} | |
| {-# LANGUAGE TypeOperators #-} | |
| {-# LANGUAGE UndecidableInstances #-} | |
| -- | Generate fake values with full constructor coverage | |
| -- | |
| -- The idea behind Fake.Cover is that although exhaustive testing is highly | |
| -- exponential, you can cover a large portion of the likely problem cases by | |
| -- exercising all the constructors of a data type and associated fields. This | |
| -- approach only requires a sub-exponential number of cases--far fewer than what | |
| -- you need for the exhaustive approach. The number of test cases needed to | |
| -- ensure that you have full coverage of all the constructors is given by the | |
| -- following relations for product and sum types: | |
| -- | |
| -- numCases (a, b) = max (numCases a) (numCases b) | |
| -- | |
| -- numCases (Either a b) = numCases a + numCases b | |
| -- | |
| -- See the test suite for examples of how many values are generated for | |
| -- different data types. | |
| module Fake.Cover | |
| ( gcover | |
| , Coverage(..) | |
| , Cover(..) | |
| ) where | |
| ------------------------------------------------------------------------------ | |
| import Control.Applicative | |
| import GHC.Generics as G | |
| ------------------------------------------------------------------------------ | |
| import Fake.Types | |
| ------------------------------------------------------------------------------ | |
| ------------------------------------------------------------------------------ | |
| -- | Coverage is a list of values, implemented here with a newtype around a | |
| -- list of fake value generators. It's @[FGen a]@ instead of @FGen [a]@ | |
| -- because we don't want to have to evaluate the 'FGen' monad to work with | |
| -- coverage lists. | |
| newtype Coverage a = Coverage { unCoverage :: [FGen a] } | |
| deriving (Functor) | |
| instance Applicative Coverage where | |
| pure = Coverage . pure . pure | |
| Coverage as <*> Coverage bs = Coverage $ zipWith (<*>) | |
| (as ++ take (newlen - alen) (cycle as)) | |
| (bs ++ take (newlen - blen) (cycle bs)) | |
| where | |
| alen = length as | |
| blen = length bs | |
| newlen = max alen blen | |
| instance Alternative Coverage where | |
| empty = Coverage empty | |
| Coverage as <|> Coverage bs = Coverage (as ++ bs) | |
| ------------------------------------------------------------------------------ | |
| -- | A type class that generates a list of values giving full construcor | |
| -- coverage for data types. You can write your own instances by hand or you | |
| -- can use the default instance which calls 'gcover' provided your data type | |
| -- has a Generic instance. | |
| class Cover a where | |
| cover :: Coverage a | |
| default cover :: (Generic a, GCover ga, ga ~ G.Rep a) => Coverage a | |
| cover = gcover | |
| instance Cover () where | |
| cover = gcover | |
| instance Cover a => Cover (Maybe a) where | |
| cover = gcover | |
| instance (Cover a, Cover b) => Cover (Either a b) where | |
| cover = gcover | |
| instance (Cover a, Cover b) => Cover (a,b) where | |
| cover = gcover | |
| instance (Cover a, Cover b, Cover c) => Cover (a,b,c) where | |
| cover = gcover | |
| instance (Cover a, Cover b, Cover c, Cover d) => Cover (a,b,c,d) where | |
| cover = gcover | |
| instance (Cover a, Cover b, Cover c, Cover d, Cover e) | |
| => Cover (a,b,c,d,e) where | |
| cover = gcover | |
| instance (Cover a, Cover b, Cover c, Cover d, Cover e, Cover f) | |
| => Cover (a,b,c,d,e,f) where | |
| cover = gcover | |
| instance (Cover a, Cover b, Cover c, Cover d, Cover e, Cover f, Cover g) | |
| => Cover (a,b,c,d,e,f,g) where | |
| cover = gcover | |
| -- GHC only has Generic instances up to 7-tuples | |
| ------------------------------------------------------------------------------ | |
| -- | A generic function that gives you full constructor coverage for a data | |
| -- type. Using this function as the 'Cover' instance for a data type avoids | |
| -- the need to explicitly enumerate values that include coverage of all | |
| -- constructors. | |
| gcover :: (Generic a, GCover ga, ga ~ G.Rep a) => Coverage a | |
| gcover = Coverage $ fmap G.to <$> genericCover | |
| ------------------------------------------------------------------------------ | |
| -- | Used to implement 'gcover'. | |
| class GCover a where | |
| genericCover :: [FGen (a x)] | |
| instance GCover G.U1 where | |
| genericCover = pure $ pure G.U1 | |
| instance Cover c => GCover (G.K1 i c) where | |
| genericCover = fmap G.K1 <$> unCoverage cover | |
| instance GCover f => GCover (G.M1 i c f) where | |
| genericCover = fmap G.M1 <$> genericCover | |
| instance (GCover a, GCover b) => GCover (a G.:*: b) where | |
| genericCover = unCoverage $ | |
| (G.:*:) <$> Coverage genericCover <*> Coverage genericCover | |
| instance (GCover a, GCover b) => GCover (a G.:+: b) where | |
| genericCover = unCoverage $ | |
| (G.L1 <$> Coverage genericCover) <|> (G.R1 <$> Coverage genericCover) |