{-# LANGUAGE StandaloneDeriving #-}
module Main where
import Data.Monoid
import Text.Megaparsec.Pos (Pos, unsafePos, SourcePos(..), sourceName, sourceLine, sourceColumn)
import qualified Data.Map as Map
import qualified Data.Set as Set
import Test.QuickCheck
import Test.QuickCheck.Property
import Test.Tasty
import Test.Tasty.QuickCheck
import LambdaCube.Compiler.DesugaredSource
import LambdaCube.Compiler.Core
----------------------------------------------------------------- Main
-- Usage: ":main --quickcheck-max-size 30 --quickcheck-tests 100"
main = defaultMain $ testGroup "Compiler"
[ testGroup "Infer" $ concat [
monoidTestProperties "SI" (arbitrary :: Gen SI)
-- , monoidTestProperties "Infos" (arbitrary :: Gen Infos) -- list is always a monoid
-- , monoidTestProperties "MaxDB" (arbitrary :: Gen MaxDB)
]
]
----------------------------------------------------------------- Arbitraries
-- SourcePos
instance Arbitrary Pos where
arbitrary = unsafePos . getPositive <$> arbitrary
instance Arbitrary SourcePos where
arbitrary = SourcePos <$> arbitrary <*> arbitrary <*> arbitrary
shrink pos
| n <- sourceName pos, l <- sourceLine pos, c <- sourceColumn pos
= [SourcePos n' l' c' | n' <- shrink n, l' <- shrink l, c' <- shrink c]
-- TODO: Diagonalize shrink
-- TODO: generate only valid positions
instance Arbitrary SPos where
arbitrary = SPos <$> arbitrary <*> arbitrary
-- TODO: review
instance Arbitrary FileInfo where
arbitrary = FileInfo <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
-- Range
instance Arbitrary Range where
-- TODO: generate only valid ranges (positive numbers, second position is after first one)
arbitrary = Range <$> arbitrary <*> arbitrary <*> arbitrary
shrink (Range a b c) = Range <$> shrink a <*> shrink b <*> shrink c
-- SI
instance Arbitrary SI where
arbitrary = oneof [NoSI . Set.fromList <$> arbitrary, RangeSI <$> arbitrary]
shrink (NoSI ds) = []
shrink (RangeSI r) = mempty: map RangeSI (shrink r)
instance MonoidEq SI where
NoSI a =::= NoSI b = a == b
RangeSI a =::= RangeSI b = a == b
instance TestShow SI where
testShow (NoSI a) = "NoSI " ++ show a
testShow (RangeSI a) = "RangeSI " ++ show a
-- Infos
{- list is always a monoid
instance Arbitrary Info where
arbitrary = Info <$> arbitrary
instance Arbitrary Infos where
arbitrary = Infos . Map.fromList <$> arbitrary
shrink (Infos m) = map (Infos . Map.fromList . shrink) $ Map.toList m
deriving instance Eq Infos
instance MonoidEq Infos where
(=::=) = (==)
instance TestShow Infos where
testShow (Infos i) = "Infos " ++ show i
-}
-- MaxDB
{- todo
instance Arbitrary MaxDB where
arbitrary = MaxDB <$> {-fmap (fmap abs)-} arbitrary
shrink (MaxDB m) = map MaxDB $ shrink m
instance MonoidEq MaxDB where
MaxDB (Just n) =::= MaxDB (Just m) = n == m
MaxDB Nothing =::= MaxDB Nothing = True
MaxDB (Just 0) =::= MaxDB Nothing = True
MaxDB Nothing =::= MaxDB (Just 0) = True
_ =::= _ = False
instance TestShow MaxDB where
testShow (MaxDB a) = "MaxDB " ++ show a
-}
----------------------------------------------------------------- Test building blocks
class Monoid m => MonoidEq m where
(=::=) :: m -> m -> Bool
infix 4 =::=
monoidTestProperties name gen =
[ testProperty (name ++ " monoid left identity") (propMonoidLeftIdentity gen)
, testProperty (name ++ " monoid right identity") (propMonoidRightIdentity gen)
, testProperty (name ++ " monoid associativity") (propMonoidAssociativity gen)
]
----------------------------------------------------------------- Properties
-- * Monoid
propMonoidLeftIdentity :: (MonoidEq m, TestShow m) => Gen m -> Property
propMonoidLeftIdentity gen = forAll' gen (\x -> x =*= mempty <> x)
propMonoidRightIdentity :: (MonoidEq m, TestShow m) => Gen m -> Property
propMonoidRightIdentity gen = forAll' gen (\x -> x =*= x <> mempty)
propMonoidAssociativity :: (Arbitrary m, MonoidEq m, TestShow m) => Gen m -> Property
propMonoidAssociativity gen =
forAll' gen $ \x -> forAll' gen $ \y -> forAll' gen $ \z ->
(x <> y) <> z =*= x <> (y <> z)
----------------------------------------------------------------- Tools
class TestShow t where
testShow :: t -> String
-- | Like '=::=', but prints a counterexample when it fails.
infix 4 =*=
(=*=) :: (MonoidEq a, TestShow a) => a -> a -> Property
x =*= y =
counterexample (testShow x ++ " /= " ++ testShow y) (x =::= y)
forAll' :: (TestShow a, Testable prop)
=> Gen a -> (a -> prop) -> Property
forAll' gen pf =
MkProperty $
gen >>= \x ->
unProperty (counterexample (testShow x) (pf x))