Main.hs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. {-# LANGUAGE DataKinds #-}
  2. {-# LANGUAGE DeriveAnyClass #-}
  3. {-# LANGUAGE DeriveGeneric #-}
  4. {-# LANGUAGE DerivingStrategies #-}
  5. {-# LANGUAGE ScopedTypeVariables #-}
  6. {-# LANGUAGE TypeApplications #-}
  7. {-
  8. - Copyright (C) 2019 Koz Ross <koz.ross@retro-freedom.nz>
  9. -
  10. - This program is free software: you can redistribute it and/or modify
  11. - it under the terms of the GNU General Public License as published by
  12. - the Free Software Foundation, either version 3 of the License, or
  13. - (at your option) any later version.
  14. -
  15. - This program is distributed in the hope that it will be useful,
  16. - but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. - GNU General Public License for more details.
  19. -
  20. - You should have received a copy of the GNU General Public License
  21. - along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. -}
  23. module Main where
  24. import Data.Finitary (Finitary (..))
  25. import Data.Int (Int16, Int32, Int8)
  26. import Data.Ord (Down (..))
  27. import qualified Data.Vector.Sized as V
  28. import qualified Data.Vector.Storable.Sized as VS
  29. import Data.Vector.Unboxed.Sized (Unbox)
  30. import qualified Data.Vector.Unboxed.Sized as VU
  31. import Data.Word (Word16, Word32, Word8)
  32. import Foreign.Storable (Storable)
  33. import GHC.Generics (Generic)
  34. import Hedgehog ((===), Gen, PropertyT, forAll)
  35. import qualified Hedgehog.Gen as Gen
  36. import Hedgehog.Range (constantBounded)
  37. import Test.Hspec (SpecWith, describe, hspec, it, parallel)
  38. import Test.Hspec.Hedgehog (hedgehog, modifyMaxSize)
  39. main :: IO ()
  40. main = hspec . parallel $ do
  41. describe "Bijectivity and order preservation" $ do
  42. checkBijection "Char" Gen.unicode
  43. checkBijection "Word8" (Gen.enumBounded @_ @Word8)
  44. modifyMaxSize (const 10000)
  45. . checkBijection "Word16"
  46. $ Gen.enumBounded @_ @Word16
  47. modifyMaxSize (const 10000)
  48. . checkBijection "Word32"
  49. $ Gen.enumBounded @_ @Word32
  50. modifyMaxSize (const 10000)
  51. . checkBijection "Word64"
  52. $ Gen.word64 constantBounded
  53. checkBijection "Int8" (Gen.enumBounded @_ @Int8)
  54. modifyMaxSize (const 10000)
  55. . checkBijection "Int16"
  56. $ Gen.enumBounded @_ @Int16
  57. modifyMaxSize (const 10000)
  58. . checkBijection "Int32"
  59. $ Gen.enumBounded @_ @Int32
  60. modifyMaxSize (const 10000)
  61. . checkBijection "Int64"
  62. $ Gen.int64 constantBounded
  63. modifyMaxSize (const 10000)
  64. . checkBijection "Int"
  65. $ Gen.int constantBounded
  66. modifyMaxSize (const 10000)
  67. . checkBijection "Word"
  68. $ Gen.word constantBounded
  69. describe "Down" $ do
  70. checkMonotonic "Bool" Gen.bool
  71. modifyMaxSize (const 10000)
  72. . checkMonotonic "Int"
  73. $ (Gen.enumBounded @_ @Int)
  74. modifyMaxSize (const 10000)
  75. . checkMonotonic "(Either Int Bool)"
  76. $ Gen.choice
  77. [ Left <$> Gen.enumBounded @_ @Int,
  78. Right <$> Gen.enumBounded @_ @Bool
  79. ]
  80. modifyMaxSize (const 10000)
  81. . checkMonotonic "(Int, Bool)"
  82. $ ( (,)
  83. <$> Gen.enumBounded @_ @Int
  84. <*> Gen.enumBounded @_ @Bool
  85. )
  86. modifyMaxSize (const 10000)
  87. . checkMonotonic "of a user-defined type"
  88. $ genFoo
  89. describe "Fixed-length vectors" $ do
  90. modifyMaxSize (const 10000)
  91. . checkStorable "Int8"
  92. . genStorable
  93. $ Gen.enumBounded @_ @Int8
  94. modifyMaxSize (const 10000)
  95. . checkUnboxed "Int8"
  96. . genUnboxed
  97. $ Gen.enumBounded @_ @Int8
  98. modifyMaxSize (const 10000)
  99. . checkRegular "Int8"
  100. . genRegular
  101. $ Gen.enumBounded @_ @Int8
  102. modifyMaxSize (const 10000)
  103. . checkUnboxed "(Int8, Int8)"
  104. . genUnboxed
  105. $ ( (,) <$> Gen.enumBounded @_ @Int8
  106. <*> Gen.enumBounded @_ @Int8
  107. )
  108. modifyMaxSize (const 10000)
  109. . checkRegular "(Int8, Int8)"
  110. . genRegular
  111. $ ( (,) <$> Gen.enumBounded @_ @Int8
  112. <*> Gen.enumBounded @_ @Int8
  113. )
  114. modifyMaxSize (const 10000)
  115. . checkRegular "Either Int8 Bool"
  116. . genRegular
  117. . Gen.choice
  118. $ [ Left <$> Gen.enumBounded @_ @Int8,
  119. Right <$> Gen.bool
  120. ]
  121. modifyMaxSize (const 10000)
  122. . checkRegular "a user defined type"
  123. . genRegular
  124. $ genFoo
  125. -- Helpers
  126. data Foo
  127. = Bar
  128. | Baz Int8
  129. | Quux (Int8, Int8)
  130. deriving stock (Eq, Ord, Generic, Show)
  131. deriving anyclass (Finitary)
  132. checkStorable ::
  133. forall a.
  134. (Storable a, Finitary a, Show a, Ord a) =>
  135. String ->
  136. Gen (VS.Vector 10 a) ->
  137. SpecWith ()
  138. checkStorable name =
  139. it ("should biject a Storable Vector of " <> name)
  140. . hedgehog
  141. . bicheck @(VS.Vector 10 a)
  142. checkRegular ::
  143. forall a.
  144. (Finitary a, Show a, Ord a) =>
  145. String ->
  146. Gen (V.Vector 10 a) ->
  147. SpecWith ()
  148. checkRegular name =
  149. it ("should biject a Vector of " <> name)
  150. . hedgehog
  151. . bicheck @(V.Vector 10 a)
  152. checkUnboxed ::
  153. forall a.
  154. (Unbox a, Finitary a, Show a, Ord a) =>
  155. String ->
  156. Gen (VU.Vector 10 a) ->
  157. SpecWith ()
  158. checkUnboxed name =
  159. it ("should biject an Unboxed Vector of " <> name)
  160. . hedgehog
  161. . bicheck @(VU.Vector 10 a)
  162. bicheck :: forall a. (Show a, Finitary a, Ord a) => Gen a -> PropertyT IO ()
  163. bicheck gen = do
  164. v <- forAll gen
  165. let iv = toFinite v
  166. v === (fromFinite . toFinite $ v)
  167. iv === (toFinite @a . fromFinite $ iv)
  168. v' <- forAll gen
  169. let iv' = toFinite v'
  170. compare v v' === compare iv iv'
  171. genStorable :: (Storable a) => Gen a -> Gen (VS.Vector 10 a)
  172. genStorable = VS.replicateM
  173. genUnboxed :: (Unbox a) => Gen a -> Gen (VU.Vector 10 a)
  174. genUnboxed = VU.replicateM
  175. genRegular :: Gen a -> Gen (V.Vector 10 a)
  176. genRegular = V.replicateM
  177. genFoo :: Gen Foo
  178. genFoo =
  179. Gen.choice
  180. [ pure Bar,
  181. Baz <$> Gen.enumBounded,
  182. Quux <$> ((,) <$> Gen.enumBounded <*> Gen.enumBounded)
  183. ]
  184. checkBijection :: forall a. (Show a, Ord a, Finitary a) => String -> Gen a -> SpecWith ()
  185. checkBijection name gen =
  186. it ("should biject " <> name <> " with fromFinite and toFinite preserving order")
  187. . hedgehog
  188. $ go
  189. where
  190. go = do
  191. x <- forAll gen
  192. let ix = toFinite x
  193. x === (fromFinite . toFinite $ x)
  194. ix === (toFinite @a . fromFinite $ ix)
  195. y <- forAll gen
  196. let iy = toFinite y
  197. compare x y === compare ix iy
  198. checkMonotonic :: (Show a, Finitary a) => String -> Gen a -> SpecWith ()
  199. checkMonotonic name gen =
  200. it ("should be Ord-monotonic on Down " <> name)
  201. . hedgehog
  202. $ go
  203. where
  204. go = do
  205. x <- forAll gen
  206. y <- forAll gen
  207. let dx = toFinite . Down $ x
  208. let dy = toFinite . Down $ y
  209. let ix = toFinite x
  210. let iy = toFinite y
  211. case compare ix iy of
  212. LT -> compare dx dy === GT
  213. EQ -> compare dx dy === EQ
  214. GT -> compare dx dy === LT