packages feed

sized-grid-0.1.1.1: tests/Main.hs

{-# LANGUAGE ConstraintKinds       #-}
{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE TypeOperators         #-}

module Main where

import           SizedGrid

import           Test.Utils

import           Control.Lens        hiding (index)
import           Control.Monad       (replicateM)
import           Data.Functor.Rep
import           Data.Proxy
import           Generics.SOP        hiding (S, Z)
import           GHC.TypeLits
import qualified GHC.TypeLits        as GHC
import           Hedgehog
import qualified Hedgehog.Gen        as Gen
import qualified Hedgehog.Range      as Range
import           Test.Tasty
import           Test.Tasty.Hedgehog
import           Test.Tasty.HUnit

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)

genPeriodic :: (1 <= n, GHC.KnownNat n) => Gen (Periodic n)
genPeriodic = Periodic <$> Gen.enumBounded

genCoord :: SListI cs => NP Gen cs -> Gen (Coord cs)
genCoord start = Coord <$> hsequence start

gridTests ::
       forall cs a x y.
       ( Show (Coord cs)
       , Eq (Coord cs)
       , All IsCoord cs
       , GHC.KnownNat (MaxCoordSize cs)
       , Show a
       , Eq a
       , AllGridSizeKnown cs
       , cs ~ '[x,y]
       , GHC.KnownNat (MaxCoordSize '[y,x])
       )
    => Gen (Coord cs)
    -> Gen a
    -> [TestTree]
gridTests genC genA =
    let tabulateIndex =
            property $ do
                c <- forAll genC
                c === index (tabulate id :: Grid cs (Coord cs)) c
        collapseUnCollapse =
            property $ do
                g :: Grid cs a <- forAll (sequenceA $ pure genA)
                Just g === gridFromList (collapseGrid g)
        uncollapseCollapse =
            property $ do
                cg :: [[a]] <-
                    replicateM (fromIntegral $ natVal (Proxy @(CoordSized x))) $
                    replicateM (fromIntegral $ natVal (Proxy @(CoordSized y))) $ forAll genA
                Just cg === (collapseGrid <$> gridFromList @cs cg)
        doubleTranspose = property $ do
            g :: Grid cs a <- forAll (sequenceA $ pure genA)
            g === transposeGrid (transposeGrid g)
    in [ testProperty "Tabulate index" tabulateIndex
       , testProperty "Collapse UnCollapse" collapseUnCollapse
       , testProperty "UnCollapse and Collapse" uncollapseCollapse
       , testProperty "Transpose twice is id" doubleTranspose
       ]

twoDimensionalCoordTests :: (cs ~ '[x,y], All Show cs, All Eq cs) => Gen (Coord cs) -> [TestTree]
twoDimensionalCoordTests genC =
  let doubleTranspose = property $ do
          c <- forAll genC
          c === tranposeCoord (tranposeCoord c)
  in [testProperty "Transpose twice is id" doubleTranspose]

coordCreationTests ::
     (All Show cs, All Eq cs, Eq a, Show a, Show c, Eq c)
  => Gen (Coord (c ': cs))
  -> Gen a
  -> [TestTree]
coordCreationTests genC gen =
  [ testProperty "Create single coord" $
    property $ forAll gen >>= \g -> g === (singleCoord g ^. _1)
  , testProperty "Create double coord" $ property $ do
        a <- forAll gen
        b <- forAll gen
        let coord = appendCoord b $ singleCoord a
        a === coord ^. _2
        b === coord ^. _1
  , testProperty "Create triple coord" $ property $ do
        a <- forAll gen
        b <- forAll gen
        c <- forAll gen
        let coord = appendCoord c $ appendCoord b $ singleCoord a
        a === coord ^. _3
        b === coord ^. _2
        c === coord ^. _1
  , testProperty "Head and append" $ property $ do
        coord <- forAll genC
        a <- forAll gen
        let newCoord = appendCoord a coord
        a === newCoord ^. coordHead
        coord === newCoord ^. coordTail
  , testProperty "Tail destruction" $ property $ do
        coord <- forAll genC
        appendCoord (coord ^. coordHead) (coord ^. coordTail) === coord
  ]

main :: IO ()
main =
  let periodic =
        let g :: Gen (Periodic 10) = genPeriodic
        in [ semigroupLaws g
           , monoidLaws g
           , additiveGroupLaws g
           , affineSpaceLaws g
           , aesonLaws g
           ]
      hardWrap =
        let g :: Gen (HardWrap 10) = HardWrap <$> Gen.enumBounded
        in [semigroupLaws g, monoidLaws g, affineSpaceLaws g, aesonLaws g]
      coord =
        let g :: Gen (Coord '[ HardWrap 10, Periodic 20]) =
              genCoord
                ((HardWrap <$> Gen.enumBounded) :*
                 (Periodic <$> Gen.enumBounded) :*
                 Nil)
        in [ semigroupLaws g
           , monoidLaws g
           , affineSpaceLaws g
           , aesonLaws g
           , testAllCoordOrdered g
           ]
      coord2 =
        let g :: Gen (Coord '[ Periodic 10, Periodic 20]) =
              genCoord
                ((Periodic <$> Gen.enumBounded) :*
                 (Periodic <$> Gen.enumBounded) :*
                 Nil)
        in [ semigroupLaws g
           , monoidLaws g
           , affineSpaceLaws g
           , additiveGroupLaws g
           , aesonLaws g
           , testAllCoordOrdered g
           ]
  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
              (genCoord
                 ((HardWrap <$> Gen.enumBounded) :*
                  (Periodic <$> Gen.enumBounded) :*
                  Nil) :: Gen (Coord '[ HardWrap 10, Periodic 10]))
       , testGroup
           "Coord creation"
           (coordCreationTests
              (genCoord
                 ((HardWrap <$> Gen.enumBounded) :*
                  (Periodic <$> Gen.enumBounded) :*
                  Nil) :: Gen (Coord '[ HardWrap 10, Periodic 10]))
              (Gen.enumBounded :: Gen Int))
       , testGroup
           "Grid"
           ((gridTests
               @'[ Periodic 10, Periodic 11]
               (genCoord $
                (Periodic <$> Gen.enumBounded) :* (Periodic <$> Gen.enumBounded) :*
                Nil))
              (Gen.int $ Range.linear 0 100) ++
            [ applicativeLaws
                (Proxy @(Grid '[ Periodic 10, Periodic 11]))
                (Gen.int $ Range.linear 0 100)
            , aesonLaws
                (sequenceA $
                 pure @(Grid '[ Periodic 10, Periodic 11]) $
                 Gen.int $ Range.linear 0 100)
            , eq1Laws (Proxy @(Grid '[ Periodic 10, Periodic 20]))
            ])
       ]