packages feed

rhine-1.3: test/Clock/Millisecond.hs

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Clock.Millisecond where

-- base
import Control.Monad (when)
import System.Info (os)

-- tasty
import Test.Tasty (testGroup)

-- tasty-hunit
import Test.Tasty.HUnit (Assertion, assertBool, testCase)

-- rhine
import FRP.Rhine
import Util (runRhine)

-- | Milliseconds
newtype MS = MS Int
  deriving (Num, Show, Eq, Ord)

millisecondsSinceInit :: (Monad m) => ClSF m (Millisecond n) a MS
millisecondsSinceInit = sinceInitS >>> arr (MS . round . (* 1000))

tests =
  testGroup
    "Millisecond"
    [ testCase "Outputs milliseconds chronologically" $ do
        output <- runRhine (millisecondsSinceInit @@ (waitClock @1)) $ replicate 5 ()
        assertTiming output $ Just <$> [1, 2, 3, 4, 5]
    , testCase "Schedules chronologically" $ do
        output <- runRhine (millisecondsSinceInit @@ (waitClock @30) >-- collect --> (clId &&& millisecondsSinceInit) @@ (waitClock @50)) $ replicate 5 ()
        assertTiming
          output
          [ Nothing
          , Just ([30], 50)
          , Nothing
          , Nothing
          , Just ([90, 60], 100)
          ]
    ]

assertTiming :: (Show a, TimingSubsumes a) => a -> a -> Assertion
assertTiming observed expected =
  when (os /= "darwin") $
    assertBool ("Observed timing: " ++ show observed ++ "\nExpected timing: " ++ show expected) $
      timingSubsumes observed expected

class TimingSubsumes a where
  timingSubsumes :: a -> a -> Bool

instance TimingSubsumes MS where
  timingSubsumes tObserved tExpected = tExpected <= tObserved && tObserved <= 2 * tExpected + 10

instance (TimingSubsumes a) => TimingSubsumes (Maybe a) where
  timingSubsumes (Just aObserved) (Just aExpected) = timingSubsumes aObserved aExpected
  timingSubsumes Nothing Nothing = True
  timingSubsumes _ _ = False

instance (TimingSubsumes a, TimingSubsumes b) => TimingSubsumes (a, b) where
  timingSubsumes (aObserved, bObserved) (aExpected, bExpected) = timingSubsumes aObserved aExpected && timingSubsumes bObserved bExpected

instance (TimingSubsumes a) => TimingSubsumes [a] where
  timingSubsumes [] [] = True
  timingSubsumes (aObserved : aObserveds) (aExpected : aExpecteds) = timingSubsumes aObserved aExpected && timingSubsumes aObserveds aExpecteds
  timingSubsumes _ _ = False