packages feed

mockcat-1.4.0.0: test/Property/OrderProps.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-hpc #-}
module Property.OrderProps
  ( spec
  , 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)
import Data.Maybe (listToMaybe)
import Data.List (nub)
-- import Test.MockCat (shouldBeCalled, inOrderWith, inPartialOrderWith, param)
import Test.MockCat
import Property.Generators
import Control.Monad.IO.Class (liftIO)
import Test.Hspec (Spec, describe, it)

spec :: Spec
spec = do
    describe "Property Order / PartialOrder" $ do
      it "in-order script succeeds" $ property prop_inorder_succeeds
      it "adjacent swap fails order verification" $ property prop_adjacent_swap_fails
      it "subset partial order succeeds" $ property prop_partial_order_subset_succeeds
      it "reversed pair fails partial order" $ property prop_partial_order_reversed_pair_fails

-- | 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
  -- Use withMock for safe verification
  run $ withMock $ do
     -- Expect calls in exact order
     f <- mock (cases [ param a ~> True | a <- xs ])
            `expects` calledInOrder xs
     liftIO $ runScript f scr
  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
      
      -- Verify that expectation fails for swapped order
      e <- run ((try $ withMock $ do
          f <- mock (cases [ param a ~> True | a <- xs ])
                 `expects` calledInOrder swapped
          liftIO $ runScript f (Script xs)
          ) :: 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
  run $ withMock $ do
    f <- mock (cases [ param a ~> True | a <- xs ])
           `expects` calledInSequence subset
    liftIO $ runScript f scr
  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
          let reversed = [xs!!j, xs!!i]
          -- Verify failure
          e <- run ((try $ withMock $ do
              f <- mock (cases [ param a ~> True | a <- xs ])
                     `expects` calledInSequence reversed
              liftIO $ runScript f scr
              ) :: IO (Either SomeException ()))
          
          case e of
            Left _ -> assert True
            Right _ -> assert False