reanimate-0.5.0.0: test/TimeVarTests.hs
{-# LANGUAGE RankNTypes #-}
module TimeVarTests
( timeVarTests
) where
import Control.Exception
import Control.Monad.ST.Unsafe
import Data.IORef
import System.IO.Unsafe
import Test.Tasty
import Test.Tasty.ExpectedFailure
import Test.Tasty.HUnit
import Reanimate
import Reanimate.Scene
timeVarTests :: TestTree
timeVarTests =
testGroup "Time variables"
[ expectFail $ tc "Error check1" $ do
v <- newVar (0::Double)
checkAt v
[ (0,0), (0.25, 0.25), (0.5, 0.5), (1,0) ]
, expectFail $ tc "Error check2" $ do
io $ True @?= False
, tc "Default value" $ do
v <- newVar True
checkAt v
[ (-1, True), (0, True), (1, True) ]
, tc "Modified value" $ do
v <- newVar False
writeVar v True
checkAt v
[(-1, False), (0, True), (1, True)]
, tc "Modified value" $ do
v <- newVar False
writeVar v True
writeVar v False
checkAt v
[(-1, False), (0, False), (1, False)]
, tc "Overwrite future" $ do
v <- newVar "def"
fork $ do
wait 1
writeVar v "written in the future"
writeVar v "overwrite"
checkAt v
[ (-1, "def"), (0, "overwrite"), (1, "overwrite") ]
, tc "Interrupt tween" $ do
v <- newVar 0
fork $ tweenVar v 1 $ \prev t -> fromToS prev 1 t
checkAt v
[ (0,0), (0.25, 0.25), (0.5, 0.5), (1,1) ]
fork $ do
wait 0.5
writeVar v 0.5
checkAt v
[ (0,0), (0.25, 0.25), (0.5, 0.5), (1,0.5) ]
, tc "Double tween" $ do
v <- newVar 0
fork $ tweenVar v 1 $ \prev t -> fromToS prev 1 t
fork $ tweenVar v 1 $ \prev t -> fromToS prev 0 t
checkAt v
[ (0,0), (0.25, 0.25*0.75), (0.5, 0.5*0.5), (1,1*0) ]
, expectFail $ tc "Negative tween duration" $ do
v <- newVar 0
tweenVar v (-1) $ \prev t -> fromToS prev 1 t
, tc "Tween 1" $ do
v <- newVar 0
wait 1
tweenVar v 1 $ \prev t -> fromToS prev 1 t
checkAt v
[ (0,0), (1,0), (1.5,0.5), (2,1), (3,1) ]
, tc "Tween 2" $ do
v <- newVar (0::Int)
wait 1
tweenVar v 1 $ \_prev _t -> 1
checkAt v
[ (0,0), (1,1), (1.5,1), (2,1), (3,1) ]
, tc "Tween 3" $ do
v <- newVar (0::Int)
wait 1
tweenVar v 0 $ \_prev _t -> 1
checkAt v
[ (0,0), (1,1), (2,1)]
, tc "Tween 4" $ do
v <- newVar (0::Double)
tweenVar v 1 $ \prev t -> fromToS prev 1 t
tweenVar v 1 $ \prev t -> fromToS prev 0 t
checkAt v
[ (0,0), (0.5,0.5), (1,1), (1.5,0.5), (2,0)]
, tc "Tween 5" $ do
v <- newVar (0::Double)
tweenVar v 1 $ \prev t -> fromToS prev 1 t
tweenVar v 1 $ \prev t -> fromToS prev 0 t
wait (-1)
writeVar v 2
checkAt v
[ (0,0), (0.5,0.5), (1,2), (1.5,2), (2,2)]
, tc "Tween zero duration" $ do
v <- newVar 0
wait 1
tweenVar v 0 $ \_prev t -> t
checkAt v
[ (0,0), (1,1), (2,1)]
, tc "Performance/writeVar" $ do
v <- newVar (0::Int)
ref <- io $ newIORef (0::Int)
modifyVar v $ \old -> unsafePerformIO (modifyIORef ref (+1) >> return 1) + old
at0 <- readVar v
wait 1
at1 <- readVar v
io $ at0+at1 @?= 2
counter <- io $ readIORef ref
io $ counter @?= 1
, tc "Performance/tweenVar" $ do
v <- newVar (0::Double)
ref <- io $ newIORef (0::Int)
let expensive prev t = unsafePerformIO $ do
modifyIORef ref (+1)
evaluate (fromToS prev 1 t)
tweenVar v 1 expensive
-- Reading at 0.5 and 1 should call the expensive function.
-- Reading at 2 and 3 should reuse the result from 1.
-- The count should therefore be 2 and not 4.
checkAt v
[ (0.5,0.5), (1,1), (2,1), (3,1) ]
counter <- io $ readIORef ref
io $ counter @?= 2
, tc "Performance/tweenVar 2" $ do
v <- newVar (0::Double)
fork $ wait 10 >> writeVar v 1
ref <- io $ newIORef (0::Int)
let expensive prev t = unsafePerformIO $ do
modifyIORef ref (+1)
evaluate (fromToS prev 1 t)
tweenVar v 1 expensive
wait 2
writeVar v 2
-- Reading at 0.5 and 1 should call the expensive function.
-- Reading at 2 should reuse the result from 1.
-- The count should therefore be 2 and not 4.
checkAt v
[ (0.5,0.5), (1,1), (2,1), (3,2), (4,2) ]
counter <- io $ readIORef ref
io $ counter @?= 2
]
checkAt :: (Eq a, Show a) => Var s a -> [(Time, a)] -> Scene s ()
checkAt _ [] = pure ()
checkAt v ((d,expected):xs) = do
fork $ do
now <- queryNow
wait (d-now)
val <- readVar v
io $ val @?= expected
checkAt v xs
tc :: TestName -> (forall s. Scene s ()) -> TestTree
tc name action = testCase name $
(evalScene action @?= ())
`catch` \(ErrorCall err) -> assertFailure err
io :: IO a -> Scene s a
io action = liftST (unsafeIOToST action)