packages feed

finite-1.5.0.0: src/test/Test.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Test
-- Maintainer  :  Felix Klein
--
-- Simple TestSuite.
--
-----------------------------------------------------------------------------

{-# LANGUAGE

    DeriveGeneric
  , ExplicitNamespaces
  , ImplicitParams
  , LambdaCase
  , MultiParamTypeClasses
  , RankNTypes
  , RecordWildCards
  , TemplateHaskell

  #-}

-----------------------------------------------------------------------------

module Test
  ( tests
  ) where

-----------------------------------------------------------------------------

import Distribution.TestSuite
  ( TestInstance(..)
  , Progress(..)
  , Result(..)
  , Test(..)
  )

import Data.Hashable
  ( hash
  )

import Data.Ix
  ( range
  )

import Control.Exception
  ( assert
  )

import GHC.Generics
  ( Generic
  )

import Test.QuickCheck
  ( Result
      ( Success
      , Failure
      , reason
      )
  , quickCheckResult
  )

import Finite.TH

import Finite

-----------------------------------------------------------------------------

newInstance "AInst"

data Bounds = Bounds { size :: Int }

baseInstance [t|Bounds|] [|size|] "AInst"

newBaseInstance [t|Bounds|] [|size|] "BInst"

data BBounds = BBounds { bnds :: Bounds }

extendInstance [t|AInst|] [t|BBounds|] [|bnds|]

data GInst =
    AData
  | BData AInst
  | CData AInst BInst
  deriving (Eq, Ord, Generic)

instance Finite Bounds GInst

newtype OInst = OInst { oInst :: Int } deriving (Eq, Ord)

instance Finite Bounds OInst where
  elements _ = size ?bounds
  offset _ = 3
  value = OInst
  index = oInst

data TInst =
    DData
  | EData OInst
  | FData AInst BInst
  deriving (Eq, Ord, Generic)

instance Finite Bounds TInst

-----------------------------------------------------------------------------

tests
  :: IO [Test]

tests = return
  [ Test t01
  , Test t02
  , Test t03
  , Test t04
  , Test t05
  , Test t06
  ]

  where
    t01 = TestInstance
      { run =
         (Finished . allPass) <$> sequence
           (map quickCheckResult
              [ \x -> x == aInst (AInst x)
              , \x -> AInst x == AInst x
              , \x -> AInst x < AInst (x + 1)
              , \x -> show (AInst x) == show (AInst x)
              , \x -> show (AInst x) == show x
              , \x -> hash (AInst x) == hash (AInst x)
              , \x -> not $ null $ range (AInst 0, AInst $ abs x)
              , \x -> (AInst x) + (AInst 1) == (AInst 1) + (AInst x)
              ] ++
              [ quickCheckResult $ \x -> aInst x == aInst x
              ])
      , name = "TH: newInstance"
      , tags = []
      , options = []
      , setOption = \_ _ -> Right t01
      }

    t02 = TestInstance
      { run =
         (Finished . allPass) <$> sequence
           (map quickCheckResult
              [ \x -> sb x $ elements (type AInst) == abs x + 1
              , \x -> sb x $ offset (type AInst) == 0
              , \x -> sb x $ map index vs == [0,1..abs x]
              , \x -> sb x $ map value [0,1..abs x] == vs
              , \x -> sb x $ all (\x -> x == value (index x)) vs
              , \x -> sb x $ complement (filter (odd . index) vs)
                               == filter (even . index) vs
              , \x -> sb x $ initial (type AInst) |<=| final (type AInst)
              , \x -> sb x $ abs x < 1 ||
                               next (initial (type AInst))
                                 |>=| initial (type AInst)
              , \x -> sb x $ abs x < 1 ||
                               previous (final (type AInst))
                                 |/=| final (type AInst)
              ])
      , name = "TH: baseInstance"
      , tags = []
      , options = []
      , setOption = \_ _ -> Right t02
      }
     where
      vs :: FiniteBounds Bounds => [AInst]
      vs = values

      sb :: Int -> (FiniteBounds Bounds => c) -> c
      sb x = withBounds $ Bounds $ abs x + 1

    t03 = TestInstance
      { run =
         (Finished . allPass) <$> sequence
           (map quickCheckResult
              [ \x -> x == bInst (BInst x)
              , \x -> BInst x == BInst x
              , \x -> BInst x < BInst (x + 1)
              , \x -> show (BInst x) == show (BInst x)
              , \x -> show (BInst x) == show x
              , \x -> hash (BInst x) == hash (BInst x)
              , \x -> not $ null $ range (BInst 0, BInst $ abs x)
              , \x -> (BInst x) + (BInst 1) == (BInst 1) + (BInst x)
              , \x -> sb x $ elements (type BInst) == abs x + 1
              , \x -> sb x $ offset (type BInst) == 0
              , \x -> sb x $ map index vs == [0,1..abs x]
              , \x -> sb x $ map value [0,1..abs x] == vs
              , \x -> sb x $ all (\x -> x == value (index x)) vs
              , \x -> sb x $ complement (filter (odd . index) vs)
                               == filter (even . index) vs
              , \x -> sb x $ initial (type BInst) |<=| final (type BInst)
              , \x -> sb x $ abs x < 1 ||
                               next (initial (type BInst))
                                 |>=| initial (type BInst)
              , \x -> sb x $ abs x < 1 ||
                               previous (final (type BInst))
                                 |/=| final (type BInst)
              ] ++
              [ quickCheckResult $ \x -> bInst x == bInst x
              ])
      , name = "TH: newBaseInstance"
      , tags = []
      , options = []
      , setOption = \_ _ -> Right t03
      }
     where
      vs :: FiniteBounds Bounds => [BInst]
      vs = values

      sb :: Int -> (FiniteBounds Bounds => c) -> c
      sb x = withBounds $ Bounds $ abs x + 1

    t04 = TestInstance
      { run =
         (Finished . allPass) <$> sequence
           (map quickCheckResult
              [ \x -> sb x $ elements (type AInst) == abs x + 1
              , \x -> sb x $ offset (type AInst) == 0
              , \x -> sb x $ map index vs == [0,1..abs x]
              , \x -> sb x $ map value [0,1..abs x] == vs
              , \x -> sb x $ all (\x -> x == value (index x)) vs
              , \x -> sb x $ complement (filter (odd . index) vs)
                               == filter (even . index) vs
              , \x -> sb x $ initial (type AInst) |<=| final (type AInst)
              , \x -> sb x $ abs x < 1 ||
                             next (initial (type AInst))
                               |>=| initial (type AInst)
              , \x -> sb x $ abs x < 1 ||
                             previous (final (type AInst))
                               |/=| final (type AInst)
              ])
      , name = "TH: extendInstance"
      , tags = []
      , options = []
      , setOption = \_ _ -> Right t04
      }
     where
      vs :: FiniteBounds BBounds => [AInst]
      vs = values

      sb :: Int -> (FiniteBounds BBounds => c) -> c
      sb x = withBounds $ BBounds $ Bounds $ abs x + 1

    t05 = TestInstance
      { run =
         (Finished . allPass) <$> sequence
           (map quickCheckResult
              [ \x -> sb x $ elements (type GInst)
                               == 1 + (abs x + 1) + (abs x + 1) * (abs x + 1)
              , \x -> sb x $ offset (type GInst) == 0
              , \x -> sb x $ map index vs == [0,1..elements (type GInst) - 1]
              , \x -> sb x $ map value [0,1..elements (type GInst) - 1] == vs
              , \x -> sb x $ all (\x -> x == value (index x)) vs
              , \x -> sb x $ complement (filter (odd . index) vs)
                               == filter (even . index) vs
              , \x -> sb x $ initial (type GInst) |<=| final (type GInst)
              , \x -> sb x $ abs x < 1 ||
                             next (initial (type GInst))
                               |>=| initial (type GInst)
              , \x -> sb x $ abs x < 1 ||
                             previous (final (type GInst))
                               |/=| final (type GInst)
              ])
      , name = "Generics"
      , tags = []
      , options = []
      , setOption = \_ _ -> Right t05
      }
     where
      vs :: FiniteBounds Bounds => [GInst]
      vs = values

      sb :: Int -> (FiniteBounds Bounds => c) -> c
      sb x = withBounds $ Bounds $ abs x + 1

    t06 = TestInstance
      { run =
         (Finished . allPass) <$> sequence
           (map quickCheckResult
              [ \x -> sb x $ elements (type OInst) == abs x + 1
              , \x -> sb x $ offset (type OInst) == 3
              , \x -> sb x $ map index ovs == [3,4..abs x + 3]
              , \x -> sb x $ map value [3,4..abs x + 3] == ovs
              , \x -> sb x $ all (\x -> x == value (index x)) ovs
              , \x -> sb x $ complement (filter (odd . index) ovs)
                        == filter (even . index) ovs
              , \x -> sb x $ initial (type OInst) |<=| final (type OInst)
              , \x -> sb x $ abs x < 1 ||
                             next (initial (type OInst))
                               |>=| initial (type OInst)
              , \x -> sb x $ abs x < 1 ||
                             previous (final (type OInst))
                               |/=| final (type OInst)
              , \x -> sb x $ elements (type TInst)
                               == 1 + (abs x + 1) + (abs x + 1) * (abs x + 1)
              , \x -> sb x $ offset (type TInst) == 0
              , \x -> sb x $ map index tvs == [0,1..elements (type TInst) - 1]
              , \x -> sb x $ map value [0,1..elements (type TInst) - 1] == tvs
              , \x -> sb x $ all (\x -> x == value (index x)) tvs
              , \x -> sb x $ complement (filter (odd . index) tvs)
                               == filter (even . index) tvs
              , \x -> sb x $ initial (type TInst) |<=| final (type TInst)
              , \x -> sb x $ abs x < 1 ||
                             next (initial (type TInst))
                               |>=| initial (type TInst)
              , \x -> sb x $ abs x < 1 ||
                             previous (final (type TInst))
                               |/=| final (type TInst)
              ])
      , name = "Offset"
      , tags = []
      , options = []
      , setOption = \_ _ -> Right t06
      }
     where
      ovs :: FiniteBounds Bounds => [OInst]
      ovs = values

      tvs :: FiniteBounds Bounds => [TInst]
      tvs = values

      sb :: Int -> (FiniteBounds Bounds => c) -> c
      sb x = withBounds $ Bounds $ abs x + 1

    allPass xs = case dropWhile (isSuccess) xs of
      []  -> Pass
      x:_ -> case x of
        Failure{..} -> Fail reason
        _           -> assert False undefined

    isSuccess = \case
      Success {} -> True
      _          -> False

-----------------------------------------------------------------------------