{-# 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])