Main.hs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. {-
  2. - Copyright (C) 2019 Koz Ross <koz.ross@retro-freedom.nz>
  3. -
  4. - This program is free software: you can redistribute it and/or modify
  5. - it under the terms of the GNU General Public License as published by
  6. - the Free Software Foundation, either version 3 of the License, or
  7. - (at your option) any later version.
  8. -
  9. - This program is distributed in the hope that it will be useful,
  10. - but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. - GNU General Public License for more details.
  13. -
  14. - You should have received a copy of the GNU General Public License
  15. - along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. -}
  17. {-# LANGUAGE DeriveAnyClass #-}
  18. {-# LANGUAGE DeriveGeneric #-}
  19. {-# LANGUAGE TypeInType #-}
  20. {-# LANGUAGE AllowAmbiguousTypes #-}
  21. {-# LANGUAGE ScopedTypeVariables #-}
  22. {-# LANGUAGE TypeApplications #-}
  23. {-# LANGUAGE OverloadedStrings #-}
  24. {-# LANGUAGE TypeOperators #-}
  25. {-# LANGUAGE TypeFamilies #-}
  26. {-# LANGUAGE GADTs #-}
  27. {-# LANGUAGE RankNTypes #-}
  28. module Main where
  29. import Data.Maybe (isNothing)
  30. import Data.Int (Int8, Int16, Int32, Int64)
  31. import Data.Word (Word8, Word16, Word32, Word64)
  32. import GHC.TypeNats
  33. import GHC.Generics
  34. import Data.Kind (Type)
  35. import Data.Finite (Finite)
  36. import Data.Proxy (Proxy(..))
  37. import Control.Monad.Loops (andM)
  38. import Data.Typeable (Typeable, typeRep)
  39. import Data.String (fromString)
  40. import Hedgehog
  41. import qualified Hedgehog.Gen as G
  42. import qualified Hedgehog.Range as R
  43. import qualified Data.Vector.Sized as VS
  44. import qualified Data.Vector.Unboxed.Sized as VUS
  45. import qualified Data.Vector.Storable.Sized as VSS
  46. import qualified Data.Bit as B
  47. import qualified Data.Bit.ThreadSafe as BTS
  48. import Data.Finitary
  49. -- A representation of types
  50. data SomeFinitaryRep where
  51. SomeFinitaryRep :: forall (a :: Type) . (Finitary a, Show a, Eq a, Typeable a, 1 <= (Cardinality a)) => Proxy a -> SomeFinitaryRep
  52. -- A representation of test functions
  53. data SomeTestFunction where
  54. SomeTestFunction :: (forall (a :: Type) . (Finitary a, Show a, Eq a, 1 <= (Cardinality a)) => Proxy a -> Property) -> SomeTestFunction
  55. -- Some weird generic
  56. data Foo = Bar | Baz (VS.Vector 4 Bool) | Quux Word8
  57. deriving (Eq, Show, Typeable, Generic, Finitary)
  58. -- Something I will need for further tests
  59. iterateMN :: forall (m :: Type -> Type) (a :: Type) (b :: Type) . (Eq a, Bounded a, Enum a, Monad m) => a -> (b -> m b) -> b -> m b
  60. iterateMN i f x = if i == minBound
  61. then f x
  62. else iterateMN (pred i) f x >>= f
  63. -- How much testing do I want to do for random inputs?
  64. testLimit :: forall (a :: Type) (b :: Type) . (Finitary a, Num b) => b
  65. testLimit = fromIntegral . (* 2) . min 32767 . natVal $ Proxy @(Cardinality a)
  66. -- Generators
  67. choose :: forall (a :: Type) m . (MonadGen m, Finitary a) => m a
  68. choose = fromFinite <$> chooseFinite
  69. chooseFinite :: forall (n :: Nat) m . (KnownNat n, MonadGen m) => m (Finite n)
  70. chooseFinite = fromIntegral <$> G.integral (R.linear 0 limit)
  71. where limit = subtract @Integer 1 . fromIntegral . natVal @n $ Proxy
  72. -- Data
  73. allTheTypes :: [SomeFinitaryRep]
  74. allTheTypes = [
  75. SomeFinitaryRep @() Proxy,
  76. SomeFinitaryRep @(Proxy Int) Proxy,
  77. SomeFinitaryRep @Bool Proxy,
  78. SomeFinitaryRep @B.Bit Proxy,
  79. SomeFinitaryRep @BTS.Bit Proxy,
  80. SomeFinitaryRep @Ordering Proxy,
  81. SomeFinitaryRep @Char Proxy,
  82. SomeFinitaryRep @Word8 Proxy,
  83. SomeFinitaryRep @Word16 Proxy,
  84. SomeFinitaryRep @Word32 Proxy,
  85. SomeFinitaryRep @Word64 Proxy,
  86. SomeFinitaryRep @Int16 Proxy,
  87. SomeFinitaryRep @Int8 Proxy,
  88. SomeFinitaryRep @Int32 Proxy,
  89. SomeFinitaryRep @Int64 Proxy,
  90. SomeFinitaryRep @Int Proxy,
  91. SomeFinitaryRep @Word Proxy,
  92. SomeFinitaryRep @(Maybe Word8) Proxy,
  93. SomeFinitaryRep @(Either Word8 Int8) Proxy,
  94. SomeFinitaryRep @(Word8, Int8) Proxy,
  95. SomeFinitaryRep @(VS.Vector 4 Bool) Proxy,
  96. SomeFinitaryRep @(VUS.Vector 4 Bool) Proxy,
  97. SomeFinitaryRep @(VSS.Vector 4 Bool) Proxy,
  98. SomeFinitaryRep @Foo Proxy
  99. ]
  100. constructTest :: SomeTestFunction -> GroupName -> IO Bool
  101. constructTest (SomeTestFunction testFunc) name = checkParallel . Group name $ fmap go allTheTypes
  102. where go (SomeFinitaryRep p) = (fromString . show . typeRep $ p, testFunc p)
  103. -- Properties
  104. isBijection :: forall (a :: Type) . (Finitary a, Show a, Eq a) => Proxy a -> Property
  105. isBijection _ = withTests (testLimit @a) (property $ do x <- forAll $ choose @a
  106. x === (fromFinite . toFinite $ x))
  107. startIsCorrect :: forall (a :: Type) . (Finitary a, Show a, Eq a, 1 <= (Cardinality a)) => Proxy a -> Property
  108. startIsCorrect _ = property $ start @a === fromFinite minBound
  109. previousStartNothing :: forall (a :: Type) . (Finitary a, 1 <= (Cardinality a)) => Proxy a -> Property
  110. previousStartNothing _ = property $ assert (isNothing . previous $ start @a)
  111. endNextNothing :: forall (a :: Type) . (Finitary a, 1 <= (Cardinality a)) => Proxy a -> Property
  112. endNextNothing _ = property $ assert (isNothing . next $ end @a)
  113. endIsCorrect :: forall (a :: Type) . (Finitary a, Show a, Eq a, 1 <= (Cardinality a)) => Proxy a -> Property
  114. endIsCorrect _ = property $ end @a === fromFinite maxBound
  115. skipZeroIsPrevious :: forall (a :: Type) . (Finitary a, Show a, Eq a) => Proxy a -> Property
  116. skipZeroIsPrevious _ = withTests (testLimit @a) (property $ do x <- forAll $ choose @a
  117. previous @a @Maybe x === previousSkipping 0 x)
  118. skipZeroIsNext :: forall (a :: Type) . (Finitary a, Show a, Eq a) => Proxy a -> Property
  119. skipZeroIsNext _ = withTests (testLimit @a) (property $ do x <- forAll $ choose @a
  120. next @a @Maybe x === nextSkipping 0 x)
  121. skipNPreviousAgrees :: forall (a :: Type) . (Finitary a, Show a, Eq a) => Proxy a -> Property
  122. skipNPreviousAgrees _ = withTests (testLimit @a) (property $ do x <- forAll $ choose @a
  123. i <- forAll $ choose @(Finite (Cardinality a))
  124. iterateMN @Maybe i previous x === previousSkipping i x)
  125. skipNNextAgrees :: forall (a :: Type) . (Finitary a, Show a, Eq a) => Proxy a -> Property
  126. skipNNextAgrees _ = withTests (testLimit @a) (property $ do x <- forAll $ choose @a
  127. i <- forAll $ choose @(Finite (Cardinality a))
  128. iterateMN @Maybe i next x === nextSkipping i x)
  129. -- All the tests I want to use
  130. allTests :: [(SomeTestFunction, GroupName)]
  131. allTests = [
  132. (SomeTestFunction isBijection, "bijectivity"),
  133. (SomeTestFunction startIsCorrect, "start"),
  134. (SomeTestFunction endIsCorrect, "end"),
  135. (SomeTestFunction previousStartNothing, "previous + start"),
  136. (SomeTestFunction endNextNothing, "next + end"),
  137. (SomeTestFunction skipZeroIsPrevious, "previousSkipping 0"),
  138. (SomeTestFunction skipZeroIsNext, "nextSkipping 0"),
  139. (SomeTestFunction skipNPreviousAgrees, "previousSkipping n"),
  140. (SomeTestFunction skipNNextAgrees, "nextSkipping n")
  141. ]
  142. main :: IO Bool
  143. main = andM . fmap (uncurry constructTest) $ allTests