timeutils-0.1.0: test/Spec.hs
{-
timeutils
Copyright (C) 2019 Jonathan Lamothe
<jlamothe1980@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-}
import Test.Hspec (Spec, context, describe, hspec, it, shouldBe)
import Data.Time.Clock
( NominalDiffTime
, UTCTime
, addUTCTime
, getCurrentTime
)
import Data.Time.Utils
main :: IO ()
main = hspec $ do
timeElapsedAtSpec
startTimerAtSpec
stopTimerAtSpec
decomposeNDTSpec
composeNDTSpec
timerIsRunningSpec
timerIsStartedSpec
timeRemainingAtSpec
countdownIsCompletedAtSpec
countdownIsRunningSpec
countdownIsStartedSpec
startCountdownSpec
stopCountdownSpec
currentLapAtSpec
allLapsAtSpec
totalStopwatchTimeAtSpec
stopwatchIsRunningSpec
stopwatchIsStartedSpec
startStopwatchSpec
stopStopwatchSpec
newLapAtSpec
humanNDTSpec
timeElapsedAtSpec :: Spec
timeElapsedAtSpec = describe "timeElapsedAt" $ do
context "newTimer" $
it "should be 0" $ do
t <- getCurrentTime
timeElapsedAt t newTimer `shouldBe` 0
context "started, no previous" $
it "should be 1 minute" $ do
(t, t') <- times 60
let timer = newTimer { timerStartTime = Just t }
timeElapsedAt t' timer `shouldBe` 60
context "stopped, with previous" $
it "should be 1 minute" $ do
t <- getCurrentTime
let timer = newTimer { timerOffset = 60 }
timeElapsedAt t timer `shouldBe` 60
context "started, with previous" $
it "should be 2 minutes" $ do
(t, t') <- times 60
let
timer = newTimer
{ timerOffset = 60
, timerStartTime = Just t
}
timeElapsedAt t' timer `shouldBe` 120
startTimerAtSpec :: Spec
startTimerAtSpec = describe "startTimerAt" $ do
context "newTimer" $
it "should set the start time" $ do
t <- getCurrentTime
let expected = newTimer { timerStartTime = Just t }
startTimerAt t newTimer `shouldBe` expected
context "already started" $
it "should not modify the timer" $ do
(t, t') <- times 60
let timer = newTimer { timerStartTime = Just t }
startTimerAt t' timer `shouldBe` timer
context "stopped" $
it "should resume the timer" $ do
t <- getCurrentTime
let
timer = newTimer { timerOffset = 60 }
expected = timer { timerStartTime = Just t }
startTimerAt t timer `shouldBe` expected
stopTimerAtSpec :: Spec
stopTimerAtSpec = describe "stopTimerAt" $ do
context "newTimer" $
it "should not change the timer" $ do
t <- getCurrentTime
stopTimerAt t newTimer `shouldBe` newTimer
context "stopped timer" $
it "should not change the timer" $ do
t <- getCurrentTime
let timer = newTimer { timerOffset = 60 }
stopTimerAt t timer `shouldBe` timer
context "running timer" $
it "should stop the timer, and calculate the new offset" $ do
(t, t') <- times 60
let
timer = newTimer
{ timerOffset = 60
, timerStartTime = Just t
}
expected = newTimer { timerOffset = 120 }
stopTimerAt t' timer `shouldBe` expected
decomposeNDTSpec :: Spec
decomposeNDTSpec = describe "decomposeNDT" $ do
let
days = 1
hours = days * 24 + 2
minutes = hours * 60 + 3
seconds = minutes * 60 + 4
millis = seconds * 1000 + 5
time = millis / 1000
context "zero" $
it "should be zero" $
decomposeNDT 0 `shouldBe` newTimeParts
context "positive" $
it "should calculate the time parts" $ let
expected = TimeParts
{ tpDays = 1
, tpHours = 2
, tpMinutes = 3
, tpSeconds = 4
, tpMillis = 5
}
in decomposeNDT time `shouldBe` expected
context "negative" $
it "should calculate the time parts" $ let
expected = TimeParts
{ tpDays = -1
, tpHours = -2
, tpMinutes = -3
, tpSeconds = -4
, tpMillis = -5
}
in decomposeNDT (-time) `shouldBe` expected
composeNDTSpec :: Spec
composeNDTSpec = describe "composeNDT" $
it "should compose correctly" $ let
parts = TimeParts
{ tpDays = 1
, tpHours = 2
, tpMinutes = 3
, tpSeconds = 4
, tpMillis = 5
}
t = composeNDT parts
in decomposeNDT t `shouldBe` parts
timerIsRunningSpec :: Spec
timerIsRunningSpec = describe "timerIsRunning" $ do
context "not running" $
it "should return False" $
timerIsRunning newTimer `shouldBe` False
context "running" $
it "should return True" $ do
timer <- startTimer newTimer
timerIsRunning timer `shouldBe` True
timerIsStartedSpec :: Spec
timerIsStartedSpec = describe "timerIsStarted" $ do
context "newTimer" $
it "should be False" $
timerIsStarted newTimer `shouldBe` False
context "started" $
it "should be True" $ do
timer <- startTimer newTimer
timerIsStarted timer `shouldBe` True
context "started, then stopped" $
it "should be True" $ do
(t, t') <- times 60
let
timer = startTimerAt t newTimer
timer' = stopTimerAt t' timer
timerIsStarted timer' `shouldBe` True
timeRemainingAtSpec :: Spec
timeRemainingAtSpec = describe "timeRemainingAt" $ do
context "not started" $
it "should be one minute" $ do
t <- getCurrentTime
let countdown = newCountdown 60
timeRemainingAt t countdown `shouldBe` 60
context "started" $ mapM_
(\(len, dt, expected) -> let
label = "length: " ++ show len ++
", elapsed: " ++ show dt
expectation = "should be " ++ show expected
in
context label $
it expectation $ do
(t, t') <- times dt
let
timer = startTimerAt t newTimer
countdown = Countdown
{ countdownLength = len
, countdownTimer = timer
}
timeRemainingAt t' countdown `shouldBe` expected)
-- length, elapsed, expected
[ ( 60, 30, 30 )
, ( 60, 60, 0 )
, ( 30, 60, -30 )
]
countdownIsCompletedAtSpec :: Spec
countdownIsCompletedAtSpec = describe "countdownIsCompletedAt" $ do
context "not started" $ mapM_
(\(label, dt, expected) ->
context label $
it ("should be " ++ show expected) $ do
t <- getCurrentTime
let cd = newCountdown dt
countdownIsCompletedAt t cd `shouldBe` expected)
-- label, length, expected
[ ( "negative length", -60, True )
, ( "zero length", 0, True )
, ( "posititive length", 60, False )
]
context "started" $ mapM_
(\(len, dt, expected) -> let
label = "length: " ++ show len ++
", elapsed: " ++ show dt
in context label $
it ("should be " ++ show expected) $ do
t1 <- getCurrentTime
let
t2 = addUTCTime dt t1
timer = startTimerAt t1 newTimer
countdown = (newCountdown len) { countdownTimer = timer }
countdownIsCompletedAt t2 countdown `shouldBe` expected)
-- length, elapsed, expected
[ ( 60, 0, False )
, ( 60, 30, False )
, ( 60, 60, True )
, ( 0, 0, True )
, ( 30, 60, True )
]
countdownIsRunningSpec :: Spec
countdownIsRunningSpec = describe "countdownIsRunning" $ do
context "not running" $
it "should return False" $ let
countdown = newCountdown 60
in countdownIsRunning countdown `shouldBe` False
context "running" $
it "should return True" $ do
timer <- startTimer newTimer
let countdown = (newCountdown 60) { countdownTimer = timer }
countdownIsRunning countdown `shouldBe` True
countdownIsStartedSpec :: Spec
countdownIsStartedSpec = describe "countdownIsStarted" $ do
context "new countdown" $
it "should be False" $
countdownIsStarted (newCountdown 60) `shouldBe` False
context "countdown started" $
it "should be True" $ do
countdown <- startCountdown $ newCountdown 60
countdownIsStarted countdown `shouldBe` True
context "countdown started, then stopped" $
it "should be True" $ do
(t, t') <- times 30
let
countdown = startCountdownAt t $ newCountdown 60
countdown' = stopCountdownAt t' countdown
countdownIsStarted countdown' `shouldBe` True
startCountdownSpec :: Spec
startCountdownSpec = describe "startCountdown" $
it "should start the countdown" $ do
let countdown = newCountdown 60
countdown' <- startCountdown countdown
countdownIsRunning countdown' `shouldBe` True
stopCountdownSpec :: Spec
stopCountdownSpec = describe "stopCountdown" $
it "should stop the countdown" $ do
countdown <- startCountdown $ newCountdown 60
countdown' <- stopCountdown countdown
countdownIsRunning countdown' `shouldBe` False
currentLapAtSpec :: Spec
currentLapAtSpec = describe "currentLapAt" $ do
context "newStopwatch" $
it "should be zero" $ do
t <- getCurrentTime
currentLapAt t newStopwatch ` shouldBe` 0
context "running" $
it "should be 1 minute" $ do
(t, t') <- times 60
let stopwatch = runningStopwatch t
currentLapAt t' stopwatch `shouldBe` 60
allLapsAtSpec :: Spec
allLapsAtSpec = describe "allLapsAt" $ do
context "newStopwatch" $
it "should return a lap of zero length" $ do
t <- getCurrentTime
allLapsAt t newStopwatch `shouldBe` [0]
context "single lap" $
it "should return a single lap of 1 minute" $ do
(t, t') <- times 60
let stopwatch = runningStopwatch t
allLapsAt t' stopwatch `shouldBe` [60]
context "multiple laps" $
it "should return the lap times" $ do
(t, t') <- times 60
let
timer = startTimerAt t newTimer
stopwatch = Stopwatch
{ stopwatchTimer = timer
, stopwatchLaps = [10, 20, 30]
}
allLapsAt t' stopwatch `shouldBe` [60, 10, 20, 30]
totalStopwatchTimeAtSpec :: Spec
totalStopwatchTimeAtSpec = describe "totalStopwatchTimeAt" $ do
context "newStopwatch" $
it "should be zero" $ do
t <- getCurrentTime
totalStopwatchTimeAt t newStopwatch `shouldBe` 0
context "single lap" $
it "should be 1 minute" $ do
(t, t') <- times 60
let stopwatch = runningStopwatch t
totalStopwatchTimeAt t' stopwatch `shouldBe` 60
context "multiple laps" $
it "should be 90 seconds" $ do
(t, t') <- times 60
let
stopwatch = (runningStopwatch t)
{ stopwatchLaps = [10, 20] }
totalStopwatchTimeAt t' stopwatch `shouldBe` 90
stopwatchIsRunningSpec :: Spec
stopwatchIsRunningSpec = describe "stopwatchIsRunning" $ do
context "newStopwatch" $
it "should be False" $
stopwatchIsRunning newStopwatch `shouldBe` False
context "running" $
it "should be True" $ do
t <- getCurrentTime
let stopwatch = runningStopwatch t
stopwatchIsRunning stopwatch `shouldBe` True
stopwatchIsStartedSpec :: Spec
stopwatchIsStartedSpec = describe "stopwatchIsStarted" $ do
context "newStopwatch" $
it "should be False" $
stopwatchIsStarted newStopwatch `shouldBe` False
context "started" $
it "should be True" $ do
(t, t') <- times 60
let
timer = startTimerAt t newTimer
stopwatch = newStopwatch { stopwatchTimer = timer }
stopwatchIsStarted stopwatch `shouldBe` True
context "previous laps" $
it "should be True" $ let
stopwatch = newStopwatch { stopwatchLaps = [60] }
in stopwatchIsStarted stopwatch `shouldBe` True
startStopwatchSpec = describe "startStopwatch" $
it "should start the stopwatch" $ do
stopwatch <- startStopwatch newStopwatch
stopwatchIsRunning stopwatch `shouldBe` True
stopStopwatchSpec = describe "stopStopwatch" $
it "should stop the Stopwatch" $ do
stopwatch <- startStopwatch newStopwatch
stopwatch' <- stopStopwatch stopwatch
stopwatchIsRunning stopwatch' `shouldBe` False
newLapAtSpec :: Spec
newLapAtSpec = describe "newLapAt" $ do
context "newStopwatch" $
it "should record a zero length lap, and start a new one" $ do
(t, t') <- times 60
let stopwatch = newLapAt t newStopwatch
allLapsAt t' stopwatch `shouldBe` [60, 0]
context "running" $
it "should start a new lap" $ do
(t, t') <- times 60
let
stopwatch = runningStopwatch t
stopwatch' = newLapAt t' stopwatch
allLapsAt t' stopwatch' `shouldBe` [0, 60]
context "previous lap" $
it "should start a new lap" $ do
(t, t') <- times 60
let
stopwatch = (runningStopwatch t)
{ stopwatchLaps = [30] }
stopwatch' = newLapAt t' stopwatch
allLapsAt t' stopwatch' `shouldBe` [0, 60, 30]
humanNDTSpec :: Spec
humanNDTSpec = describe "humanNDT" $ mapM_
(\(val, expected) ->
context (show val) $
it ("should be " ++ expected) $
humanNDT val `shouldBe` expected)
-- value, expected
[ ( 0, "0d 00h 00m 00.000s" )
, ( single, "1d 02h 03m 04.005s" )
, ( double, "11d 12h 13m 14.015s" )
, ( triple, "11d 12h 13m 14.123s" )
, ( negative, "-1d 02h 03m 04.005s" )
]
where
single = composeNDT $ TimeParts 1 2 3 4 5
double = composeNDT $ TimeParts 11 12 13 14 15
triple = composeNDT $ TimeParts 11 12 13 14 123
negative = -single
times :: NominalDiffTime -> IO (UTCTime, UTCTime)
times dt = do
t <- getCurrentTime
let t' = addUTCTime dt t
return (t, t')
runningStopwatch :: UTCTime -> Stopwatch
runningStopwatch t = newStopwatch { stopwatchTimer = timer }
where timer = startTimerAt t newTimer
-- jl