{-# 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))