mirror of
https://github.com/reanimate/reanimate.git
synced 2026-09-09 15:12:21 +00:00
166 lines
4.9 KiB
Haskell
166 lines
4.9 KiB
Haskell
{-# 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)
|