looper 0.0.0.2 → 0.2.0.1
raw patch · 9 files changed
+286/−249 lines, 9 filesdep +autodocodecdep +autodocodec-yamldep +envparsedep −hspecsetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: autodocodec, autodocodec-yaml, envparse, sydtest
Dependencies removed: hspec
API changes (from Hackage documentation)
+ Looper: instance Autodocodec.Class.HasCodec Looper.LooperConfiguration
+ Looper: instance Data.Aeson.Types.ToJSON.ToJSON Looper.LooperConfiguration
+ Looper: looperEnvironmentParser :: String -> Parser Error LooperEnvironment
- Looper: LooperConfiguration :: Maybe Bool -> Maybe Int -> Maybe Int -> LooperConfiguration
+ Looper: LooperConfiguration :: Maybe Bool -> Maybe Word -> Maybe Word -> LooperConfiguration
- Looper: LooperEnvironment :: Maybe Bool -> Maybe Int -> Maybe Int -> LooperEnvironment
+ Looper: LooperEnvironment :: Maybe Bool -> Maybe Word -> Maybe Word -> LooperEnvironment
- Looper: LooperFlags :: Maybe Bool -> Maybe Int -> Maybe Int -> LooperFlags
+ Looper: LooperFlags :: Maybe Bool -> Maybe Word -> Maybe Word -> LooperFlags
- Looper: [looperConfPeriod] :: LooperConfiguration -> Maybe Int
+ Looper: [looperConfPeriod] :: LooperConfiguration -> Maybe Word
- Looper: [looperConfPhase] :: LooperConfiguration -> Maybe Int
+ Looper: [looperConfPhase] :: LooperConfiguration -> Maybe Word
- Looper: [looperEnvPeriod] :: LooperEnvironment -> Maybe Int
+ Looper: [looperEnvPeriod] :: LooperEnvironment -> Maybe Word
- Looper: [looperEnvPhase] :: LooperEnvironment -> Maybe Int
+ Looper: [looperEnvPhase] :: LooperEnvironment -> Maybe Word
- Looper: [looperFlagPeriod] :: LooperFlags -> Maybe Int
+ Looper: [looperFlagPeriod] :: LooperFlags -> Maybe Word
- Looper: [looperFlagPhase] :: LooperFlags -> Maybe Int
+ Looper: [looperFlagPhase] :: LooperFlags -> Maybe Word
- Looper: runLoopersIgnoreOverrun :: MonadUnliftIO m => (LooperDef m -> m ()) -> [LooperDef m] -> m ()
+ Looper: runLoopersIgnoreOverrun :: (MonadUnliftIO m, MonadUnliftIO n) => (LooperDef m -> n ()) -> [LooperDef m] -> n ()
- Looper: runLoopersRaw :: MonadUnliftIO m => m () -> (LooperDef m -> m ()) -> [LooperDef m] -> m ()
+ Looper: runLoopersRaw :: (MonadUnliftIO m, MonadUnliftIO n) => (LooperDef m -> n ()) -> (LooperDef m -> n ()) -> [LooperDef m] -> n ()
Files
- ChangeLog.md +0/−20
- LICENSE +1/−1
- README.md +0/−1
- Setup.hs +0/−2
- looper.cabal +12/−13
- src/Looper.hs +181/−154
- test/LooperSpec.hs +71/−57
- test/Spec.hs +1/−1
- test_resources/configuration.txt +20/−0
− ChangeLog.md
@@ -1,20 +0,0 @@-# Changelog- -## [Unreleased]--## [0.0.0.2] - 2019-05-15--### Changed--* Exposed 'readLooperEnvironment'-* Doc fix--## [0.0.0.1] - 2019-05-15--### Changed--* Better docs--## [0.0.0.0] - 2019-05-15--Initial version
LICENSE view
@@ -1,4 +1,4 @@-Copyright Tom Sydney Kerckhove (c) 2019+Copyright Tom Sydney Kerckhove (c) 2019-2022 All rights reserved.
− README.md
@@ -1,1 +0,0 @@-# looper
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
looper.cabal view
@@ -1,25 +1,22 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.1.+-- This file has been generated from package.yaml by hpack version 0.34.7. -- -- see: https://github.com/sol/hpack------ hash: 89647c3d09f7fa4d71e44a70470be2d4892fa2d52a50236d39c23d195fe84eae name: looper-version: 0.0.0.2-description: Please see the README on GitHub at <https://github.com/NorfairKing/looper#readme>+version: 0.2.0.1+description: Configure and run recurring jobs indefinitely homepage: https://github.com/NorfairKing/looper#readme bug-reports: https://github.com/NorfairKing/looper/issues author: Tom Sydney Kerckhove maintainer: syd@cs-syd.eu-copyright: Copyright: (c) 2019 Tom Sydney Kerckhove-license: BSD3+copyright: Copyright: (c) 2019-2022 Tom Sydney Kerckhove+license: MIT license-file: LICENSE build-type: Simple extra-source-files:- README.md- ChangeLog.md+ test_resources/configuration.txt source-repository head type: git@@ -34,7 +31,9 @@ src build-depends: aeson+ , autodocodec , base >=4.7 && <5+ , envparse , optparse-applicative , text , time@@ -50,13 +49,13 @@ hs-source-dirs: test ghc-options: -threaded -rtsopts -with-rtsopts=-N+ build-tool-depends:+ sydtest-discover:sydtest-discover build-depends:- aeson+ autodocodec-yaml , base >=4.7 && <5- , hspec , looper , optparse-applicative- , text- , time+ , sydtest , unliftio default-language: Haskell2010
src/Looper.hs view
@@ -1,55 +1,57 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} module Looper- ( LooperDef(..)- , seconds- , minutes- , hours- , LooperFlags(..)- , getLooperFlags- , LooperEnvironment(..)- , getLooperEnvironment- , readLooperEnvironment- , LooperConfiguration(..)- , LooperSettings(..)- , deriveLooperSettings- , mkLooperDef- , runLoopers- , runLoopersIgnoreOverrun- , runLoopersRaw- , waitNominalDiffTime- ) where--import GHC.Generics (Generic)+ ( LooperDef (..),+ seconds,+ minutes,+ hours,+ LooperFlags (..),+ getLooperFlags,+ LooperEnvironment (..),+ getLooperEnvironment,+ readLooperEnvironment,+ looperEnvironmentParser,+ LooperConfiguration (..),+ LooperSettings (..),+ deriveLooperSettings,+ mkLooperDef,+ runLoopers,+ runLoopersIgnoreOverrun,+ runLoopersRaw,+ waitNominalDiffTime,+ )+where +import Autodocodec import Control.Applicative import Control.Monad--import qualified System.Environment as System (getEnvironment)--import Data.Aeson+import Data.Aeson (FromJSON, ToJSON) import Data.Maybe-import Data.String import Data.Text (Text) import Data.Time--import Options.Applicative-import Text.Read-+import qualified Env+import GHC.Generics (Generic)+import Options.Applicative as OptParse+import qualified System.Environment as System (getEnvironment) import UnliftIO import UnliftIO.Concurrent -- | A looper definition-data LooperDef m =- LooperDef- { looperDefName :: Text -- ^ The name of the looper, can be useful for logging- , looperDefEnabled :: Bool -- ^ Whether this looper is enabled- , looperDefPeriod :: NominalDiffTime -- ^ The time between the start of each run- , looperDefPhase :: NominalDiffTime -- ^ The time before the first run- , looperDefFunc :: m () -- ^ The function to run- }+data LooperDef m = LooperDef+ { -- | The name of the looper, can be useful for logging+ looperDefName :: Text,+ -- | Whether this looper is enabled+ looperDefEnabled :: Bool,+ -- | The time between the start of each run+ looperDefPeriod :: NominalDiffTime,+ -- | The time before the first run+ looperDefPhase :: NominalDiffTime,+ -- | The function to run+ looperDefFunc :: m ()+ } deriving (Generic) -- | Construct a 'NominalDiffTime' from a number of seconds@@ -65,147 +67,167 @@ hours = minutes . (* 60) -- | A structure to parse command-line flags for a looper into-data LooperFlags =- LooperFlags- { looperFlagEnabled :: Maybe Bool- , looperFlagPhase :: Maybe Int -- Seconds- , looperFlagPeriod :: Maybe Int -- Seconds- }+data LooperFlags = LooperFlags+ { looperFlagEnabled :: Maybe Bool,+ looperFlagPhase :: Maybe Word, -- Seconds+ looperFlagPeriod :: Maybe Word -- Seconds+ } deriving (Show, Eq, Generic) -- | An optparse applicative parser for 'LooperFlags' getLooperFlags ::- String -- ^ The name of the looper (best to make this all-lowercase)- -> Parser LooperFlags+ -- | The name of the looper (best to make this all-lowercase)+ String ->+ OptParse.Parser LooperFlags getLooperFlags name =- LooperFlags <$> doubleSwitch name (unwords ["enable the", name, "looper"]) mempty <*>- option- (Just <$> auto)- (mconcat- [ long $ name <> "-phase"- , metavar "SECONDS"- , value Nothing- , help $ unwords ["the phase for the", name, "looper in seconsd"]- ]) <*>- option- (Just <$> auto)- (mconcat- [ long $ name <> "-period"- , metavar "SECONDS"- , value Nothing- , help $ unwords ["the period for the", name, "looper in seconds"]- ])+ LooperFlags <$> doubleSwitch name (unwords ["enable the", name, "looper"]) mempty+ <*> option+ (Just <$> auto)+ ( mconcat+ [ long $ name <> "-phase",+ metavar "SECONDS",+ value Nothing,+ help $ unwords ["the phase for the", name, "looper in seconsd"]+ ]+ )+ <*> option+ (Just <$> auto)+ ( mconcat+ [ long $ name <> "-period",+ metavar "SECONDS",+ value Nothing,+ help $ unwords ["the period for the", name, "looper in seconds"]+ ]+ ) -doubleSwitch :: String -> String -> Mod FlagFields Bool -> Parser (Maybe Bool)+doubleSwitch :: String -> String -> Mod FlagFields Bool -> OptParse.Parser (Maybe Bool) doubleSwitch name helpText mods = let enabledValue = True disabledValue = False defaultValue = True- in ((last . map Just) <$>- some- ((flag'- enabledValue- (hidden <> internal <> long ("enable-" ++ name) <> help helpText <> mods) <|>- flag'- disabledValue- (hidden <> internal <> long ("disable-" ++ name) <> help helpText <> mods)) <|>- flag'- disabledValue- (long ("(enable|disable)-" ++ name) <>- help ("Enable/disable " ++ helpText ++ " (default: " ++ show defaultValue ++ ")") <>- mods))) <|>- pure Nothing+ in ( last . map Just+ <$> some+ ( ( flag'+ enabledValue+ (hidden <> internal <> long ("enable-" ++ name) <> help helpText <> mods)+ <|> flag'+ disabledValue+ (hidden <> internal <> long ("disable-" ++ name) <> help helpText <> mods)+ )+ <|> flag'+ disabledValue+ ( long ("(enable|disable)-" ++ name)+ <> help ("Enable/disable " ++ helpText ++ " (default: " ++ show defaultValue ++ ")")+ <> mods+ )+ )+ )+ <|> pure Nothing -- | A structure to parse environment variables for a looper into-data LooperEnvironment =- LooperEnvironment- { looperEnvEnabled :: Maybe Bool- , looperEnvPhase :: Maybe Int -- Seconds- , looperEnvPeriod :: Maybe Int -- Seconds- }+data LooperEnvironment = LooperEnvironment+ { looperEnvEnabled :: Maybe Bool,+ looperEnvPhase :: Maybe Word, -- Seconds+ looperEnvPeriod :: Maybe Word -- Seconds+ } deriving (Show, Eq, Generic) -- | Get a 'LooperEnvironment' from the environment getLooperEnvironment ::- String -- ^ Prefix for each variable (best to make this all-caps)- -> String -- ^ Name of the looper (best to make this all-caps too)- -> IO LooperEnvironment+ -- | Prefix for each variable (best to make this all-caps)+ String ->+ -- | Name of the looper (best to make this all-caps too)+ String ->+ IO LooperEnvironment getLooperEnvironment prefix name = readLooperEnvironment prefix name <$> System.getEnvironment -- | Get a 'LooperEnvironment' from a pure environment readLooperEnvironment ::- String -- ^ Prefix for each variable (best to make this all-caps)- -> String -- ^ Name of the looper (best to make this all-caps too)- -> [(String, String)]- -> LooperEnvironment-readLooperEnvironment prefix name env =- let v :: IsString s => String -> Maybe s- v k = fromString <$> lookup (prefix <> k) env- r :: Read a => String -> Maybe a- r k = v k >>= readMaybe- lr :: Read a => String -> Maybe a- lr k = r $ name <> "_" <> k- in LooperEnvironment- { looperEnvEnabled = lr "ENABLED"- , looperEnvPhase = lr "PHASE"- , looperEnvPeriod = lr "PERIOD"- }+ -- | Prefix for each variable (best to make this all-caps)+ String ->+ -- | Name of the looper (best to make this all-caps too)+ String ->+ [(String, String)] ->+ LooperEnvironment+readLooperEnvironment prefix name env = case Env.parsePure (Env.prefixed prefix $ looperEnvironmentParser name) env of+ Left _ -> error "This indicates a bug in looper because all environment variables are optional."+ Right r -> r +-- | An 'envparse' parser for a 'LooperEnvironment'+looperEnvironmentParser ::+ -- | Name of the looper (best to make this all-caps)+ String ->+ Env.Parser Env.Error LooperEnvironment+looperEnvironmentParser name =+ Env.prefixed (name <> "_") $+ LooperEnvironment+ <$> Env.var (fmap Just . Env.auto) "ENABLED" (Env.def Nothing <> Env.help "Whether to enable this looper")+ <*> Env.var (fmap Just . Env.auto) "PHASE" (Env.def Nothing <> Env.help "The amount of time to wait before starting the looper the first time, in seconds")+ <*> Env.var (fmap Just . Env.auto) "PERIOD" (Env.def Nothing <> Env.help "The amount of time to wait between runs of the looper, in seconds")+ -- | A structure to configuration for a looper into-data LooperConfiguration =- LooperConfiguration- { looperConfEnabled :: Maybe Bool- , looperConfPhase :: Maybe Int- , looperConfPeriod :: Maybe Int- }- deriving (Show, Eq, Generic)+data LooperConfiguration = LooperConfiguration+ { looperConfEnabled :: Maybe Bool,+ looperConfPhase :: Maybe Word,+ looperConfPeriod :: Maybe Word+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON) via (Autodocodec LooperConfiguration) --- | You can parse Data.Aeson's JSON or Data.Yaml's YAML to parse a 'LooperConfiguration'.--- You can also use Data.Yaml.Config.-instance FromJSON LooperConfiguration where- parseJSON =- withObject "LooperConfiguration" $ \o ->- LooperConfiguration <$> o .:? "enabled" <*> o .:? "phase" <*> o .: "period"+instance HasCodec LooperConfiguration where+ codec =+ named "LooperConfiguration" $+ object "LooperConfiguration" $+ LooperConfiguration+ <$> parseAlternative+ (optionalFieldOrNull "enable" "Enable this looper")+ (optionalFieldOrNull "enabled" "Enable this looper")+ .= looperConfEnabled+ <*> optionalFieldOrNull "phase" "The amount of time to wait before starting the looper the first time, in seconds" .= looperConfPhase+ <*> optionalFieldOrNull "period" "The amount of time to wait between runs of the looper, in seconds" .= looperConfPeriod -- | Settings that you might want to pass into a looper using 'mkLooperDef'-data LooperSettings =- LooperSettings- { looperSetEnabled :: Bool- , looperSetPhase :: NominalDiffTime- , looperSetPeriod :: NominalDiffTime- }+data LooperSettings = LooperSettings+ { looperSetEnabled :: Bool,+ looperSetPhase :: NominalDiffTime,+ looperSetPeriod :: NominalDiffTime+ } deriving (Show, Eq, Generic) deriveLooperSettings ::- NominalDiffTime -- ^ Default phase- -> NominalDiffTime -- ^ Default period- -> LooperFlags- -> LooperEnvironment- -> Maybe LooperConfiguration- -> LooperSettings+ -- | Default phase+ NominalDiffTime ->+ -- | Default period+ NominalDiffTime ->+ LooperFlags ->+ LooperEnvironment ->+ Maybe LooperConfiguration ->+ LooperSettings deriveLooperSettings defaultPhase defaultPeriod LooperFlags {..} LooperEnvironment {..} mlc = let looperSetEnabled = fromMaybe True $ looperFlagEnabled <|> looperEnvEnabled <|> (mlc >>= looperConfEnabled) looperSetPhase = maybe defaultPhase fromIntegral $- looperFlagPhase <|> looperEnvPhase <|> (mlc >>= looperConfPhase)+ looperFlagPhase <|> looperEnvPhase <|> (mlc >>= looperConfPhase) looperSetPeriod = maybe defaultPeriod fromIntegral $- looperFlagPeriod <|> looperEnvPeriod <|> (mlc >>= looperConfPeriod)+ looperFlagPeriod <|> looperEnvPeriod <|> (mlc >>= looperConfPeriod) in LooperSettings {..} mkLooperDef ::- Text -- ^ Name- -> LooperSettings- -> m () -- ^ The function to loop- -> LooperDef m+ -- | Name+ Text ->+ LooperSettings ->+ -- | The function to loop+ m () ->+ LooperDef m mkLooperDef name LooperSettings {..} func = LooperDef- { looperDefName = name- , looperDefEnabled = looperSetEnabled- , looperDefPeriod = looperSetPeriod- , looperDefPhase = looperSetPhase- , looperDefFunc = func+ { looperDefName = name,+ looperDefEnabled = looperSetEnabled,+ looperDefPeriod = looperSetPeriod,+ looperDefPhase = looperSetPhase,+ looperDefFunc = func } -- | Simply run loopers@@ -226,11 +248,13 @@ -- -- Note that this function will loop forever, you need to wrap it using 'async' yourself. runLoopersIgnoreOverrun ::- MonadUnliftIO m- => (LooperDef m -> m ()) -- ^ Custom runner- -> [LooperDef m] -- ^ Loopers- -> m ()-runLoopersIgnoreOverrun = runLoopersRaw (pure ())+ (MonadUnliftIO m, MonadUnliftIO n) =>+ -- | Custom runner+ (LooperDef m -> n ()) ->+ -- | Loopers+ [LooperDef m] ->+ n ()+runLoopersIgnoreOverrun = runLoopersRaw (const $ pure ()) -- | Run loopers, with a custom runner and overrun handler --@@ -244,23 +268,26 @@ -- -- Note that this function will loop forever, you need to wrap it using 'async' yourself. runLoopersRaw ::- MonadUnliftIO m- => m () -- ^ Overrun handler- -> (LooperDef m -> m ()) -- ^ Runner- -> [LooperDef m] -- ^ Loopers- -> m ()+ (MonadUnliftIO m, MonadUnliftIO n) =>+ -- | Overrun handler+ (LooperDef m -> n ()) ->+ -- | Runner+ (LooperDef m -> n ()) ->+ -- | Loopers+ [LooperDef m] ->+ n () runLoopersRaw onOverrun runLooper = mapConcurrently_ $ \ld@LooperDef {..} -> when looperDefEnabled $ do waitNominalDiffTime looperDefPhase let loop = do- start <- liftIO $ getCurrentTime+ start <- liftIO getCurrentTime runLooper ld- end <- liftIO $ getCurrentTime+ end <- liftIO getCurrentTime let elapsed = diffUTCTime end start let nextWait = looperDefPeriod - elapsed- if (nextWait < 0)- then onOverrun+ if nextWait < 0+ then onOverrun ld else waitNominalDiffTime nextWait loop loop
test/LooperSpec.hs view
@@ -1,16 +1,17 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-} module LooperSpec- ( spec- ) where--import Test.Hspec+ ( spec,+ )+where +import Autodocodec.Yaml+import Looper import Options.Applicative as AP+import Test.Syd import UnliftIO -import Looper- spec :: Spec spec = do describe "getLooperFlags" $ do@@ -18,33 +19,50 @@ parserSucceedsWith (getLooperFlags "test") []- (LooperFlags- {looperFlagEnabled = Nothing, looperFlagPhase = Nothing, looperFlagPeriod = Nothing})+ ( 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})+ ( 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})+ ( 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 0.01- , looperDefPhase = seconds 0- , looperDefFunc = atomically $ modifyTVar' v succ+ { looperDefName = "l1",+ looperDefEnabled = True,+ looperDefPeriod = seconds 1,+ looperDefPhase = seconds 0,+ looperDefFunc = atomically $ modifyTVar' v succ } a <- async $ runLoopers [l]- waitNominalDiffTime $ seconds 0.015+ waitNominalDiffTime $ seconds 1.5 cancel a r <- readTVarIO v r `shouldBe` 2@@ -52,14 +70,14 @@ v <- newTVarIO (0 :: Int) let l = LooperDef- { looperDefName = "l"- , looperDefEnabled = True- , looperDefPeriod = seconds 0.01- , looperDefPhase = seconds 0.02- , looperDefFunc = atomically $ modifyTVar' v succ+ { looperDefName = "l",+ looperDefEnabled = True,+ looperDefPeriod = seconds 1,+ looperDefPhase = seconds 2,+ looperDefFunc = atomically $ modifyTVar' v succ } a <- async $ runLoopers [l]- waitNominalDiffTime $ seconds 0.015+ waitNominalDiffTime $ seconds 1.5 cancel a r <- readTVarIO v r `shouldBe` 0@@ -69,28 +87,28 @@ v3 <- newTVarIO (0 :: Int) let l1 = LooperDef- { looperDefName = "l1"- , looperDefEnabled = True- , looperDefPeriod = seconds 0.01- , looperDefPhase = seconds 0- , looperDefFunc =+ { 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.005- , looperDefPhase = seconds 0.005- , looperDefFunc =+ { 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 0.0225+ waitNominalDiffTime $ seconds 2.25 cancel a r1 <- readTVarIO v1 r2 <- readTVarIO v2@@ -104,14 +122,14 @@ inc2 = atomically $ modifyTVar' v2 succ let l = LooperDef- { looperDefName = "l"- , looperDefEnabled = True- , looperDefPeriod = seconds 0.01- , looperDefPhase = seconds 0- , looperDefFunc = inc1+ { looperDefName = "l",+ looperDefEnabled = True,+ looperDefPeriod = seconds 1,+ looperDefPhase = seconds 0,+ looperDefFunc = inc1 }- a <- async $ runLoopersRaw (pure ()) (\ld -> looperDefFunc ld >> inc2) [l]- waitNominalDiffTime $ seconds 0.015+ a <- async $ runLoopersRaw (const $ pure ()) (\ld -> looperDefFunc ld >> inc2) [l]+ waitNominalDiffTime $ seconds 1.5 cancel a r1 <- readTVarIO v1 r2 <- readTVarIO v2@@ -123,14 +141,14 @@ inc2 = atomically $ modifyTVar' v2 succ let l = LooperDef- { looperDefName = "l"- , looperDefEnabled = True- , looperDefPeriod = seconds 0.01- , looperDefPhase = seconds 0- , looperDefFunc = inc1 >> waitNominalDiffTime (seconds 0.015)+ { looperDefName = "l",+ looperDefEnabled = True,+ looperDefPeriod = seconds 1,+ looperDefPhase = seconds 0,+ looperDefFunc = inc1 >> waitNominalDiffTime (seconds 1.5) }- a <- async $ runLoopersRaw inc2 looperDefFunc [l]- waitNominalDiffTime $ seconds 0.035+ a <- async $ runLoopersRaw (const inc2) looperDefFunc [l]+ waitNominalDiffTime $ seconds 3.5 cancel a r1 <- readTVarIO v1 r2 <- readTVarIO v2@@ -143,16 +161,12 @@ AP.Failure fp -> let (err, ec) = renderFailure fp "test" in expectationFailure $- unlines ["Failed to parse:", err, "would have resulted in exit code", show ec]+ 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 =- ParserPrefs- { prefMultiSuffix = ""- , prefDisambiguate = True- , prefShowHelpOnError = True- , prefShowHelpOnEmpty = True- , prefBacktrack = True- , prefColumns = 80+ defaultPrefs+ { prefShowHelpOnError = True,+ prefShowHelpOnEmpty = True }
test/Spec.hs view
@@ -1,1 +1,1 @@-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}+{-# OPTIONS_GHC -F -pgmF sydtest-discover #-}
+ test_resources/configuration.txt view
@@ -0,0 +1,20 @@+[36mdef: LooperConfiguration[m+# LooperConfiguration+# [32many of[m+[ [37menable[m: # [34moptional[m+ # Enable this looper+ # [32mor null[m+ [33m<boolean>[m+, [37menabled[m: # [34moptional[m+ # Enable this looper+ # [32mor null[m+ [33m<boolean>[m+]+[37mphase[m: # [34moptional[m+ # The amount of time to wait before starting the looper the first time, in seconds+ # [32mor null[m+ [33m<number>[m # between [32m0[m and [32m18446744073709551615[m+[37mperiod[m: # [34moptional[m+ # The amount of time to wait between runs of the looper, in seconds+ # [32mor null[m+ [33m<number>[m # between [32m0[m and [32m18446744073709551615[m