packages feed

twee-2.7.1: test/Index.hs

-- Tests for the term index.

{-# LANGUAGE TupleSections #-}
module Index(tests) where

import Common
import Twee.Base
import Twee.Index(Index)
import qualified Twee.Index as Index
import Data.Hashable
import Data.List
import Data.Maybe
import Data.Typeable
import Test.Tasty
import Test.Tasty.QuickCheck
import Data.Binary.Sharing

newtype IndexOps f = IndexOps [IndexOp f] deriving Show
data IndexOp f = Add (Term f) | Delete (Term f) deriving Show

instance (Hashable f, Eq f, Typeable f, Arbitrary f, Arity f) => Arbitrary (IndexOps f) where
  arbitrary =
    sized $ \n -> IndexOps <$> take n <$> arbOps []
    where
      arbOps ts =
        frequency $
          [(2, do { t <- arbitrary; ops <- arbOps (t:ts); return (Add t:ops) })] ++
          [(1, do { t <- elements ts; ops <- arbOps (delete t ts); return (Delete t:ops) }) | not (null ts)]
  shrink (IndexOps ops) =
    IndexOps <$> shrinkList shr ops
    where
      shr (Add t) = Add <$> shrink t
      shr (Delete t) = Delete <$> shrink t

prop_index_insert :: [Term Func] -> Term Func -> Property
prop_index_insert ts u =
  counterexample (show ts') $
  counterexample (show idx) $
  sort (catMaybes [fmap (,t) (match t u) | t <- ts']) ===
  sort (Index.matches u idx)
  where
    idx = foldr (\t -> Index.insert t t) Index.empty ts
    ts' = map canonicalise ts

prop_index_invariant :: IndexOps Func -> Property
prop_index_invariant (IndexOps ops) =
  flip (foldr (counterexample . show)) idxs $
  property $ Index.invariant (last idxs)
  where
    idxs = scanl (\idx op -> applyIndex op idx) Index.empty ops
    applyIndex (Add t) = Index.insert t t
    applyIndex (Delete t) = Index.delete t t

prop_index_serialise :: [Term Func] -> Property
prop_index_serialise ts =
  counterexample (show idx) $
  counterexample (show serial) $
  counterexample (show idx') $
  Index.elems idx === Index.elems idx' .&&.
  serial === encode idx'
  where
    idx, idx' :: Index Func (Term Func)
    idx = foldr (\t -> Index.insert t t) Index.empty ts
    serial = encode idx
    idx' = decode serial

tests :: TestTree
tests =
  localOption (QuickCheckTests 100000) $
  testGroup "Term indexing"
    [testProperty "Invariant holds" prop_index_invariant,
     testProperty "Inserted terms are found" prop_index_insert,
     testProperty "Serialisation round trip" prop_index_serialise]