opt-env-conf-test-0.0.0.2: test/OptEnvConf/ArgsSpec.hs
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
module OptEnvConf.ArgsSpec (spec) where
import Data.List.NonEmpty (NonEmpty (..))
import OptEnvConf.Args
import OptEnvConf.Args.Gen ()
import Test.QuickCheck hiding (Args)
import Test.Syd
import Test.Syd.Validity
spec :: Spec
spec = do
describe "parseArg" $ do
it "produces valid args" $
producesValid parseArg
it "roundtrips with renderArg" $
forAllValid $ \s ->
renderArg (parseArg s) `shouldBe` s
describe "parseArgs" $ do
it "produces valid Args" $
producesValid parseArgs
let annoyingStrings :: Gen [String]
annoyingStrings = genListOf $ genListOf $ oneof [genValid, pure '-']
it "produces valid Argss for annoying strings" $
forAll annoyingStrings $
shouldBeValid . parseArgs
it "parses empty args as an empty arg map" $
parseArgs [] `shouldBe` emptyArgs
describe "renderDashed" $ do
it "roundtrips with parseArg for long dashed" $
forAllValid $ \n ->
parseArg (renderDashed (DashedLong n)) `shouldBe` ArgDashed True n
it "roundtrips with parseArg for short dashed" $
forAllValid $ \c ->
parseArg (renderDashed (DashedShort c)) `shouldBe` ArgDashed False (c :| [])
describe "consumeArgument" $ do
it "does not consume anything if there is nothing to consume" $
consumeArgument [] `shouldBe` [(Nothing, emptyArgs)]
it "consumes a plain argument when there is one" $
forAllValid $ \s ->
let as = [Live (ArgPlain s)]
in consumeArgument as
`shouldBe` [(Just s, Args [Dead] []), (Nothing, as)]
it "consumes a bare double-dash if it's the last argument" $
forAllValid $ \befores ->
let as = Args befores [Live ArgBareDoubleDash]
in consumeArgument as
`shouldBe` [ (Just "--", Args (befores ++ [Dead]) []),
(Nothing, as)
]
it "consumes any argument after a double-dash as an argument" $
forAllValid $ \befores ->
forAllValid $ \bareArg ->
forAllValid $ \rest ->
let as = Args befores (Live ArgBareDoubleDash : Live bareArg : rest)
in consumeArgument as
`shouldBe` [ (Just (renderArg bareArg), Args befores (Live ArgBareDoubleDash : Dead : rest)),
(Nothing, as)
]
it "skips dead arguments" $
forAllValid $ \befores ->
forAllValid $ \afters ->
consumeArgument (Args befores (Dead : afters)) `shouldBe` consumeArgument (Args (befores ++ [Dead]) afters)
it "tries to consume dashed argument followed by a dead argument" $
forAllValid $ \befores ->
forAllValid $ \isLong ->
forAllValid $ \cs ->
let d = ArgDashed isLong cs
args = Args befores [Live d, Dead]
in consumeArgument args
`shouldBe` [ (Nothing, Args (befores ++ [Live d, Dead]) []),
(Just (renderArg d), Args (befores ++ [Dead]) [Dead])
]
it "tries to consume dashed argument followed by a live argument" $
forAllValid $ \befores ->
forAllValid $ \isLong ->
forAllValid $ \cs ->
forAll (genValid `suchThat` (/= ArgBareDoubleDash)) $ \arg ->
let d = ArgDashed isLong cs
args = Args befores [Live d, Live arg]
in context (ppShow args) $
consumeArgument args
`shouldBe` [ (Nothing, Args (befores ++ [Live d, Live arg]) []),
-- Consuming the value (dashed is a switch) is
-- more likely than consuming the dashed as an
-- argument
(Just (renderArg arg), Args (befores ++ [Live d, Dead]) []),
(Just (renderArg d), Args (befores ++ [Dead]) [Live arg])
]
it "tries to ignore this value that looks like an option value" $
consumeArgument ["-p1", "--port", "2"]
`shouldBe` [ (Nothing, Args ["-p1", "--port", "2"] []),
(Just "2", Args ["-p1", "--port", Dead] []),
(Just "--port", Args ["-p1", Dead] ["2"]),
(Just "-p1", Args [Dead] ["--port", "2"])
]
describe "consumeSwitch" $ do
it "fails to consume if there are no dasheds" $
forAllValid $ \as ->
consumeSwitch [] as `shouldBe` Nothing
it "fails to consume if there are no arguments" $
forAllValid $ \ds ->
consumeSwitch ds [] `shouldBe` Nothing
it "does not consume a mismatched switch" $
consumeSwitch ["--foo"] ["--bar"] `shouldBe` Nothing
it "consumes a short switch if there are no other args" $
consumeSwitch ["-v"] ["-v"] `shouldBe` Just [Dead]
it "consumes a long switch if there are no other args" $
consumeSwitch ["--verbose"] ["--verbose"] `shouldBe` Just [Dead]
it "consumes a switch at the front first" $
consumeSwitch ["-a", "-b"] ["-a", "-b"] `shouldBe` Just [Dead, "-b"]
it "consumes a folded switch at the front first" $
consumeSwitch ["-a", "-b"] ["-ab"] `shouldBe` Just ["-b"]
describe "consumeOption" $ do
it "fails to consume if there are no dasheds" $
forAllValid $ \as ->
consumeOption [] as `shouldBe` Nothing
it "fails to consume if there are no arguments" $
forAllValid $ \ds ->
consumeOption ds [] `shouldBe` Nothing
it "does not consume a mismatched option" $
consumeOption ["--foo"] ["--bar", "quux"] `shouldBe` Nothing
it "consumes a short option" $
consumeOption ["-f"] ["-f", "foo"] `shouldBe` Just ("foo", [Dead])
it "consumes a long option" $
consumeOption ["--foo"] ["--foo", "foo"] `shouldBe` Just ("foo", [Dead])
it "consumes an option at the front first" $
consumeOption ["-f"] ["-f", "foo", "-f", "bar"] `shouldBe` Just ("foo", [Dead, "-f", "bar"])
it "consumes a folded option" $
consumeOption ["-f"] ["-vf", "foo"] `shouldBe` Just ("foo", ["-v", Dead])
it "consumes a long option with an equals sign" $
consumeOption ["--file"] ["--file=foo.txt"] `shouldBe` Just ("foo.txt", [Dead])
it "consumes a short option in shorthand notation" $
consumeOption ["-f"] ["-ffoo.txt"] `shouldBe` Just ("foo.txt", [Dead])
it "consumes a short option before a long option" $
consumeOption
["-p", "--port"]
["-p1", "--port", "2"]
`shouldBe` Just ("1", [Dead, "--port", "2"])
it "consumes a short option before a long option" $
consumeOption
["-p", "--port"]
[Dead, "--port", "2"]
`shouldBe` Just ("2", [Dead, Dead])