packages feed

delay-0: tests/test.hs

module Main where

import qualified Control.Exception as E
import Control.Concurrent.Async
import Control.Concurrent.MVar
import Control.Monad
import Control.Time
import Data.Fixed
import Data.Int
import Numeric.Units.Dimensional ((*~))
import qualified Numeric.Units.Dimensional as D
import qualified Numeric.Units.Dimensional.SIUnits as D
import Data.Time
import Data.Word
import Numeric.Natural

assert :: Bool -> String -> IO ()
assert True _ = return ()
assert False err = E.throwIO . E.AssertionFailed $ err

-- Delay an amount, and see what time it is when we're done.
delayTest :: AsMicro w => w -> IO UTCTime
delayTest w = delay w >> getCurrentTime

microInSecond :: Integer
microInSecond = 10^(6::Int)

main :: IO ()
main = do
   assert (toMicro (1::Uni) == microInSecond) "Uni conversion"
   assert (toMicro (1::Deci) == microInSecond) "Deci conversion"
   assert (toMicro (1::Centi) == microInSecond) "Centi conversion"
   assert (toMicro (1::Milli) == microInSecond) "Milli conversion"
   assert (toMicro (1::Micro) == microInSecond) "Micro conversion"
   assert (toMicro (1::Nano) == microInSecond) "Nano conversion"
   assert (toMicro (1::Pico) == microInSecond) "Pico conversion"
   assert (toMicro ((1::Double) *~ D.second) == microInSecond) "Second conversion"
   assert (toMicro ((1/60::Float) *~ D.minute) == microInSecond) "Minute conversion"
   assert (toMicro ((1/3600::Double) *~ D.hour) == microInSecond) "Hour conversion"
   assert (toMicro ((1::Double) *~ D.day) == 24*60*60*microInSecond) "Day conversion"
   assert (toMicro (((1::Double) *~ D.minute) D.+ (1 *~ D.second)) == 61*microInSecond) "Dim math conversion"

   testDelays secondTests 1 0.1
   testDelays halfSecondTests 0.5 0.1

   Just True <- timeout (0.5::DiffTime) (delay (0.25::DiffTime) >> return True)
   Nothing <- timeout (0.5::DiffTime) (delay (0.75::DiffTime) >> return True)
   n <- getCurrentTime
   Just True <- timeoutAt (addUTCTime 0.5 n) (delay (0.25::DiffTime) >> return True)
   Nothing <- timeoutAt (addUTCTime 0.5 n) (delay (0.75::DiffTime) >> return True)

   -- Make sure we're not subject to https://ghc.haskell.org/trac/ghc/ticket/7719
   Just True <- join <$> (timeout (0.5::DiffTime) . timeout (0.25::DiffTime) $
               (delay (0.15::DiffTime) >> return True))

   Just Nothing <- timeout (0.5::DiffTime) . timeout (0.25::DiffTime) $
               (delay (0.75::DiffTime) >> return True)

   Nothing <- timeout (0.25::DiffTime) . timeout (0.5::DiffTime) $
               (delay (0.75::DiffTime) >> return True)

   void $ mapConcurrently id
       [ testCBAfter
       , testCBAt
       , testCBAfterUpAfter
       , testCBAfterUpAt
       , testCBAtUpAfter
       , testCBAtUpAt
       , testCBCancelAfter
       , testCBCancelAt
       , testCBCancelCancel
       , testCBCancelExecutedAfter
       , testCBCancelExecutedAt
       , testCBUpdateAfterCanceled
       , testCBUpdateAtCanceled
       ]

   return ()
 where
  testDelays :: [IO UTCTime] -> NominalDiffTime -> NominalDiffTime -> IO ()
  testDelays ds l v = do
    st <- getCurrentTime
    ets <- mapConcurrently id ds
    let ots = map (`diffUTCTime` st) ets
    assert (minimum ots >= l) "Returned before delay time passed!"
    assert (maximum ots - minimum ots <= v) "Too much variance."
  secondTests =
      [ delayTest (1::Int)
      , delayTest (1::Int8)
      , delayTest (1::Int16)
      , delayTest (1::Int32)
      , delayTest (1::Int64)
      , delayTest (1::Integer)
      , delayTest (1::Word)
      , delayTest (1::Word8)
      , delayTest (1::Word16)
      , delayTest (1::Word32)
      , delayTest (1::Word64)
      , delayTest (1::Natural)
      , delayTest (1::Float)
      , delayTest (1::Double)
      , delayTest (1::DiffTime)
      , delayTest (1::Uni)
      , delayTest (1::Deci)
      , delayTest (1::Centi)
      , delayTest (1::Milli)
      , delayTest (1::Micro)
      , delayTest (1::Nano)
      , delayTest (1::Pico)
      , delayTest $ (1::Float) *~ D.second
      , delayTest $ (1::Double) *~ D.second
      , delayTest $ (1/60::Double) *~ D.minute
      , delayTest $ (1/60/60::Double) *~ D.hour
      , delayTest $ (1/60/60/24::Double) *~ D.day
      ]
  halfSecondTests =
      [ delayTest (0.5::Float)
      , delayTest (0.5::Double)
      , delayTest (0.5::DiffTime)
      , delayTest (0.5::Deci)
      , delayTest (0.5::Centi)
      , delayTest (0.5::Milli)
      , delayTest (0.5::Micro)
      , delayTest (0.5::Nano)
      , delayTest (0.5::Pico)
      , delayTest $ (0.5::Float) *~ D.second
      , delayTest $ (0.5::Double) *~ D.second
      , delayTest $ (0.5/60::Double) *~ D.minute
      , delayTest $ (0.5/60/60::Double) *~ D.hour
      , delayTest $ (0.5/60/60/24::Double) *~ D.day
      ]

  writeMVar :: MVar a -> a -> IO ()
  writeMVar m v = void $ swapMVar m v

  testCBAfter = do
    cbm <- newMVar False
    void $ callbackAfter (0.5::DiffTime) (writeMVar cbm True)
    delay (0.4::DiffTime)
    False <- readMVar cbm
    delay (0.2::DiffTime)
    True <- readMVar cbm
    return ()

  testCBAt = do
    cbm <- newMVar False
    n <- getCurrentTime
    void $ callbackAt (addUTCTime 0.5 n) (writeMVar cbm True)
    delay (0.4::DiffTime)
    False <- readMVar cbm
    delay (0.2::DiffTime)
    True <- readMVar cbm
    return ()

  testCBAfterUpAfter = do
    cbm <- newMVar False
    ck <- callbackAfter (0.5::DiffTime) (writeMVar cbm True)
    updateCallbackToAfter ck (1::DiffTime)
    delay (0.75::DiffTime)
    False <- readMVar cbm
    delay (0.5::DiffTime)
    True <- readMVar cbm
    return ()

  testCBAfterUpAt = do
    cbm <- newMVar False
    n <- getCurrentTime
    ck <- callbackAfter (0.5::DiffTime) (writeMVar cbm True)
    updateCallbackTo ck (addUTCTime 1.0 n)
    delay (0.75::DiffTime)
    False <- readMVar cbm
    delay (0.5::DiffTime)
    True <- readMVar cbm
    return ()

  testCBAtUpAfter = do
    cbm <- newMVar False
    n <- getCurrentTime
    ck <- callbackAt (addUTCTime 0.5 n) (writeMVar cbm True)
    updateCallbackToAfter ck (1::DiffTime)
    delay (0.75::DiffTime)
    False <- readMVar cbm
    delay (0.5::DiffTime)
    True <- readMVar cbm
    return ()

  testCBAtUpAt = do
    cbm <- newMVar False
    n <- getCurrentTime
    ck <- callbackAt (addUTCTime 0.5 n) (writeMVar cbm True)
    updateCallbackTo ck (addUTCTime 1.0 n)
    delay (0.75::DiffTime)
    False <- readMVar cbm
    delay (0.5::DiffTime)
    True <- readMVar cbm
    return ()

  testCBCancelAfter = do
    cbm <- newMVar False
    ck <- callbackAfter (0.5::DiffTime) (writeMVar cbm True)
    cancelCallback ck
    delay (0.75::DiffTime)
    False <- readMVar cbm
    return ()

  testCBCancelAt = do
    cbm <- newMVar False
    n <- getCurrentTime
    ck <- callbackAt (addUTCTime 0.5 n) (writeMVar cbm True)
    cancelCallback ck
    delay (0.75::DiffTime)
    False <- readMVar cbm
    return ()

  testCBCancelCancel = do
    cbm <- newMVar False
    n <- getCurrentTime
    ck <- callbackAt (addUTCTime 0.5 n) (writeMVar cbm True)
    cancelCallback ck
    delay (0.75::DiffTime)
    cancelCallback ck
    False <- readMVar cbm
    return ()

  testCBUpdateAfterCanceled = do
    cbm <- newMVar False
    n <- getCurrentTime
    ck <- callbackAt (addUTCTime 0.5 n) (writeMVar cbm True)
    cancelCallback ck
    updateCallbackToAfter ck (0.6::DiffTime)
    delay (0.75::DiffTime)
    cancelCallback ck
    False <- readMVar cbm
    return ()

  testCBUpdateAtCanceled = do
    cbm <- newMVar False
    n <- getCurrentTime
    ck <- callbackAt (addUTCTime 0.5 n) (writeMVar cbm True)
    cancelCallback ck
    updateCallbackTo ck (addUTCTime 0.6 n)
    delay (0.75::DiffTime)
    cancelCallback ck
    False <- readMVar cbm
    return ()

  testCBCancelExecutedAfter = do
    cbm <- newMVar False
    ck <- callbackAfter (0.5::DiffTime) (writeMVar cbm True)
    delay (0.75::DiffTime)
    cancelCallback ck
    True <- readMVar cbm
    return ()

  testCBCancelExecutedAt = do
    cbm <- newMVar False
    n <- getCurrentTime
    ck <- callbackAt (addUTCTime 0.5 n) (writeMVar cbm True)
    delay (0.75::DiffTime)
    cancelCallback ck
    True <- readMVar cbm
    return ()