sized-grid-0.1.1.4: tests/Main.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Main where
import SizedGrid
import Test.Shrink
import Test.Utils
import Control.Lens hiding (index)
import Control.Monad (replicateM)
import Data.Functor.Rep
import Data.Proxy
import qualified Data.Vector as V
import Generics.SOP hiding (S, Z)
import qualified GHC.Generics as GHC
import GHC.TypeLits
import qualified GHC.TypeLits as GHC
import Test.QuickCheck (Arbitrary (..), Arbitrary1 (..),
Property, oneof, property, (.&&.),
(===))
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck (testProperty)
instance (1 <= n, KnownNat n) => Arbitrary (Periodic n) where
arbitrary = Periodic <$> oneof (map pure [minBound .. maxBound])
instance (1 <= n, KnownNat n) => Arbitrary (HardWrap n) where
arbitrary = HardWrap <$> oneof (map pure [minBound .. maxBound])
instance (All Arbitrary cs, SListI cs) => Arbitrary (Coord cs) where
arbitrary = Coord <$> hsequence (hcpure (Proxy @Arbitrary) arbitrary)
instance AllSizedKnown cs => Arbitrary1 (Grid cs) where
liftArbitrary g = sequenceA (pure g)
instance (AllSizedKnown cs, Arbitrary a) => Arbitrary (Grid cs a) where
arbitrary = liftArbitrary arbitrary
assertOrderd :: Ord a => [a] -> Assertion
assertOrderd =
let helper [] = True
helper (x:xs) = all (x <=) xs && helper xs
in assertBool "Ordered" . helper
testAllCoordOrdered ::
forall cs proxy. (All Eq cs, All Ord cs, All IsCoord cs)
=> proxy (Coord cs)
-> TestTree
testAllCoordOrdered _ =
testCase "allCoord is ordered" $ assertOrderd (allCoord @cs)
gridTests ::
forall cs a x y.
( Show (Coord cs)
, Eq (Coord cs)
, All IsCoord cs
, AllSizedKnown cs
, Show a
, Eq a
, cs ~ '[ x, y]
, KnownNat (CoordSized y * CoordSized x)
, KnownNat (CoordSized x * CoordSized y)
, Arbitrary a
, Arbitrary x
, Arbitrary y
)
=> Proxy (Coord cs)
-> Proxy a
-> [TestTree]
gridTests genC genA =
let tabulateIndex :: Coord cs -> Property
tabulateIndex c = c === index (tabulate id :: Grid cs (Coord cs)) c
collapseUnCollapse :: Property
collapseUnCollapse =
property $ do
g :: Grid cs a <- sequenceA $ pure arbitrary
return (Just g === gridFromList (collapseGrid g))
uncollapseCollapse =
property $ do
cg :: [[a]] <-
replicateM (fromIntegral $ natVal (Proxy @(CoordSized x))) $
replicateM (fromIntegral $ natVal (Proxy @(CoordSized y))) $
arbitrary
return (Just cg === (collapseGrid <$> gridFromList @cs cg))
doubleTranspose =
property $ do
g :: Grid cs a <- sequenceA $ pure arbitrary
return (g === transposeGrid (transposeGrid g))
in [ testProperty "Tabulate index" tabulateIndex
, testProperty "Collapse UnCollapse" collapseUnCollapse
, testProperty "UnCollapse and Collapse" uncollapseCollapse
, testProperty "Transpose twice is id" doubleTranspose
]
splitTests ::
forall c cs a.
( Show a
, Eq a
, Num a
, All IsCoord (c ': cs)
, KnownNat (CoordSized c * MaxCoordSize cs)
, KnownNat (MaxCoordSize cs)
, KnownNat (5 * MaxCoordSize cs)
, KnownNat (3 * MaxCoordSize cs)
, KnownNat (2 * MaxCoordSize cs)
, KnownNat (CoordSized (CoordFromNat c 2) * MaxCoordSize cs)
, KnownNat (CoordSized (CoordFromNat c 2))
, AllSizedKnown cs
, Arbitrary a
)
=> Proxy (c ': cs)
-> Proxy a
-> [TestTree]
splitTests _ _ =
let splitAndCombine =
property $ do
g :: Grid (c ': cs) a <- sequenceA $ pure arbitrary
return (g === combineGrid (splitGrid g))
combineAndSplit =
property $ do
g :: Grid '[ c] (Grid cs a) <-
sequenceA $ pure (sequenceA $ pure arbitrary)
return (g === splitGrid (combineGrid g))
higherSplitAndCombine =
property $ do
g :: Grid (Ordinal 5 ': cs) a <- sequenceA $ pure arbitrary
let (a :: Grid (Ordinal 3 ': cs) a, b) = splitHigherDim g
return (g === combineHigherDim a b)
higherCombineAndSplit =
property $ do
g1 :: Grid (Ordinal 3 ': cs) a <- sequenceA $ pure arbitrary
g2 :: Grid (Ordinal 2 ': cs) a <- sequenceA $ pure arbitrary
let g = combineHigherDim g1 g2
return ((g1, g2) === splitHigherDim g)
in [ testProperty "Split and Combine" splitAndCombine
, testProperty "Combine and split" combineAndSplit
, testProperty "Split and Combine Higher dim" higherSplitAndCombine
, testProperty "Combine and Split Higher dim" higherCombineAndSplit
]
twoDimensionalCoordTests ::
forall cs x y . (cs ~ '[ x, y], All Show cs, All Eq cs, All Arbitrary cs)
=> Proxy (Coord cs)
-> [TestTree]
twoDimensionalCoordTests _ =
let doubleTranspose :: Coord cs -> Property
doubleTranspose c = c === tranposeCoord (tranposeCoord c)
in [testProperty "Transpose twice is id" doubleTranspose]
coordCreationTests ::
forall cs a c.
( All Show cs
, All Eq cs
, Eq a
, Show a
, Show c
, Eq c
, Arbitrary a
, All Arbitrary cs
, Arbitrary c
)
=> Proxy (Coord (c ': cs))
-> Proxy a
-> [TestTree]
coordCreationTests genC gen =
[ testProperty "Create single coord" $
property $ \(g :: a) -> g === (singleCoord g ^. _1)
, testProperty "Create double coord" $
property $ \(a :: a) (b :: a) ->
let coord = b :| singleCoord a
in (a === coord ^. _2) .&&. (b === coord ^. _1)
, testProperty "Create triple coord" $
property $ \(a :: a) (b :: a) (c :: a) ->
let coord = c :| (b :| singleCoord a)
in (a === coord ^. _3) .&&. (b === coord ^. _2) .&&. (c === coord ^. _1)
, testProperty "Head and append" $
property $ \(coord :: Coord (c ': cs)) (a :: a) ->
let newCoord = appendCoord a coord
in (a === newCoord ^. coordHead) .&&. (coord === newCoord ^. coordTail)
, testProperty "Tail destruction" $
property $ \(coord :: Coord (c ': cs)) ->
appendCoord (coord ^. coordHead) (coord ^. coordTail) === coord
]
main :: IO ()
main =
let periodic =
let p = Proxy @(Periodic 10)
in [ semigroupLaws p
, monoidLaws p
, additiveGroupLaws p
, affineSpaceLaws p
, aesonLaws p
, isCoordLaws p
]
hardWrap =
let p = Proxy @(HardWrap 10)
in [ semigroupLaws p
, monoidLaws p
, affineSpaceLaws p
, aesonLaws p
, isCoordLaws p
]
coord =
let p = Proxy @(Coord '[ HardWrap 10, Periodic 20])
in [ semigroupLaws p
, monoidLaws p
, affineSpaceLaws p
, aesonLaws p
, testAllCoordOrdered p
]
coord2 =
let p = Proxy @(Coord '[ Periodic 10, Periodic 20])
in [ semigroupLaws p
, monoidLaws p
, affineSpaceLaws p
, additiveGroupLaws p
, aesonLaws p
, testAllCoordOrdered p
]
in defaultMain $
testGroup
"tests"
[ testGroup "Periodic 20" periodic
, testGroup "HardWrap 20" hardWrap
, testGroup "Coord [HardWrap 10, Periodic 20]" coord
, testGroup "Coord [Periodic 10, Periodic 20]" coord2
, testGroup "2D Coords" $
twoDimensionalCoordTests (Proxy @(Coord '[ HardWrap 10, Periodic 10]))
, testGroup
"Coord creation"
(coordCreationTests
(Proxy @(Coord '[ HardWrap 10, Periodic 10]))
(Proxy @Int))
, testGroup
"Grid"
((gridTests
(Proxy @(Coord ('[ Periodic 10, Periodic 11])))
(Proxy @Int) ++
[ applicativeLaws
(Proxy @(Grid '[ Periodic 10, Periodic 11]))
(Proxy @Int)
, aesonLaws (Proxy @(Grid '[ Periodic 10, Periodic 11] Int))
, eq1Laws (Proxy @(Grid '[ Periodic 10, Periodic 20]))
]))
, testGroup
"Splitting"
(splitTests
(Proxy @('[ HardWrap 8, HardWrap 3, HardWrap 5]))
(Proxy @Int))
, shrinkTests
]