mockcat-0.6.0.0: test/Property/OrderProps.hs
{-# LANGUAGE ScopedTypeVariables #-}
module Property.OrderProps
( prop_inorder_succeeds
, prop_adjacent_swap_fails
, prop_partial_order_subset_succeeds
, prop_partial_order_reversed_pair_fails
) where
import Test.QuickCheck
import Test.QuickCheck.Monadic (monadicIO, run, assert)
import Control.Exception (try, SomeException, evaluate)
import Data.Maybe (listToMaybe)
import Data.List (nub)
import Test.MockCat (shouldApplyInOrder, shouldApplyInPartialOrder, stubFn)
import Property.Generators
import Test.MockCat (param)
-- | Property: executing a non-empty script yields exact order success.
prop_inorder_succeeds :: Property
prop_inorder_succeeds = forAll scriptGen $ \scr@(Script xs) -> not (null xs) ==> monadicIO $ do
m <- run $ buildUnaryMock scr
let f = stubFn m
run $ runScript f scr
run $ m `shouldApplyInOrder` (param <$> xs)
assert True
-- | Property: a single adjacent swap causes order verification failure.
prop_adjacent_swap_fails :: Property
prop_adjacent_swap_fails = forAll scriptGen $ \(Script xs) -> length xs >= 2 ==> monadicIO $ do
let distinct = nub xs
if length distinct /= length xs
then assert True -- discard scripts with duplicates; they can mask order errors
else do
i <- run $ generate $ chooseInt (0, length xs - 2)
let swapped = take i xs ++ [xs !! (i+1), xs !! i] ++ drop (i+2) xs
m <- run $ buildUnaryMock (Script xs)
let f = stubFn m
run $ runScript f (Script xs)
e <- run $ (try (m `shouldApplyInOrder` (param <$> swapped)) :: IO (Either SomeException ()))
case e of
Left _ -> assert True
Right _ -> assert False
-- | Helper: produce a non-empty in-order subsequence.
chooseSubsequence :: [a] -> Gen [a]
chooseSubsequence [] = pure []
chooseSubsequence xs = do
bools <- vectorOf (length xs) arbitrary
let picked = [ x | (x,b) <- zip xs bools, b ]
if null picked then chooseSubsequence xs else pure picked
-- | Property: any non-empty subsequence (order-preserving) passes partial order check.
prop_partial_order_subset_succeeds :: Property
prop_partial_order_subset_succeeds = forAll scriptGen $ \scr@(Script xs) -> not (null xs) ==> monadicIO $ do
subset <- run $ generate $ chooseSubsequence xs
m <- run $ buildUnaryMock scr
let f = stubFn m
run $ runScript f scr
run $ m `shouldApplyInPartialOrder` (param <$> subset)
assert True
-- | Property: selecting two distinct values and reversing them causes partial order failure.
prop_partial_order_reversed_pair_fails :: Property
prop_partial_order_reversed_pair_fails = forAll scriptGen $ \scr@(Script xs) -> length xs >= 2 ==> monadicIO $ do
if length (nub xs) /= length xs
then assert True -- discard non-unique scripts to avoid accidental subsequences
else do
let pairs = [ (i,j) | i <- [0..length xs -2], j <- [i+1..length xs -1] ]
case listToMaybe pairs of
Nothing -> assert True
Just (i,j) -> do
m <- run $ buildUnaryMock scr
let f = stubFn m
run $ runScript f scr
let reversed = [xs!!j, xs!!i]
e <- run $ (try (m `shouldApplyInPartialOrder` (param <$> reversed)) :: IO (Either SomeException ()))
case e of
Left _ -> assert True
Right _ -> assert False