looper-0.2.0.1: test/LooperSpec.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module LooperSpec
( spec,
)
where
import Autodocodec.Yaml
import Looper
import Options.Applicative as AP
import Test.Syd
import UnliftIO
spec :: Spec
spec = do
describe "getLooperFlags" $ do
it "parses default values for an empty list of arguments" $ do
parserSucceedsWith
(getLooperFlags "test")
[]
( LooperFlags
{ looperFlagEnabled = Nothing,
looperFlagPhase = Nothing,
looperFlagPeriod = Nothing
}
)
it "parses an enable flag correctly" $ do
parserSucceedsWith
(getLooperFlags "test")
["--enable-test"]
( LooperFlags
{ looperFlagEnabled = Just True,
looperFlagPhase = Nothing,
looperFlagPeriod = Nothing
}
)
it "parses an disable flag correctly" $ do
parserSucceedsWith
(getLooperFlags "test")
["--disable-test"]
( LooperFlags
{ looperFlagEnabled = Just False,
looperFlagPhase = Nothing,
looperFlagPeriod = Nothing
}
)
describe "Configuration" $ do
it "has the same schema as before" $
pureGoldenByteStringFile "test_resources/configuration.txt" (renderColouredSchemaViaCodec @LooperConfiguration)
describe "runLoopers" $ do
it "runs one looper as intended" $ do
v <- newTVarIO (0 :: Int)
let l =
LooperDef
{ looperDefName = "l1",
looperDefEnabled = True,
looperDefPeriod = seconds 1,
looperDefPhase = seconds 0,
looperDefFunc = atomically $ modifyTVar' v succ
}
a <- async $ runLoopers [l]
waitNominalDiffTime $ seconds 1.5
cancel a
r <- readTVarIO v
r `shouldBe` 2
it "does not run a looper before its phase" $ do
v <- newTVarIO (0 :: Int)
let l =
LooperDef
{ looperDefName = "l",
looperDefEnabled = True,
looperDefPeriod = seconds 1,
looperDefPhase = seconds 2,
looperDefFunc = atomically $ modifyTVar' v succ
}
a <- async $ runLoopers [l]
waitNominalDiffTime $ seconds 1.5
cancel a
r <- readTVarIO v
r `shouldBe` 0
it "runs two loopers as intended" $ do
v1 <- newTVarIO (0 :: Int)
v2 <- newTVarIO (0 :: Int)
v3 <- newTVarIO (0 :: Int)
let l1 =
LooperDef
{ looperDefName = "l1",
looperDefEnabled = True,
looperDefPeriod = seconds 1,
looperDefPhase = seconds 0,
looperDefFunc =
atomically $ do
modifyTVar' v1 succ
modifyTVar' v2 succ
}
let l2 =
LooperDef
{ looperDefName = "l2",
looperDefEnabled = True,
looperDefPeriod = seconds 0.5,
looperDefPhase = seconds 0.5,
looperDefFunc =
atomically $ do
modifyTVar' v2 succ
modifyTVar' v3 succ
}
a <- async $ runLoopers [l1, l2]
waitNominalDiffTime $ seconds 2.25
cancel a
r1 <- readTVarIO v1
r2 <- readTVarIO v2
r3 <- readTVarIO v3
(r1, r2, r3) `shouldBe` (3, 7, 4)
describe "runLoopersRaw" $ do
it "runs one looper as intended with a custom runner" $ do
v1 <- newTVarIO (0 :: Int)
v2 <- newTVarIO (0 :: Int)
let inc1 = atomically $ modifyTVar' v1 succ
inc2 = atomically $ modifyTVar' v2 succ
let l =
LooperDef
{ looperDefName = "l",
looperDefEnabled = True,
looperDefPeriod = seconds 1,
looperDefPhase = seconds 0,
looperDefFunc = inc1
}
a <- async $ runLoopersRaw (const $ pure ()) (\ld -> looperDefFunc ld >> inc2) [l]
waitNominalDiffTime $ seconds 1.5
cancel a
r1 <- readTVarIO v1
r2 <- readTVarIO v2
(r1, r2) `shouldBe` (2, 2)
it "runs one looper as intended with a custom overrun hook" $ do
v1 <- newTVarIO (0 :: Int)
v2 <- newTVarIO (0 :: Int)
let inc1 = atomically $ modifyTVar' v1 succ
inc2 = atomically $ modifyTVar' v2 succ
let l =
LooperDef
{ looperDefName = "l",
looperDefEnabled = True,
looperDefPeriod = seconds 1,
looperDefPhase = seconds 0,
looperDefFunc = inc1 >> waitNominalDiffTime (seconds 1.5)
}
a <- async $ runLoopersRaw (const inc2) looperDefFunc [l]
waitNominalDiffTime $ seconds 3.5
cancel a
r1 <- readTVarIO v1
r2 <- readTVarIO v2
(r1, r2) `shouldBe` (3, 2)
parserSucceedsWith :: (Show a, Eq a) => Parser a -> [String] -> a -> Expectation
parserSucceedsWith parser args expectedValue =
case execParserPure parserPrefs (info parser mempty) args of
AP.Success r -> r `shouldBe` expectedValue
AP.Failure fp ->
let (err, ec) = renderFailure fp "test"
in expectationFailure $
unlines ["Failed to parse:", err, "would have resulted in exit code", show ec]
AP.CompletionInvoked _ -> expectationFailure "Tried to invoke a completion, should not happen"
where
parserPrefs :: ParserPrefs
parserPrefs =
defaultPrefs
{ prefShowHelpOnError = True,
prefShowHelpOnEmpty = True
}