-- | This module defines utilities to test 'Speculate' itself. -- -- It should never be exported in a cabal package, and should not be included -- in Haddock documentation. Hence the weird name, simply "Test". -- -- This module exports a Listable Expr instance, that does not, by any means, -- list all possible expressions. But instead, list expressions based on the -- names exported by this module. module Test ( -- * Module exports module Test.LeanCheck , module Test.Speculate , module Data.Express.Fixtures , module Test.ListableExpr -- * Test reporting , reportTests , getMaxTestsFromArgs , mainTest , printLines -- * Test types , listThyInefficient , Thyght (..) , Equation (..) -- * Test fixtures , foo, goo -- * Enumerate expressions , expressionsT ) where import Test.LeanCheck import Data.Express.Fixtures hiding (compose) import System.Environment (getArgs, getProgName) import System.Exit (exitFailure) import Data.List (elemIndices, sort) import Test.Speculate hiding (getArgs) import Test.Speculate.Reason import Test.Speculate.Reason.Order import Test.Speculate.Utils import Test.ListableExpr -- test reporting -- reportTests :: String -> [Bool] -> IO () reportTests s tests = do case elemIndices False tests of [] -> putStrLn $ s ++ ": tests passed" is -> do putStrLn (s ++ ": failed tests: " ++ show is) exitFailure getMaxTestsFromArgs :: Int -> IO Int getMaxTestsFromArgs n = do as <- getArgs return $ case as of (s:_) -> read s _ -> n mainTest :: (Int -> [Bool]) -> Int -> IO () mainTest tests n' = do pn <- getProgName n <- getMaxTestsFromArgs n' reportTests pn (tests n) printLines :: Show a => [a] -> IO () printLines = putStrLn . unlines . map show -- test fixtures -- foo :: Expr -> Expr foo = (value "f" (undefined :: Int -> Int) :$) goo :: Expr -> Expr goo = (value "g" (undefined :: Int -> Int) :$) -- test types -- data Rule = Rule Expr Expr deriving (Show, Eq, Ord) data Equation = Equation Expr Expr deriving (Show, Eq, Ord) newtype RuleSet = RuleSet [(Expr,Expr)] deriving Show newtype EquationSet = EquationSet [(Expr,Expr)] deriving Show newtype Thyght = Thyght { unThyght :: Thy } deriving Show -- Listable instances -- -- beware: enumerating beyond 600 values will make this very slow as it is -- very hard to satisfy canonicalEqn and ->-. In practice, this should not be a -- problem as we enumerate far less than that when enerating 'Thy's. instance Listable Rule where tiers = (`ofWeight` 0) . filterT (\(Rule e1 e2) -> canonicalRule (e1,e2) && e1 ->- e2) . mapT (uncurry Rule . orientRule) . filterT (uncurry (<)) . mapT unSameTypeE $ tiers where (->-) = canReduceTo emptyThy orientRule (e1,e2) | e1 ->- e2 = (e1,e2) | otherwise = (e2,e1) instance Listable Equation where tiers = (`ofWeight` 0) . mapT (uncurry Equation) . filterT (canonicalEqn emptyThy) . mapT orientEqn . filterT (uncurry (<=)) . mapT unSameTypeE $ tiers where orientEqn (e1,e2) | e1 < e2 = (e2,e1) | otherwise = (e1,e2) instance Listable RuleSet where tiers = setCons (RuleSet . map unRule) `ofWeight` 0 where unRule (Rule e1 e2) = (e1,e2) instance Listable EquationSet where tiers = setCons (EquationSet . map unEquation) `ofWeight` 0 where unEquation (Equation e1 e2) = (e1,e2) instance Listable Thy where tiers = concatMapT expandCanReduceTo $ concatMapT expandClosureLimit $ concatMapT expandKeepE $ cons2 (\(RuleSet rs) (EquationSet eqs) -> emptyThy { rules = sort rs , equations = sort eqs }) instance Listable Thyght where tiers = mapT Thyght $ concatMapT expandCanReduceTo $ concatMapT expandClosureLimit $ mapT defaultKeep $ cons2 (\(RuleSet rs) (EquationSet eqs) -> emptyThy { rules = sort rs , equations = sort eqs }) expandKeepE :: Thy -> [[Thy]] expandKeepE thy = cons0 thy \/ cons0 thy {keepE = keepUpToLength (maxLen + 0)} `ofWeight` 1 \/ cons0 thy {keepE = keepUpToLength (maxLen + 1)} `ofWeight` 2 \/ cons0 thy {keepE = keepUpToLength (maxLen + 2)} `ofWeight` 4 \/ cons0 thy {keepE = keepUpToLength (maxLen + 3)} `ofWeight` 6 \/ cons0 thy {keepE = keepUpToLength (maxLen + 4)} `ofWeight` 8 where maxLen = maximum . (0:) . map size . catPairs $ equations thy ++ rules thy expandClosureLimit :: Thy -> [[Thy]] expandClosureLimit thy = cons0 thy {closureLimit = 3} \/ cons0 thy {closureLimit = 0} `ofWeight` 1 \/ cons0 thy {closureLimit = 2} `ofWeight` 2 \/ cons0 thy {closureLimit = 1} `ofWeight` 3 -- TODO: make Listable Thy enumeration complete w.r.t: canReduceTo -- for a complete version, Listable Rule will have to be transformed on a -- higher order function that take canReduceTo. (harder to maintain) expandCanReduceTo :: Thy -> [[Thy]] expandCanReduceTo thy = cons0 thy \/ if all (uncurry (|>|)) (rules thy) then cons0 thy {canReduceTo = (|>|)} `ofWeight` 1 else [] -- FIXME: KBO is broken ATM: -- \/ if all (uncurry ( >|)) (rules thy) -- then cons0 thy {canReduceTo = ( >|)} `ofWeight` 2 -- else [] listThyInefficient :: [Thy] listThyInefficient = concat . concatMapT expandCanReduceTo . concatMapT expandClosureLimit . concatMapT expandKeepE $ cons2 (\(SameTypedPairsE rs) (SameTypedPairsE eqs) -> emptyThy { rules = sort rs , equations = sort eqs }) `suchThat` okThy -- Quick and Dirty! instance Show Thy where show Thy { rules = rs , equations = eqs , canReduceTo = (->-) , closureLimit = cl , keepE = keep } = "Thy { rules = " ++ drop 14 (indent 14 . listLines $ map showEquation rs) ++ " , equations = " ++ drop 18 (indent 18 . listLines $ map showEquation eqs) ++ " , canReduceTo = " ++ showCanReduceTo (->-) ++ "\n" ++ " , closureLimit = " ++ show cl ++ "\n" ++ " , keepE = " ++ showKeepE keep ++ "\n" ++ " }" where showEquation (e1,e2) = showExpr e1 ++ " == " ++ showExpr e2 listLines [] = "[]" listLines ss = '[':(tail . unlines $ map (", " ++) ss) ++ "]" showCanReduceTo (->-) | holds 1000 $ (->-) ==== (|>|) = "(|>|)" | holds 1000 $ (->-) ==== ( >|) = "(>|)" | holds 1000 $ (->-) ==== (|> ) = "(|>)" | otherwise = "(??)" showKeepE keep | holds 1000 $ keep === const True = "const True" | holds 1000 $ keep === keepUpToLength 0 = "keepUpToLength 0" | holds 1000 $ keep === keepUpToLength 1 = "keepUpToLength 1" | holds 1000 $ keep === keepUpToLength 2 = "keepUpToLength 2" | holds 1000 $ keep === keepUpToLength 3 = "keepUpToLength 3" | holds 1000 $ keep === keepUpToLength 4 = "keepUpToLength 4" | holds 1000 $ keep === keepUpToLength 5 = "keepUpToLength 5" | holds 1000 $ keep === keepUpToLength 6 = "keepUpToLength 6" | holds 1000 $ keep === keepUpToLength 7 = "keepUpToLength 7" | holds 1000 $ keep === keepUpToLength 8 = "keepUpToLength 8" | holds 1000 $ keep === keepUpToLength 9 = "keepUpToLength 9" | otherwise = "\\e -> ??" expressionsT :: [Expr] -> [[Expr]] expressionsT ds = [ds] \/ productMaybeWith ($$) es es `addWeight` 1 where es = expressionsT ds -- TODO: maybe use expressionsT as the main function to generate Exprs. -- By using it, I speculate a 20% increase in runtime. But the code will -- certainly be smaller and easier to maintain.