rest-rewrite-0.1: test/QuickCheckTests.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ImplicitParams #-}
{-# LANGUAGE OverloadedStrings #-}
module QuickCheckTests where
import Test.QuickCheck
import Test.QuickCheck.Monadic
import Control.Monad.Identity
import Data.Maybe as Mb
import qualified Data.Text as T
import Debug.Trace (trace)
import Language.REST.Core hiding (syms)
import qualified Language.REST.PartialOrder as PO
import qualified Language.REST.WQO as WQO
import qualified Language.REST.OrderingConstraints as OC
import qualified Language.REST.OrderingConstraints.Strict as SC
import qualified Language.REST.OrderingConstraints.Lazy as LC
import Language.REST.RPO
import Nat
import Language.REST.Op
import Language.REST.Types
import Language.REST.RuntimeTerm
import Prelude hiding (EQ, GT)
import Text.Printf
type WQO = WQO.WQO
syms = [
("f", 3)
, ("g", 2)
, ("h", 1)
, ("s", 1)
, ("z", 0)
, ("+", 2)
, ("cons", 2)
, ("nil", 0)
]
gen_op :: Gen Op
gen_op =
oneof (map (return . Op . fst) syms)
gen_po :: Gen (PO.PartialOrder Op)
gen_po =
do
num_ops <- choose (0, 10)
go PO.empty num_ops
where
go :: PO.PartialOrder Op -> Int -> Gen (PO.PartialOrder Op)
go po 0 = return po
go po n = do
f <- gen_op
g <- gen_op
let po' = fromMaybe po $ PO.insert po f g
go po' (n - 1)
gen_wqo_steps :: Gen ([(Op, Op, WQO.QORelation)])
gen_wqo_steps =
do
numOps <- choose (0, 10)
vectorOf numOps go
where
go = do
f <- gen_op
g <- gen_op
r <- arbitrary
return (f, g, r)
toWQO :: [(Op, Op, WQO.QORelation)] -> WQO Op
toWQO = go WQO.empty where
go wqo [] = wqo
go wqo ((f, g, r):xs) =
let
wqo' = fromMaybe wqo $ WQO.insertMaybe wqo (f, g, r)
in
go wqo' xs
gen_wqo :: Gen (WQO Op)
gen_wqo = fmap toWQO gen_wqo_steps
gen_term :: Gen RuntimeTerm
gen_term = sized go
where
go :: Int -> Gen RuntimeTerm
go sz = do
(op, arity) <- oneof $ map return $ (filter ((<= sz) . snd) syms)
args <- vectorOf arity (go (sz `div` (arity + 1)))
return $ App (Op op) args
instance Arbitrary WQO.QORelation where
arbitrary = oneof [return WQO.QGT, return WQO.QEQ]
instance Arbitrary Op where
arbitrary = gen_op
instance Arbitrary RuntimeTerm where
arbitrary = gen_term
instance Arbitrary (PO.PartialOrder Op) where
arbitrary = gen_po
instance Arbitrary (WQO Op) where
arbitrary = gen_wqo
prop_poTrans f g h po =
PO.gt po f g && PO.gt po g h ==> PO.gt po f h
where
types = f::Op
prop_wqoTrans f g h wqo = f `gte` g && g `gte` h ==> f `gte` h
where
gte f g = Mb.isJust $ WQO.getRelation wqo f g
types = f::Op
prop_rpoTrans t u v wqo = synGTE wqo t u && synGTE wqo u v ==> synGTE wqo t v
prop_rpoCons impl t u = monadicIO $ do
isSat <- run $ OC.isSatisfiable impl constraints
pre isSat
assert $ synGTE ordering t u
where
constraints = rpoGTE t u
ordering = Mb.fromJust (OC.getOrdering impl constraints)
prop_permits steps = SC.permits (SC.noConstraints) (toWQO steps)
-- Should fail
-- If this prop was true, we'd only ever need to check each term once
-- Generated counterexample:
-- f(s(z), h(nil), nil) -> cons(s(z), z) -> h(nil + z) \-> s(nil) + h(nil)
-- f(s(z), h(nil), nil) -> s(nil) + h(nil) -> h(nil + z) -> s(nil) + h(nil)
-- prop_rpot2 t0 t1 t2 t3 u1 = monadicIO $ do
-- pre <$> run (canOrient [t0, t1, t2])
-- pre <$> run (canOrient [t0, u1, t2, t3])
-- assert <$> run (canOrient [t0, t1, t2, t3])
-- where
-- types = t0::RuntimeTerm
tests = sequence
[
-- quickCheckWith stdArgs{maxDiscardRatio = 1000} prop_rpot2
quickCheckWith stdArgs prop_permits
, quickCheckWith stdArgs{maxDiscardRatio = 1000} prop_poTrans
, quickCheckWith stdArgs{maxDiscardRatio = 100} prop_wqoTrans
, putStrLn "Transitivity of RPO:"
, quickCheckWith stdArgs{maxDiscardRatio = 100} prop_rpoTrans
, putStrLn "RPO aligns with concrete:"
, quickCheckWith stdArgs{maxSize = 10, maxSuccess = 20, maxDiscardRatio = 100} (prop_rpoCons ?impl)
]
where
?impl = LC.lazyOC