allen-0.1.0.0: test/Spec.hs
{-# LANGUAGE ViewPatterns #-}
import Test.QuickCheck
import Control.Monad (unless)
import Data.Allen
import Data.Bits
import qualified Data.Map as Map
-- To Avoid orphaned instance warning
newtype ValidRelation = ValidRelation { toRelation :: Relation }
newtype ValidInterval = ValidInterval { toInterval :: Interval }
instance Arbitrary ValidRelation where
arbitrary = do
index <- arbitrary
let n = fromEnum (maxBound :: Relation)
return $ ValidRelation $ toEnum $ index `mod` n
instance Arbitrary ValidInterval where
arbitrary = do
iD <- abs <$> arbitrary
return $ ValidInterval $ Interval iD Map.empty
instance Show ValidRelation where
show = show . toRelation
instance Show ValidInterval where
show = show . toInterval
main :: IO ()
main = do
putStrLn "Testing Relations...\n"
test prop_relationBits
test prop_relationInverse
test prop_relationBitAmount
test prop_relationSet
test prop_relationTestSet
putStrLn "\nTesting Intervals...\n"
test prop_intervalAssume
testAllen prop_intervalAllBitsDefault
putStrLn "\nTests from Allen (1983)...\n"
testAllen prop_Section4Subsection2Part1
testAllen prop_Section4Subsection2Part2
testAllen prop_Section4Subsection2Part3
-- Throw error on test failure so that
-- Spec.hs can properly recognize that a test has failed.
test :: Testable prop => prop -> IO ()
test func = do
result <- quickCheckWithResult stdArgs func
unless (isSuccess result) $ error "Test Failed!"
-- Similar to the `test` function but for Allen calculations
-- Upon failure: throws error and prints results of the calculation
testAllen :: Allen Bool -> IO ()
testAllen calc = do
let (successful, result) = runAllen calc
if successful then
putStrLn "+++ OK, passed 1 test."
else do
putStrLn "--- [ Test Failed! ] ---"
putStrLn "Result: "
putStrLn "------------------------"
mapM_ (print . snd) $ Map.toList result
putStrLn "------------------------"
errorWithoutStackTrace "Test Failure."
prop_relationBits :: ValidRelation -> Bool
prop_relationBits (toRelation -> r) = r == head (fromBits $ toBits r)
prop_relationInverse :: ValidRelation -> Bool
prop_relationInverse (toRelation -> toBits -> r) = r == doubleConverse r
where doubleConverse = converse . converse
prop_relationBitAmount :: Bool
prop_relationBitAmount = and $ zipWith (==) twos relations
where twos = map bit [0.. fromEnum (maxBound :: Relation)]
relations = map toBits allRelations
prop_relationSet :: ValidInterval -> ValidRelation -> ValidRelation -> IntervalID -> Bool
prop_relationSet (toInterval -> i) (toRelation -> toBits -> r1) (toRelation -> toBits -> r2) iD = r == r2
where i' = setRelation i r1 iD
i'' = setRelation i' r2 iD
r = intervalRelations i'' Map.! iD
prop_relationTestSet :: [ValidRelation] -> Bool
prop_relationTestSet (map toRelation -> r) = evalAllen calc
where calc :: Allen Bool
calc = do
a <- interval
b <- interval
assumeBits a allRelationBits b
testRelationSet r a b
prop_intervalAssume :: ValidRelation -> Bool
prop_intervalAssume (toRelation -> r) = evalAllen calc
where calc :: Allen Bool
calc = do
a <- interval
b <- interval
assume a r b
r1 <- getConstraints a b
r2 <- getConstraints b a
return $ r1 == converse r2
prop_intervalAllBitsDefault :: Allen Bool
prop_intervalAllBitsDefault = do
a <- interval
b <- interval
c1 <- getConstraints a b
c2 <- getConstraints b a
return (c1 == c2 && c1 == allRelationBits)
--
-- Examples from Section 4.2 of the Allen (1983) paper.
--
networkSection4Subsection2 :: Allen (IntervalID, IntervalID, IntervalID)
networkSection4Subsection2 = do
r <- interval
s <- interval
l <- interval
assumeSet s [Precedes, Meets, MetBy, PrecededBy] r
assumeSet s [Overlaps, Meets] l
return (r, s, l)
-- First inference from Section 4.2
prop_Section4Subsection2Part1 :: Allen Bool
prop_Section4Subsection2Part1 = do
-- Set up basic network.
-- s is discarded since it is not used
(r, _, l) <- networkSection4Subsection2
lrInferredBits <- getConstraints l r
-- expected [Precedes, PrecededBy, Overlaps, Meets, Contains, Starts, StartedBy, FinishedBy, Equals]
let lrExpectedBits = bitsFromString "pPomDsSFe"
return (lrInferredBits == lrExpectedBits)
-- Additional inference from Section 4.2.
-- Assumes part 1 inference works.
prop_Section4Subsection2Part2 :: Allen Bool
prop_Section4Subsection2Part2 = do
-- Set up same network as Part 1, but with added l->r relations.
(r, s, l) <- networkSection4Subsection2
assumeSet l [Overlaps, Starts, During] r
lrInferredBits <- getConstraints l r
srInferredBits <- getConstraints s r
-- lrExpected [Overlaps, Starts]
-- srExpected [Precedes, Meets]
let lrExpectedBits = bitsFromString "os"
srExpectedBits = bitsFromString "pm"
return (lrInferredBits == lrExpectedBits
&& srInferredBits == srExpectedBits)
-- Final inference from Section 4.2.
-- Assumes inferences from parts 1 and 2 works.
prop_Section4Subsection2Part3 :: Allen Bool
prop_Section4Subsection2Part3 = do
-- Set up same network as Part 2.
(r, s, l) <- networkSection4Subsection2
assumeSet l [Overlaps, Starts, During] r
-- Add new interval D
-- w/ D -[During]-> S
d <- interval
let dsRelationBits = bitsFromString "d"
assumeBits d dsRelationBits s
drInferredBits <- getConstraints d r
dlInferredBits <- getConstraints d l
-- drExpected [Precedes]
-- dlExpected [Precedes, Overlaps, Meets, During, Starts]
let drExpectedBits = bitsFromString "p"
dlExpectedBits = bitsFromString "pomds"
return (drInferredBits == drExpectedBits
&& dlInferredBits == dlExpectedBits)