packages feed

async-timer-0.2.0.0: test/Spec.hs

module Main where

import           Control.Concurrent
import           Control.Concurrent.Async
import qualified Control.Concurrent.Async.Timer as Timer
import           Control.Exception
import           Control.Monad
import           Criterion.Measurement
import           Data.Function                  ((&))
import           Data.IORef
import           Data.Typeable
import           Test.Tasty
import           Test.Tasty.HUnit

data MyException = MyException
    deriving (Show, Typeable)

instance Exception MyException

main :: IO ()
main = defaultMain (testGroup "Unit Tests" unitTests)

unitTests :: [TestTree]
unitTests =
  [ testGroup "Timer Tests"
    [ testCase "Timer reset" testTimerReset
    , testCase "Simple timer ticks" testSimpleTimerTicks
    ]
  ]

timedAssert :: Int -> IO () -> IO () -> IO ()
timedAssert delay before after = do
  threadDelay $ delay - epsilon
  before
  threadDelay $ 2 * epsilon
  after

  where epsilon = 2 * 10^5

testTimerReset :: IO ()
testTimerReset = do
  let conf = Timer.defaultConf
             & Timer.setInitDelay 1000
             & Timer.setInterval  1000 -- ms
      noOfTicks = 3

  counter <- newIORef 0

  _handle <- async $
    Timer.withAsyncTimer conf $ \ timer ->
    forM_ [1..noOfTicks] $ \ idx -> do
    when (idx == 2) $ do
      threadDelay (5 * 10^5)
      Timer.reset timer
    Timer.wait timer
    modifyIORef counter (+ 1)

  timedAssert (2 * 10^6 + 5 * 10^5)
              ((1 @=?) =<< readIORef counter)
              ((2 @=?) =<< readIORef counter)

testSimpleTimerTicks :: IO ()
testSimpleTimerTicks = do
  let conf = Timer.defaultConf
             & Timer.setInitDelay    0
             & Timer.setInterval  1000 -- ms
      noOfTicks = 3

  counter <- newIORef 0
  times <- newIORef []

  Timer.withAsyncTimer conf $ \ timer ->
    forM_ [1..noOfTicks] $ \_ -> do
      Timer.wait timer
      void $ forkIO $ myAction counter times

  threadDelay (5 * 10^5)
  readIORef counter >>= (noOfTicks @=?)

  ts <- readIORef times
  let deltas = case ts of
                 []         -> []
                 _ : tsTail -> zipWith (-) ts tsTail

      avgDiff = sum (map (subtract 1) deltas) / fromIntegral (length deltas)
  forM_ deltas (\ dt -> putStrLn $ "dt = " ++ show dt)
  putStrLn $ "average dt = " ++ show avgDiff
  return ()

  where myAction :: IORef Int -> IORef [Double] -> IO ()
        myAction counter times = do
          t <- getTime
          n <- readIORef counter
          let n' = n + 1
          writeIORef counter n'
          modifyIORef times (t :)
          putStrLn $ "Tick no. " ++ show n' ++ " (t = " ++ show t ++ ")"
          threadDelay 500000