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 ()