packages feed

o-clock-1.2.0: test/Test/Time/Units.hs

-- SPDX-FileCopyrightText: 2019 Serokell <https://serokell.io>
--
-- SPDX-License-Identifier: MPL-2.0

{-# LANGUAGE DataKinds          #-}
{-# LANGUAGE ExplicitNamespaces #-}
{-# LANGUAGE TypeApplications   #-}
{-# LANGUAGE TypeOperators      #-}

module Test.Time.Units
       ( unitsTestTree
       ) where

import Control.Exception (evaluate)
import GHC.Real (Ratio ((:%)))
import Test.Tasty (TestTree)
import Test.Tasty.Hspec (Spec, anyException, describe, it, shouldBe, shouldThrow, testSpec)

import Time (Day, Hour, Millisecond, Minute, Second, Time (..), Week, day, floorUnit, fortnight,
             hour, mcs, minute, ms, ns, ps, sec, seriesF, toUnit, unitsF, week, (+:+))

unitsTestTree :: IO TestTree
unitsTestTree = testSpec "Units" spec_Units

spec_Units :: Spec
spec_Units = do
    describe "Unit Conversion Test" $ do
        it "11 seconds is 11000 milliseconds" $
            toUnit (sec 11) `shouldBe` ms 11000
        it "5000 milliseconds is 5 seconds" $
            toUnit (ms 5000) `shouldBe` sec 5
        it "3 seconds is 3000000 microseconds" $
            toUnit (sec 3) `shouldBe` mcs 3000000
        it "3 microseconds is 3/1000000 seconds" $
            toUnit @Second (mcs 3) `shouldBe` Time (3 :% 1000000)
        it "7 days is 1 week" $
            toUnit (day 7) `shouldBe` week 1
        it "2 fornights is 28 days" $
            toUnit (fortnight 2) `shouldBe` day 28
        it "1 nanosecond is 1000 picoseconds" $
            toUnit (ns 1) `shouldBe` ps 1000
    describe "Read Time Test" $ do
        it "parses '42s' as 42 seconds" $
            read "42s" `shouldBe` sec 42
        it "fails when '42mm' is expected as seconds" $
            evaluate (read @(Time Second) "42mm") `shouldThrow` anyException
        it "parses '7/2s' as 7/2 seconds" $
            read @(Time Second) "7/2s" `shouldBe` Time (7 :% 2)
        it "fails when '-4s' is expected as seconds" $
            evaluate (read @(Time Second) "-4s") `shouldThrow` anyException
        it "parses '25+5/7s' as 180/7 seconds" $
            read @(Time Second) "25+5/7s" `shouldBe` Time (180 :% 7)
        it "fails when '3+2s' is expected as seconds" $
            evaluate (read @(Time Second) "3+2s") `shouldThrow` anyException
        it "fails when '+3s' is expected as seconds" $
            evaluate (read @(Time Second) "+3s") `shouldThrow` anyException
        it "fails when '/3s' is expected as seconds" $
            evaluate (read @(Time Second) "/3s") `shouldThrow` anyException
        it "parses '14/2h' as 7 hours" $
            read "14/2h" `shouldBe` hour 7
        it "fails when '14/2h' expected as 7 seconds" $
            evaluate (read @(Time Second) "14/2h") `shouldThrow` anyException
        it "parses big number to big number" $
            read ('1' : replicate 20 '0' ++ "mcs") `shouldBe` mcs (10 ^ (20 :: Int))
        it "fails when '4ms' expected as 4 seconds" $
            evaluate (read @(Time Second) "4ms") `shouldThrow` anyException
    describe "Floor tests" $ do
        it "returns 0s when floor < 1 second" $
            floorUnit (Time @Second $ 2 :% 3) `shouldBe` sec 0
        it "returns 2d when floor 2.5 days" $
            floorUnit (Time $ 5 :% 2) `shouldBe` day 2
        it "returns 42ps when floor integer" $
            floorUnit (ps 42) `shouldBe` ps 42
    describe "Formatting tests" $ do
        it "4000 minutes should be formatted without ending-zeros" $
            seriesF @'[Day, Hour, Minute, Second] (minute 4000) `shouldBe` "2d18h40m"
        it "4000 minutes should be formatted without beginning-zeros" $
            seriesF @'[Week, Day, Hour, Minute] (minute 4000) `shouldBe` "2d18h40m"
        it "3601 sec should be formatted without middle-zeros" $
            seriesF @'[Hour, Minute, Second] (sec 3601) `shouldBe` "1h1s"
        it "works on rational nums" $
            seriesF @'[Hour, Second, Millisecond] (Time @Minute $ 3 :% 2) `shouldBe` "90s"
        it "works without minutes formatting" $
            seriesF @'[Day, Minute, Second] (minute 4000) `shouldBe` "2d1120m"

        it "4000 minutes should be formatted like 2d18h40m" $
            unitsF (minute 4000) `shouldBe` "2d18h40m"
        it "42 fortnights should be formatted like 42fn" $
            unitsF (fortnight 42) `shouldBe` "42fn"
        it "the first zero time unit when receive zero time" $
            unitsF (Time @Hour 0) `shouldBe` "0fn"
        it "sums all time units" $
            unitsF (  fortnight 1 +:+ week 1 +:+ day 1 +:+ hour 1 +:+ minute 1
                  +:+ sec 1 +:+ ms 1 +:+ mcs 1 +:+ ns 1 +:+ ps 1
                   ) `shouldBe` "1fn1w1d1h1m1s1ms1mcs1ns1ps"