Helpers.hs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. {-# LANGUAGE CPP #-}
  2. -- | Some functions, generators and instances suitable for writing
  3. -- QuickCheck properties.
  4. module Internal.Helpers
  5. ( module Internal.Helpers
  6. , module Test.QuickCheck
  7. -- * Tasty framework functions
  8. , testGroup
  9. , testProperties
  10. , testProperty
  11. , TestTree
  12. ) where
  13. import Control.Monad
  14. import qualified Control.Monad.Fail as Fail
  15. import Data.Functor
  16. import Data.Semigroup ( (<>), Semigroup )
  17. import Test.QuickCheck
  18. import Test.Tasty ( testGroup, TestName, TestTree )
  19. import Test.Tasty.QuickCheck ( testProperties, testProperty )
  20. import Agda.Utils.Functor
  21. import Agda.Utils.List1 ( List1, pattern (:|) )
  22. import qualified Agda.Utils.List1 as List1
  23. import Agda.Utils.PartialOrd
  24. import Agda.Utils.POMonoid
  25. ------------------------------------------------------------------------
  26. -- QuickCheck helpers
  27. #if !MIN_VERSION_QuickCheck(2,12,5)
  28. isSuccess :: Result -> Bool
  29. isSuccess Success{} = True
  30. isSuccess _ = False
  31. #endif
  32. quickCheck' :: Testable prop => prop -> IO Bool
  33. quickCheck' p = fmap isSuccess $ quickCheckResult p
  34. quickCheckWith' :: Testable prop => Args -> prop -> IO Bool
  35. quickCheckWith' args p = fmap isSuccess $ quickCheckWithResult args p
  36. ------------------------------------------------------------------------
  37. -- Helpers for type signatures of algebraic properties.
  38. -- | Binary operator.
  39. type BinOp a = a -> a -> a
  40. -- | Property over 1 variable.
  41. type Prop1 a = a -> Bool
  42. type Property1 a = a -> Property
  43. -- | Property over 2 variables.
  44. type Prop2 a = a -> a -> Bool
  45. type Property2 a = a -> a -> Property
  46. -- | Property over 3 variables.
  47. type Prop3 a = a -> a -> a -> Bool
  48. type Property3 a = a -> a -> a -> Property
  49. -- | Property over 4 variables.
  50. type Prop4 a = a -> a -> a -> a -> Bool
  51. type Property4 a = a -> a -> a -> a -> Property
  52. ------------------------------------------------------------------------
  53. -- Algebraic properties
  54. -- | Is the operator associative?
  55. isAssociative :: Eq a => BinOp a -> Prop3 a
  56. isAssociative (+) = \ x y z ->
  57. x + (y + z) == (x + y) + z
  58. -- | Is the operator commutative?
  59. isCommutative :: Eq a => BinOp a -> Prop2 a
  60. isCommutative (+) = \ x y ->
  61. x + y == y + x
  62. -- | Is the operator idempotent?
  63. isIdempotent :: Eq a => BinOp a -> Prop1 a
  64. isIdempotent (/\) = \ x ->
  65. (x /\ x) == x
  66. -- | Is the element a zero for the operator?
  67. isZero :: Eq a => a -> BinOp a -> Prop1 a
  68. isZero zer (*) = \ x ->
  69. (zer * x == zer)
  70. &&
  71. (x * zer == zer)
  72. -- | Is the element a unit for the operator?
  73. isIdentity :: Eq a => a -> BinOp a -> Prop1 a
  74. isIdentity one (*) = \ x ->
  75. (one * x == x)
  76. &&
  77. (x * one == x)
  78. -- | Does the first operator distribute (from the left) over the second one?
  79. isLeftDistributive :: Eq a => BinOp a -> BinOp a -> Prop3 a
  80. isLeftDistributive (*) (+) = \ x y z ->
  81. x * (y + z) == (x * y) + (x * z)
  82. -- | Does the first operator distribute (from the right) over the second one?
  83. isRightDistributive :: Eq a => BinOp a -> BinOp a -> Prop3 a
  84. isRightDistributive (*) (+) = \ x y z ->
  85. (x + y) * z == (x * z) + (y * z)
  86. -- | Does the first operator distribute over the second one?
  87. isDistributive :: Eq a => BinOp a -> BinOp a -> Prop3 a
  88. isDistributive (*) (+) = \ x y z ->
  89. isLeftDistributive (*) (+) x y z &&
  90. isRightDistributive (*) (+) x y z
  91. -- | Does the operator satisfy the semigroup law?
  92. isSemigroup :: (Eq a, Semigroup a) => Prop3 a
  93. isSemigroup = isAssociative (<>)
  94. -- | Does the operator satisfy the monoid laws?
  95. isMonoid :: (Eq a, Semigroup a, Monoid a) => Property3 a
  96. isMonoid x y z =
  97. -- ASR (2017-01-25): What if `mappend ≠ (<>)`? It isn't possible
  98. -- because we are using the `-Wnoncanonical-monoid-instances` flag.
  99. isSemigroup x y z .&&.
  100. isIdentity mempty mappend x
  101. isSemigroupMorphism :: (Eq b, Semigroup a, Semigroup b) => (a -> b) -> Prop2 a
  102. isSemigroupMorphism f = \ x y ->
  103. f (x <> y) == f x <> f y
  104. -- | Monoid morphism where the source monoid is given by a unit and a multiplication.
  105. isMonoidMorphismUnder :: (Eq b, Monoid b) => a -> (a -> a -> a) -> (a -> b) -> Property2 a
  106. isMonoidMorphismUnder one (*) f = \ x y ->
  107. f one == mempty
  108. .&&.
  109. f (x * y) == f x `mappend` f y
  110. isMonoidMorphism :: (Eq b, Monoid a, Monoid b) => (a -> b) -> Property2 a
  111. isMonoidMorphism = isMonoidMorphismUnder mempty mappend
  112. -- | The semiring is given by an additive monoid, a unit and a multiplication.
  113. isSemimodule :: (Eq m, Monoid r, Monoid m) => r -> (r -> r -> r) -> (r -> m -> m)
  114. -> r -> r -> Property2 m
  115. isSemimodule one (*) op r s m n =
  116. isMonoidMorphism (op r) m n
  117. .&&.
  118. isMonoidMorphism (`op` m) r s
  119. .&&.
  120. -- isMonoidMorphismUnder one (*) (Endo . op) r s -- Problem: no Eq Endo
  121. -- expand to points:
  122. op one m == m
  123. .&&.
  124. op (r * s) m == op r (op s m)
  125. -- | The semiring is given by an additive monoid, a unit and a multiplication.
  126. isAlmostSemimodule :: (Eq m, Semigroup r, Monoid r, Semigroup m, Monoid m) => r -> (r -> r -> r) -> (r -> m -> m)
  127. -> r -> r -> Property2 m
  128. isAlmostSemimodule one (*) op r s m n =
  129. isMonoidMorphism (op r) m n
  130. .&&.
  131. isSemigroupMorphism (`op` m) r s
  132. .&&.
  133. -- isMonoidMorphismUnder one (*) (Endo . op) r s -- Problem: no Eq Endo
  134. -- expand to points:
  135. op one m == m
  136. .&&.
  137. op (r * s) m == op r (op s m)
  138. -- | Is the semigroup operation monotone in both arguments
  139. -- wrt. to the associated partial ordering?
  140. -- We state this with only three variables to get fewer discarded tests.
  141. isMonotoneComposition :: (Eq a, POSemigroup a) => Property3 a
  142. isMonotoneComposition x x' y =
  143. related x POLE x' ==> related (x <> y) POLE (x' <> y) && related (y <> x) POLE (y <> x')
  144. -- | Do the semigroup operation and the inverse composition form
  145. -- a Galois connection?
  146. isGaloisConnection :: (Eq a, Semigroup a, LeftClosedPOMonoid a) => Prop3 a
  147. isGaloisConnection p x y =
  148. related (inverseCompose p x) POLE y == related x POLE (p <> y)
  149. ------------------------------------------------------------------------
  150. -- Generators
  151. -- | Generates natural numbers.
  152. natural :: (Integral i) => Gen i
  153. natural = fromInteger . abs <$> arbitrary
  154. -- | Generates positive numbers.
  155. positive :: (Integral i) => Gen i
  156. positive = succ <$> natural
  157. -- | Generates a list of elements picked from a given list.
  158. listOfElements :: [a] -> Gen [a]
  159. listOfElements [] = return []
  160. listOfElements xs = listOf $ elements xs
  161. -- | Generates an officially non-empty list, while 'listOf1' does it inofficially.
  162. list1Of :: Gen a -> Gen (List1 a)
  163. list1Of = List1.fromList <.> listOf1
  164. -- | If the given list is non-empty, then an element from the list is
  165. -- generated, and otherwise an arbitrary element is generated.
  166. elementsUnlessEmpty :: Arbitrary a => [a] -> Gen a
  167. elementsUnlessEmpty [] = arbitrary
  168. elementsUnlessEmpty xs = elements xs
  169. -- | Generates values of 'Maybe' type, using the given generator to
  170. -- generate the contents of the 'Just' constructor.
  171. maybeGen :: Gen a -> Gen (Maybe a)
  172. maybeGen gen = frequency [ (1, return Nothing)
  173. , (9, Just <$> gen)
  174. ]
  175. -- | 'Coarbitrary' \"generator\" for 'Maybe'.
  176. maybeCoGen :: (a -> Gen b -> Gen b) -> (Maybe a -> Gen b -> Gen b)
  177. maybeCoGen f Nothing = variant 0
  178. maybeCoGen f (Just x) = variant 1 . f x
  179. -- | Generates two elements.
  180. two :: Gen a -> Gen (a, a)
  181. two gen = liftM2 (,) gen gen
  182. -- | Generates three elements.
  183. three :: Gen a -> Gen (a, a, a)
  184. three gen = liftM3 (,,) gen gen gen
  185. -- | \"Resizes\" a generator.
  186. smaller
  187. :: Int -- ^ This number must not be 0.
  188. -> Gen a
  189. -> Gen a
  190. smaller k g = sized $ \ n -> resize (1 + div n k) g
  191. ------------------------------------------------------------------------
  192. -- Instances
  193. instance Fail.MonadFail Gen where
  194. fail = error
  195. instance CoArbitrary a => CoArbitrary (List1 a) where
  196. coarbitrary (x :| xs) = coarbitrary (x, xs)
  197. ------------------------------------------------------------------------
  198. -- Test driver
  199. -- | Runs the tests, and returns 'True' if all tests were successful.
  200. runTests :: String -- ^ A label for the tests. Used for
  201. -- informational purposes.
  202. -> [IO Bool]
  203. -> IO Bool
  204. runTests name tests = do
  205. putStrLn name
  206. and <$> sequence tests