opt-env-conf-test 0.0.0.3 → 0.0.0.4
raw patch · 50 files changed
+1941/−308 lines, 50 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- OptEnvConf.Test: pureGoldenNixOptions :: FilePath -> Parser a -> GoldenTest Text
+ OptEnvConf.Test: pureGoldenNixOptions :: HasCallStack => FilePath -> Parser a -> GoldenTest Text
Files
- CHANGELOG.md +6/−0
- opt-env-conf-test.cabal +2/−2
- src/OptEnvConf/Test.hs +42/−4
- test/OptEnvConf/APISpec.hs +1/−2
- test/OptEnvConf/CompleterSpec.hs +179/−21
- test/OptEnvConf/CompletionSpec.hs +1540/−233
- test/OptEnvConf/RunSpec.hs +52/−11
- test_resources/completion/fish-completion-script.fish +4/−3
- test_resources/completion/zsh-completion-script.zsh +9/−2
- test_resources/docs/big-config/nix-options.nix +4/−0
- test_resources/docs/empty/nix-options.nix +4/−0
- test_resources/docs/enable-disable-optional/nix-options.nix +4/−0
- test_resources/docs/enable-disable/nix-options.nix +4/−0
- test_resources/docs/greet/nix-options.nix +4/−0
- test_resources/docs/hidden/nix-options.nix +4/−0
- test_resources/docs/many-args/nix-options.nix +4/−0
- test_resources/docs/optional/nix-options.nix +4/−0
- test_resources/docs/same-help/nix-options.nix +4/−0
- test_resources/docs/secret/nix-options.nix +4/−0
- test_resources/docs/some-args/nix-options.nix +4/−0
- test_resources/docs/sub-commands/nix-options.nix +4/−0
- test_resources/docs/sub-settings/nix-options.nix +4/−0
- test_resources/docs/sum-type/nix-options.nix +4/−0
- test_resources/docs/three-commands/nix-options.nix +4/−0
- test_resources/docs/verbose/nix-options.nix +4/−0
- test_resources/docs/with-default/nix-options.nix +4/−0
- test_resources/docs/yes-no-optional/nix-options.nix +4/−0
- test_resources/docs/yes-no/nix-options.nix +4/−0
- test_resources/error/all-or-nothing-relevant.txt +1/−1
- test_resources/error/all-or-nothing.txt +1/−1
- test_resources/help/toplevel-pure-with-invalid-port.txt +3/−3
- test_resources/help/toplevel-pure.txt +3/−3
- test_resources/help/wrong-command.txt +3/−3
- test_resources/lint/config-without-load.txt +1/−1
- test_resources/lint/dash-in-long.txt +1/−1
- test_resources/lint/dash-in-short.txt +1/−1
- test_resources/lint/empty-setting.txt +1/−1
- test_resources/lint/no-commands.txt +1/−1
- test_resources/lint/no-dashed-for-option.txt +1/−1
- test_resources/lint/no-dashed-for-switch.txt +1/−1
- test_resources/lint/no-metavar-for-argument.txt +1/−1
- test_resources/lint/no-metavar-for-env.txt +1/−1
- test_resources/lint/no-metavar-for-option.txt +1/−1
- test_resources/lint/no-option-or-switch-for-dashed.txt +1/−1
- test_resources/lint/no-reader-for-argument.txt +1/−1
- test_resources/lint/no-reader-for-env.txt +2/−2
- test_resources/lint/no-reader-for-option.txt +1/−1
- test_resources/lint/null-setting.txt +2/−2
- test_resources/lint/unknown-default-command.txt +1/−1
- test_resources/lint/unreadable-example.txt +1/−1
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Changelog +## [0.0.0.4] - 2026-02-25++### Changed++* `pureGoldenNixOptions` now prepends a generated file comment pointing to the test that generated each file+ ## [0.0.0.3] - 2025-11-20 ### Added
opt-env-conf-test.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.36.1.+-- This file has been generated from package.yaml by hpack version 0.38.3. -- -- see: https://github.com/sol/hpack name: opt-env-conf-test-version: 0.0.0.3+version: 0.0.0.4 synopsis: A testing companion package for opt-env-conf homepage: https://github.com/NorfairKing/opt-env-conf#readme bug-reports: https://github.com/NorfairKing/opt-env-conf/issues
src/OptEnvConf/Test.hs view
@@ -38,10 +38,12 @@ where import Data.Aeson as JSON+import Data.Char (isDigit) import Data.Maybe import Data.Text (Text) import qualified Data.Text as T-import GHC.Stack (HasCallStack, withFrozenCallStack)+import GHC.Stack (CallStack, HasCallStack, getCallStack, withFrozenCallStack)+import qualified GHC.Stack as Stack import OptEnvConf import OptEnvConf.Args import OptEnvConf.Completion@@ -106,7 +108,8 @@ let arg = fromMaybe "" $ listToMaybe $ drop ix ws let completions = pureCompletionQuery p ix ws evaluatedCompletions <- evalCompletions arg completions- evaluatedCompletions `shouldBe` expected+ let toStringCompletion c = c {completionSuggestion = completionResultValue (completionSuggestion c)}+ map toStringCompletion evaluatedCompletions `shouldBe` expected parserCompletionDescriptionTest :: Parser a -> Int -> [String] -> [String] -> IO () parserCompletionDescriptionTest p ix ws descriptions = do@@ -136,7 +139,42 @@ specify "produces the nix options as before" $ pureGoldenNixOptions path parser -pureGoldenNixOptions :: FilePath -> Parser a -> GoldenTest Text+pureGoldenNixOptions :: (HasCallStack) => FilePath -> Parser a -> GoldenTest Text pureGoldenNixOptions path parser = pureGoldenTextFile path $- renderParserNixOptions parser+ generatedFileComment Stack.callStack <> renderParserNixOptions parser++generatedFileComment :: CallStack -> T.Text+generatedFileComment cs =+ let loc = case getCallStack cs of+ (_, srcLoc) : _ ->+ packageName (Stack.srcLocPackage srcLoc)+ <> ":"+ <> Stack.srcLocFile srcLoc+ <> ":"+ <> show (Stack.srcLocStartLine srcLoc)+ [] -> "unknown"+ in T.pack $+ unlines $+ map+ ("# " <>)+ [ "DO NOT EDIT THIS FILE DIRECTLY",+ "This file was generated by",+ loc,+ "To update this it, run the test with --golden-reset"+ ]++-- | Extract just the package name from a GHC package identifier.+--+-- GHC package identifiers look like: @name-version-hash-unitid@+-- This function returns just @name@.+packageName :: String -> String+packageName = go+ where+ go [] = []+ go ('-' : rest)+ | isVersion version = []+ where+ version = takeWhile (\c -> isDigit c || c == '.') rest+ go (c : rest) = c : go rest+ isVersion v = not (null v) && elem '.' v && all (\c -> isDigit c || c == '.') v
test/OptEnvConf/APISpec.hs view
@@ -112,8 +112,7 @@ parserDocs p it "renders the Nix options the same way" $- pureGoldenTextFile ("test_resources/docs/" <> dir <> "/nix-options.nix") $- renderParserNixOptions p+ pureGoldenNixOptions ("test_resources/docs/" <> dir <> "/nix-options.nix") p sameHelpParser :: Parser (Either Int String, Bool) sameHelpParser =
test/OptEnvConf/CompleterSpec.hs view
@@ -27,6 +27,9 @@ -- File in dir in dir exampleFile3 <- resolveFile deeperDir "gold.txt" writeFile (fromAbsFile exampleFile3) ""+ -- YAML file+ yamlFile <- resolveFile tdir "config.yaml"+ writeFile (fromAbsFile yamlFile) "" -- Hidden file hiddenFile <- resolveFile tdir ".hidden.txt" writeFile (fromAbsFile hiddenFile) ""@@ -43,37 +46,192 @@ ) $ do describe "filePath" $ do- let c :: (HasCallStack) => String -> [String] -> TestDef '[Path Abs Dir] ()+ let c :: (HasCallStack) => String -> [CompletionResult] -> TestDef '[Path Abs Dir] () c s l = withFrozenCallStack $- itWithOuter (unwords ["can complete", show s, "to", show l]) $ \tdir ->+ itWithOuter (unwords ["can complete", show s, "to", show (map completionResultValue l)]) $ \tdir -> withCurrentDir tdir $ unCompleter filePath s `shouldReturn` l - c "" ["foo.txt", "bar/"]- c "f" ["foo.txt"]- c "b" ["bar/"]- c "bar" ["bar/quux.txt", "bar/", "bar/deep/"]+ c "" [fileR "foo.txt", fileR "config.yaml", dirR "bar/"]+ c "f" [fileR "foo.txt"]+ c "b" [dirR "bar/"]+ c "bar" [fileR "bar/quux.txt", dirR "bar/", dirR "bar/deep/"]+ c "c" [fileR "config.yaml"] c "q" []- c "." [".hidden.txt", ".hidden/"]- c "./" ["./foo.txt", "./bar/"]- c "././" ["././foo.txt", "././bar/"]- c "./." ["./.hidden.txt", "./.hidden/"]- c "./bar" ["./bar/quux.txt", "./bar/", "./bar/deep/"]+ c "." [fileR ".hidden.txt", dirR ".hidden/"]+ c "./" [fileR "./foo.txt", fileR "./config.yaml", dirR "./bar/"]+ c "././" [fileR "././foo.txt", fileR "././config.yaml", dirR "././bar/"]+ c "./." [fileR "./.hidden.txt", dirR "./.hidden/"]+ c "./bar" [fileR "./bar/quux.txt", dirR "./bar/", dirR "./bar/deep/"] + -- Deeper nesting.+ -- Directories end in /, files do not. This convention is how+ -- shells decide whether to append a trailing space after a+ -- completion.+ c "bar/" [fileR "bar/quux.txt", dirR "bar/deep/"]+ c "bar/d" [dirR "bar/deep/"]+ c "bar/deep" [fileR "bar/deep/gold.txt", dirR "bar/deep/"]+ c "bar/deep/" [fileR "bar/deep/gold.txt"]+ c "bar/q" [fileR "bar/quux.txt"]++ -- Parent directory paths (..)+ -- These tests run from inside bar/ so that .. refers to the+ -- test root directory.+ itWithOuter "can complete \"..\" to parent directory contents" $ \tdir -> do+ subdir <- resolveDir tdir "bar"+ withCurrentDir subdir $ do+ results <- unCompleter filePath ".."+ results+ `shouldBe` [ fileR "../foo.txt",+ fileR "../config.yaml",+ dirR "../bar/"+ ]+ itWithOuter "can complete \"../\" to parent directory contents" $ \tdir -> do+ subdir <- resolveDir tdir "bar"+ withCurrentDir subdir $ do+ results <- unCompleter filePath "../"+ results+ `shouldBe` [ fileR "../foo.txt",+ fileR "../config.yaml",+ dirR "../bar/"+ ]+ itWithOuter "can complete \"../f\" to matching parent files" $ \tdir -> do+ subdir <- resolveDir tdir "bar"+ withCurrentDir subdir $ do+ results <- unCompleter filePath "../f"+ results+ `shouldBe` [fileR "../foo.txt"]+ itWithOuter "can complete \"../bar/\" to sibling dir contents" $ \tdir -> do+ subdir <- resolveDir tdir "bar"+ withCurrentDir subdir $ do+ results <- unCompleter filePath "../bar/"+ results+ `shouldBe` [ fileR "../bar/quux.txt",+ dirR "../bar/deep/"+ ]++ -- Parent directory in the middle of a path+ itWithOuter "can complete \"bar/../\" to current dir contents" $ \tdir ->+ withCurrentDir tdir $ do+ results <- unCompleter filePath "bar/../"+ results+ `shouldBe` [ fileR "bar/../foo.txt",+ fileR "bar/../config.yaml",+ dirR "bar/../bar/"+ ]+ itWithOuter "can complete \"bar/../f\" to matching files" $ \tdir ->+ withCurrentDir tdir $ do+ results <- unCompleter filePath "bar/../f"+ results+ `shouldBe` [fileR "bar/../foo.txt"]+ itWithOuter "can complete \"../bar/deep/../\" to bar/ contents" $ \tdir -> do+ subdir <- resolveDir tdir "bar"+ withCurrentDir subdir $ do+ results <- unCompleter filePath "../bar/deep/../"+ results+ `shouldBe` [ fileR "../bar/deep/../quux.txt",+ dirR "../bar/deep/../deep/"+ ]++ -- Absolute paths+ itWithOuter "can complete absolute paths" $ \tdir ->+ withCurrentDir tdir $ do+ let absPrefix = fromAbsDir tdir+ results <- unCompleter filePath absPrefix+ results+ `shouldBe` [ fileR (absPrefix <> "foo.txt"),+ fileR (absPrefix <> "config.yaml"),+ dirR (absPrefix <> "bar/")+ ]+ describe "directoryPath" $ do- let c :: (HasCallStack) => String -> [String] -> TestDef '[Path Abs Dir] ()+ let c :: (HasCallStack) => String -> [CompletionResult] -> TestDef '[Path Abs Dir] () c s l = withFrozenCallStack $- itWithOuter (unwords ["can complete", show s, "to", show l]) $ \tdir ->+ itWithOuter (unwords ["can complete", show s, "to", show (map completionResultValue l)]) $ \tdir -> withCurrentDir tdir $ unCompleter directoryPath s `shouldReturn` l - c "" ["bar/"]- c "b" ["bar/"]+ c "" [dirR "bar/"]+ c "b" [dirR "bar/"] c "f" []- c "." [".hidden/"]- c "./" ["./bar/"]- c "./." ["./.hidden/"]- c "././" ["././bar/"]- c "./." ["./.hidden/"]- c "./bar" ["./bar/", "./bar/deep/"]+ c "." [dirR ".hidden/"]+ c "./" [dirR "./bar/"]+ c "./." [dirR "./.hidden/"]+ c "././" [dirR "././bar/"]+ c "./." [dirR "./.hidden/"]+ c "./bar" [dirR "./bar/", dirR "./bar/deep/"]++ -- Deeper nesting.+ -- Only directories are returned, never files.+ c "bar/" [dirR "bar/", dirR "bar/deep/"]+ c "bar/d" [dirR "bar/deep/"]+ c "bar/deep" [dirR "bar/deep/"]++ -- Parent directory paths (..)+ itWithOuter "can complete \"..\" to parent directories" $ \tdir -> do+ subdir <- resolveDir tdir "bar"+ withCurrentDir subdir $ do+ results <- unCompleter directoryPath ".."+ results+ `shouldBe` [dirR "../bar/"]+ itWithOuter "can complete \"../\" to parent directories" $ \tdir -> do+ subdir <- resolveDir tdir "bar"+ withCurrentDir subdir $ do+ results <- unCompleter directoryPath "../"+ results+ `shouldBe` [dirR "../bar/"]+ itWithOuter "can complete \"../b\" to matching parent directories" $ \tdir -> do+ subdir <- resolveDir tdir "bar"+ withCurrentDir subdir $ do+ results <- unCompleter directoryPath "../b"+ results+ `shouldBe` [dirR "../bar/"]++ -- Parent directory in the middle of a path+ itWithOuter "can complete \"bar/../\" to current dir directories" $ \tdir ->+ withCurrentDir tdir $ do+ results <- unCompleter directoryPath "bar/../"+ results+ `shouldBe` [dirR "bar/../bar/"]+ itWithOuter "can complete \"bar/../b\" to matching directories" $ \tdir ->+ withCurrentDir tdir $ do+ results <- unCompleter directoryPath "bar/../b"+ results+ `shouldBe` [dirR "bar/../bar/"]++ describe "filePathWithExtension" $ do+ let c :: (HasCallStack) => String -> [CompletionResult] -> TestDef '[Path Abs Dir] ()+ c s l = withFrozenCallStack $+ itWithOuter (unwords ["can complete", show s, "to", show (map completionResultValue l)]) $ \tdir ->+ withCurrentDir tdir $+ unCompleter (filePathWithExtension ".yaml") s `shouldReturn` l++ c "" [fileR "config.yaml", dirR "bar/"]+ c "c" [fileR "config.yaml"]+ c "b" [dirR "bar/"]+ c "bar" [dirR "bar/", dirR "bar/deep/"]++ describe "filePathWithExtensions" $ do+ let c :: (HasCallStack) => String -> [CompletionResult] -> TestDef '[Path Abs Dir] ()+ c s l = withFrozenCallStack $+ itWithOuter (unwords ["can complete", show s, "to", show (map completionResultValue l)]) $ \tdir ->+ withCurrentDir tdir $+ unCompleter (filePathWithExtensions [".txt", ".yaml"]) s `shouldReturn` l++ c "" [fileR "foo.txt", fileR "config.yaml", dirR "bar/"]+ c "bar/" [fileR "bar/quux.txt", dirR "bar/deep/"]++fileR :: String -> CompletionResult+fileR s =+ CompletionResult+ { completionResultValue = s,+ completionResultFinality = CompletionFinal+ }++dirR :: String -> CompletionResult+dirR s =+ CompletionResult+ { completionResultValue = s,+ completionResultFinality = CompletionNotFinal+ }
test/OptEnvConf/CompletionSpec.hs view
@@ -4,236 +4,1543 @@ module OptEnvConf.CompletionSpec (spec) where import Control.Applicative-import OptEnvConf.Completer-import OptEnvConf.Completion-import OptEnvConf.Parser-import OptEnvConf.Setting-import OptEnvConf.Test-import Path-import Test.Syd--spec :: Spec-spec = do- describe "bash" $ do- it "produces the same bash completion script" $- pureGoldenStringFile "test_resources/completion/bash-completion-script.bash" $- bashCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"- describe "zsh" $ do- it "produces the same zsh completion script" $- pureGoldenStringFile "test_resources/completion/zsh-completion-script.zsh" $- zshCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"- describe "fish" $ do- it "produces the same fish completion script" $- pureGoldenStringFile "test_resources/completion/fish-completion-script.fish" $- fishCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"-- describe "pureCompletionQuery" $ do- it "can complete a switch from nothing" $- parserCompletionTest- (setting [switch (), short 'e', long "example"])- 0- []- ["--example"] -- Only the long version- it "can complete a short switch from a single dash" $- parserCompletionTest- (setting [switch (), short 'e'])- 0- ["-"]- ["-e"]-- it "can complete a long switch from a single dash" $- parserCompletionTest- (setting [switch (), long "example"])- 0- ["-"]- ["--example"]-- it "can complete a long switch from a double dash" $- parserCompletionTest- (setting [switch (), long "example"])- 0- ["--"]- ["--example"]-- it "can complete a short option's dashed" $- parserCompletionTest- (setting [option, short 'e', completer $ listCompleter ["hi"]])- 0- []- ["-e"]-- it "can complete a long option's dashed" $- parserCompletionTest- (setting [option, long "example", completer $ listCompleter ["hi"]])- 0- []- ["--example"]-- it "can complete a short option with a separate arg" $- parserCompletionTest- (setting [option, short 'e', completer $ listCompleter ["hi"]])- 1- ["-e"]- ["hi"]-- -- Don't think we want to support this.- -- pending "can complete a short option in short-hand mode.-- it "can complete a long option" $- parserCompletionTest- (setting [option, long "example", completer $ listCompleter ["hi"]])- 1- ["--example"]- ["hi"]-- -- Don't think we want to support this- -- pending "can complete a long option with equals sign"-- it "can complete both switches of a tuple" $- parserCompletionTest- ((,) <$> setting [switch (), long "foo"] <*> setting [switch (), long "bar"])- 0- []- ["--foo", "--bar"]-- it "can complete both switches of a tuple, with a prefix" $- parserCompletionTest- ((,) <$> setting [switch (), long "bar"] <*> setting [switch (), long "baz"])- 0- ["--b"]- ["--bar", "--baz"]-- it "can complete both switches of an either" $- parserCompletionTest- (setting [switch (), long "foo"] <|> setting [switch (), long "bar"])- 0- []- ["--foo", "--bar"]-- it "can complete both switches of an either wrapped in optionals" $- parserCompletionTest- (optional (setting [switch (), long "foo"]) <|> optional (setting [switch (), long "bar"]))- 0- []- ["--foo", "--bar"]-- describe "commands" $ do- let p =- commands- [ command "foo" "1" $ pure (),- command "bar" "2" $ pure (),- command "baz" "3" $ pure ()- ]-- it "can complete a command argument" $- parserCompletionTest- p- 0- []- [Completion "foo" (Just "1"), Completion "bar" (Just "2"), Completion "baz" (Just "3")]-- it "can complete a command argument when it's been partially provided" $- parserCompletionTest- p- 0- ["b"]- [Completion "bar" (Just "2"), Completion "baz" (Just "3")]-- describe "completion after a command" $ do- it "can complete a command with a switch" $- parserCompletionTest- (commands [command "foo" "1" $ setting [help "ex", switch (), short 'e', long "example"]])- 1- ["foo"]- [Completion "--example" (Just "ex")] -- Only the long version- it "can complete a command's short switch" $- parserCompletionTest- (commands [command "foo" "1" $ setting [switch (), short 'e']])- 1- ["foo", "-"]- ["-e"]-- it "can complete a command's long switch from a single dash" $- parserCompletionTest- (commands [command "foo" "1" $ setting [switch (), long "example"]])- 1- ["foo", "-"]- ["--example"]-- it "can complete a command's long switch from a double dash" $- parserCompletionTest- (commands [command "foo" "1" $ setting [switch (), long "example"]])- 1- ["foo", "--"]- ["--example"]-- it "can complete a command's short option" $- parserCompletionTest- (commands [command "foo" "1" $ setting [option, short 'e', completer $ listCompleter ["hi"]]])- 2- ["foo", "-e"]- ["hi"]-- it "can complete a command's long option" $- parserCompletionTest- (commands [command "foo" "1" $ setting [option, long "example", completer $ listCompleter ["hi"]]])- 2- ["foo", "--example"]- ["hi"]-- it "can complete a file argument" $- parserCompletionDescriptionTest- (filePathSetting [help "file arg", argument])- 0- []- ["file arg"]-- it "can complete a file option" $- parserCompletionDescriptionTest- (filePathSetting [help "file arg", option, long "file"])- 1- ["--file"]- ["file arg"]-- it "can complete a directory argument" $- parserCompletionDescriptionTest- (directoryPathSetting [help "dir arg", argument])- 0- []- ["dir arg"]-- it "can complete a directory option" $- parserCompletionDescriptionTest- (directoryPathSetting [help "dir arg", option, long "file"])- 1- ["--file"]- ["dir arg"]-- it "no longer suggests a switch that has already been parsed" $- parserCompletionTest- ((,) <$> setting [switch (), long "foo"] <*> setting [switch (), long "bar"])- 1- ["--foo"]- ["--bar"]-- it "no longer suggests an option that has already been parsed" $- parserCompletionTest- ( (,,)- <$> setting [option, reader (str :: Reader String), long "foo"]- <*> setting [option, reader (str :: Reader String), long "bar"]- <*> setting [switch (), long "quux"]- )- 2- ["--foo", "foo"]- ["--bar", "--quux"]-- it "no longer suggests an argument that has already been parsed" $- parserCompletionDescriptionTest- ( (,,)- <$> setting [argument, reader (str :: Reader String), help "hi", completer $ listCompleter ["hi"]]- <*> setting [argument, reader (str :: Reader String), help "ho", completer $ listCompleter ["ho"]]- <*> setting [switch (), long "bar", help "hu"]- )- 1- ["foo"]- ["ho", "hu"]+import qualified Data.List.NonEmpty as NE+import Data.Maybe+import OptEnvConf.Completer+import OptEnvConf.Completion+import OptEnvConf.Parser+import OptEnvConf.Setting+import OptEnvConf.Test+import Path+import Path.IO+import Test.Syd++spec :: Spec+spec = do+ describe "bash" $ do+ it "produces the same bash completion script" $+ pureGoldenStringFile "test_resources/completion/bash-completion-script.bash" $+ bashCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"+ describe "zsh" $ do+ it "produces the same zsh completion script" $+ pureGoldenStringFile "test_resources/completion/zsh-completion-script.zsh" $+ zshCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"+ describe "fish" $ do+ it "produces the same fish completion script" $+ pureGoldenStringFile "test_resources/completion/fish-completion-script.fish" $+ fishCompletionScript [absfile|/usr/bin/example-executable|] "example-executable"++ describe "pureCompletionQuery" $ do+ it "can complete a switch from nothing" $+ parserCompletionTest+ (setting [switch (), short 'e', long "example"])+ 0+ []+ ["--example"] -- Only the long version+ it "can complete a short switch from a single dash" $+ parserCompletionTest+ (setting [switch (), short 'e'])+ 0+ ["-"]+ ["-e"]++ it "can complete a long switch from a single dash" $+ parserCompletionTest+ (setting [switch (), long "example"])+ 0+ ["-"]+ ["--example"]++ it "can complete a long switch from a double dash" $+ parserCompletionTest+ (setting [switch (), long "example"])+ 0+ ["--"]+ ["--example"]++ it "can complete a short option's dashed" $+ parserCompletionTest+ (setting [option, short 'e', completer $ listCompleter ["hi"]])+ 0+ []+ ["-e"]++ it "can complete a long option's dashed" $+ parserCompletionTest+ (setting [option, long "example", completer $ listCompleter ["hi"]])+ 0+ []+ ["--example"]++ it "can complete a short option with a separate arg" $+ parserCompletionTest+ (setting [option, short 'e', completer $ listCompleter ["hi"]])+ 1+ ["-e"]+ ["hi"]++ -- Don't think we want to support this.+ -- pending "can complete a short option in short-hand mode.++ it "can complete a long option" $+ parserCompletionTest+ (setting [option, long "example", completer $ listCompleter ["hi"]])+ 1+ ["--example"]+ ["hi"]++ -- Don't think we want to support this+ -- pending "can complete a long option with equals sign"++ it "can complete both switches of a tuple" $+ parserCompletionTest+ ((,) <$> setting [switch (), long "foo"] <*> setting [switch (), long "bar"])+ 0+ []+ [ "--foo",+ "--bar"+ ]++ it "can complete both switches of a tuple, with a prefix" $+ parserCompletionTest+ ((,) <$> setting [switch (), long "bar"] <*> setting [switch (), long "baz"])+ 0+ ["--b"]+ [ "--bar",+ "--baz"+ ]++ it "can complete both switches of an either" $+ parserCompletionTest+ (setting [switch (), long "foo"] <|> setting [switch (), long "bar"])+ 0+ []+ [ "--foo",+ "--bar"+ ]++ it "can complete both switches of an either wrapped in optionals" $+ parserCompletionTest+ (optional (setting [switch (), long "foo"]) <|> optional (setting [switch (), long "bar"]))+ 0+ []+ [ "--foo",+ "--bar"+ ]++ describe "commands" $ do+ let p =+ commands+ [ command "foo" "1" $ pure (),+ command "bar" "2" $ pure (),+ command "baz" "3" $ pure ()+ ]++ it "can complete a command argument" $+ parserCompletionTest+ p+ 0+ []+ [ Completion "foo" (Just "1"),+ Completion "bar" (Just "2"),+ Completion "baz" (Just "3")+ ]++ it "can complete a command argument when it's been partially provided" $+ parserCompletionTest+ p+ 0+ ["b"]+ [ Completion "bar" (Just "2"),+ Completion "baz" (Just "3")+ ]++ describe "completion after a command" $ do+ it "can complete a command with a switch" $+ parserCompletionTest+ (commands [command "foo" "1" $ setting [help "ex", switch (), short 'e', long "example"]])+ 1+ ["foo"]+ [Completion "--example" (Just "ex")] -- Only the long version+ it "can complete a command's short switch" $+ parserCompletionTest+ (commands [command "foo" "1" $ setting [switch (), short 'e']])+ 1+ ["foo", "-"]+ ["-e"]++ it "can complete a command's long switch from a single dash" $+ parserCompletionTest+ (commands [command "foo" "1" $ setting [switch (), long "example"]])+ 1+ ["foo", "-"]+ ["--example"]++ it "can complete a command's long switch from a double dash" $+ parserCompletionTest+ (commands [command "foo" "1" $ setting [switch (), long "example"]])+ 1+ ["foo", "--"]+ ["--example"]++ it "can complete a command's short option" $+ parserCompletionTest+ (commands [command "foo" "1" $ setting [option, short 'e', completer $ listCompleter ["hi"]]])+ 2+ ["foo", "-e"]+ ["hi"]++ it "can complete a command's long option" $+ parserCompletionTest+ (commands [command "foo" "1" $ setting [option, long "example", completer $ listCompleter ["hi"]]])+ 2+ ["foo", "--example"]+ ["hi"]++ it "can complete a file argument" $+ parserCompletionDescriptionTest+ (filePathSetting [help "file arg", argument])+ 0+ []+ ["file arg"]++ it "can complete a file option" $+ parserCompletionDescriptionTest+ (filePathSetting [help "file arg", option, long "file"])+ 1+ ["--file"]+ ["file arg"]++ it "can complete a directory argument" $+ parserCompletionDescriptionTest+ (directoryPathSetting [help "dir arg", argument])+ 0+ []+ ["dir arg"]++ it "can complete a directory option" $+ parserCompletionDescriptionTest+ (directoryPathSetting [help "dir arg", option, long "file"])+ 1+ ["--file"]+ ["dir arg"]++ it "no longer suggests a switch that has already been parsed" $+ parserCompletionTest+ ((,) <$> setting [switch (), long "foo"] <*> setting [switch (), long "bar"])+ 1+ ["--foo"]+ ["--bar"]++ it "no longer suggests an option that has already been parsed" $+ parserCompletionTest+ ( (,,)+ <$> setting [option, reader (str :: Reader String), long "foo"]+ <*> setting [option, reader (str :: Reader String), long "bar"]+ <*> setting [switch (), long "quux"]+ )+ 2+ ["--foo", "foo"]+ [ "--bar",+ "--quux"+ ]++ it "no longer suggests an argument that has already been parsed" $+ parserCompletionDescriptionTest+ ( (,,)+ <$> setting [argument, reader (str :: Reader String), help "hi", completer $ listCompleter ["hi"]]+ <*> setting [argument, reader (str :: Reader String), help "ho", completer $ listCompleter ["ho"]]+ <*> setting [switch (), long "bar", help "hu"]+ )+ 1+ ["foo"]+ [ "ho",+ "hu"+ ]++ describe "commands with a default command" $ do+ let p =+ commands+ [ command "foo" "1" $ pure (),+ command "bar" "2" $ pure (),+ command "baz" "3" $ pure (),+ defaultCommand "bar"+ ]++ it "still lists all commands when there is a default" $+ parserCompletionTest+ p+ 0+ []+ [ Completion "foo" (Just "1"),+ Completion "bar" (Just "2"),+ Completion "baz" (Just "3")+ ]++ it "still filters commands by prefix when there is a default" $+ parserCompletionTest+ p+ 0+ ["b"]+ [ Completion "bar" (Just "2"),+ Completion "baz" (Just "3")+ ]++ it "completes the default command's switch when no command is given" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $ setting [switch (), long "example"],+ command "bar" "2" $ pure (),+ defaultCommand "foo"+ ]+ )+ 0+ ["--"]+ ["--example"]++ it "completes both commands and the default command's switch" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $ setting [switch (), long "example"],+ command "bar" "2" $ pure (),+ defaultCommand "foo"+ ]+ )+ 0+ []+ [ Completion "foo" (Just "1"),+ Completion "bar" (Just "2"),+ "--example"+ ]++ it "completes the default command's option when no command is given" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $ setting [option, long "example", completer $ listCompleter ["hi"]],+ command "bar" "2" $ pure (),+ defaultCommand "foo"+ ]+ )+ 0+ ["--"]+ ["--example"]++ it "completes the default command's option value when no command is given" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $ setting [option, long "example", reader (str :: Reader String), completer $ listCompleter ["hi"]],+ command "bar" "2" $ pure "bar",+ defaultCommand "foo"+ ]+ )+ 1+ ["--example"]+ ["hi"]++ it "completes the default command's argument when no command is given" $+ parserCompletionDescriptionTest+ ( commands+ [ command "foo" "1" $ setting [argument, reader (str :: Reader String), help "arg help", completer $ listCompleter ["val"]],+ command "bar" "2" $ pure "bar",+ defaultCommand "foo"+ ]+ )+ 0+ []+ -- Should include the default command's argument completer along with command names+ [ "1",+ "2",+ "arg help"+ ]++ it "completes inside the default command after consuming its switch" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $+ (,)+ <$> setting [switch (), long "first"]+ <*> setting [switch (), long "second"],+ command "bar" "2" $ pure ((), ()),+ defaultCommand "foo"+ ]+ )+ 1+ ["--first"]+ ["--second"]++ it "completes inside the default command after consuming its option" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $+ (,)+ <$> setting [option, reader (str :: Reader String), long "name"]+ <*> setting [switch (), long "verbose"],+ command "bar" "2" $ pure ("bar", ()),+ defaultCommand "foo"+ ]+ )+ 2+ ["--name", "hello"]+ ["--verbose"]++ describe "global options with commands and a default" $ do+ it "completes global options and commands together" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> commands+ [ command "foo" "1" $ pure (),+ command "bar" "2" $ pure (),+ defaultCommand "foo"+ ]+ )+ 0+ []+ [ "--verbose",+ Completion "foo" (Just "1"),+ Completion "bar" (Just "2")+ ]++ it "completes commands after a global option" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> commands+ [ command "foo" "1" $ pure (),+ command "bar" "2" $ pure (),+ defaultCommand "foo"+ ]+ )+ 1+ ["--verbose"]+ [ Completion "foo" (Just "1"),+ Completion "bar" (Just "2")+ ]++ it "completes the default command's options after a global option" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> commands+ [ command "foo" "1" $ setting [switch (), long "example"],+ command "bar" "2" $ pure (),+ defaultCommand "foo"+ ]+ )+ 1+ ["--verbose"]+ [ Completion "foo" (Just "1"),+ Completion "bar" (Just "2"),+ "--example"+ ]++ describe "many" $ do+ it "can complete repeated switches" $+ parserCompletionTest+ (length <$> many (setting [switch (), long "verbose", short 'v']))+ 0+ []+ ["--verbose"]++ it "can still complete after consuming one" $+ parserCompletionTest+ (length <$> many (setting [switch (), long "verbose", short 'v']))+ 1+ ["--verbose"]+ ["--verbose"]++ it "can still complete after consuming two" $+ parserCompletionTest+ (length <$> many (setting [switch (), long "verbose", short 'v']))+ 2+ ["-v", "-v"]+ ["--verbose"]++ it "can complete repeated options" $+ parserCompletionTest+ ( many+ ( setting+ [ option,+ reader (str :: Reader String),+ long "include",+ completer $ listCompleter ["foo", "bar"]+ ]+ )+ )+ 1+ ["--include"]+ [ "foo",+ "bar"+ ]++ it "can complete repeated options after one has already been parsed" $+ parserCompletionTest+ ( many+ ( setting+ [ option,+ reader (str :: Reader String),+ long "include",+ completer $ listCompleter ["foo", "bar"]+ ]+ )+ )+ 3+ [ "--include",+ "foo",+ "--include"+ ]+ [ "foo",+ "bar"+ ]++ describe "some" $ do+ it "can complete at least one switch" $+ parserCompletionTest+ (NE.length <$> someNonEmpty (setting [switch (), long "verbose", short 'v']))+ 0+ []+ ["--verbose"]++ it "can still complete after consuming one" $+ parserCompletionTest+ (NE.length <$> someNonEmpty (setting [switch (), long "verbose", short 'v']))+ 1+ ["--verbose"]+ ["--verbose"]++ describe "allOrNothing" $ do+ it "completes normally inside allOrNothing" $+ parserCompletionTest+ ( allOrNothing $+ (,)+ <$> setting [option, reader (str :: Reader String), long "host"]+ <*> setting [option, reader (str :: Reader String), long "port"]+ )+ 0+ []+ [ "--host",+ "--port"+ ]++ it "still suggests the remaining option after one is consumed" $+ parserCompletionTest+ ( allOrNothing $+ (,)+ <$> setting [option, reader (str :: Reader String), long "host"]+ <*> setting [option, reader (str :: Reader String), long "port"]+ )+ 2+ ["--host", "localhost"]+ ["--port"]++ describe "check" $ do+ it "completes through a checkMapEither" $+ parserCompletionTest+ ( checkMapEither+ (\s -> if null s then Left "empty" else Right s)+ (setting [argument, reader (str :: Reader String), completer $ listCompleter ["hello"]])+ )+ 0+ []+ ["hello"]++ it "completes through a mapIO" $+ parserCompletionTest+ ( mapIO pure $+ setting [switch (), long "example"]+ )+ 0+ []+ ["--example"]++ describe "hidden settings" $ do+ it "does not suggest hidden switches" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "visible"]+ <*> setting [switch (), long "secret", hidden]+ )+ 0+ []+ ["--visible"]++ it "does not suggest hidden options" $+ parserCompletionTest+ ( (,)+ <$> setting [option, reader (str :: Reader String), long "visible"]+ <*> setting [option, reader (str :: Reader String), long "secret", hidden]+ )+ 0+ []+ ["--visible"]++ describe "settings with only env or conf" $ do+ it "does not error on env-only settings combined with arg settings" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> setting [reader (str :: Reader String), env "MY_VAR"]+ )+ 0+ []+ ["--verbose"]++ describe "enableDisableSwitch" $ do+ -- enableDisableSwitch uses a visible "dummy" with the+ -- (enable|disable)- prefix for documentation, while the actual+ -- --enable-feature and --disable-feature switches are hidden.+ it "completes the documented dummy flag" $+ parserCompletionTest+ ( enableDisableSwitch+ [ help "enable feature",+ name "feature",+ value False+ ]+ )+ 0+ []+ [Completion "--(enable|disable)-feature" (Just "enable feature")]++ it "filters the documented flag by prefix" $+ parserCompletionTest+ ( enableDisableSwitch+ [ help "enable feature",+ name "feature",+ value False+ ]+ )+ 0+ ["--(e"]+ [Completion "--(enable|disable)-feature" (Just "enable feature")]++ describe "choice" $ do+ it "completes across all choices" $+ parserCompletionTest+ ( choice+ [ setting [switch (), long "alpha"],+ setting [switch (), long "beta"],+ setting [switch (), long "gamma"]+ ]+ )+ 0+ []+ [ "--alpha",+ "--beta",+ "--gamma"+ ]++ describe "withDefault" $ do+ it "completes the underlying parser" $+ parserCompletionTest+ (withDefault "default" $ setting [option, reader (str :: Reader String), long "name", completer $ listCompleter ["alice", "bob"]])+ 1+ ["--name"]+ [ "alice",+ "bob"+ ]++ it "still suggests the option when it has a default" $+ parserCompletionTest+ (withDefault "default" $ setting [option, reader (str :: Reader String), long "name"])+ 0+ []+ ["--name"]++ describe "nested commands" $ do+ let p =+ commands+ [ command "top" "top-level" $+ commands+ [ command "sub-a" "sub a" $ pure (),+ command "sub-b" "sub b" $ pure ()+ ],+ command "other" "other" $ pure ()+ ]++ it "completes top-level commands" $+ parserCompletionTest+ p+ 0+ []+ [ Completion "top" (Just "top-level"),+ Completion "other" (Just "other")+ ]++ it "completes sub-commands after selecting a top-level command" $+ parserCompletionTest+ p+ 1+ ["top"]+ [ Completion "sub-a" (Just "sub a"),+ Completion "sub-b" (Just "sub b")+ ]++ it "filters sub-commands by prefix" $+ parserCompletionTest+ p+ 1+ ["top", "sub-a"]+ [Completion "sub-a" (Just "sub a")]++ it "completes nested default commands" $+ parserCompletionTest+ ( commands+ [ command "top" "top-level" $+ commands+ [ command "sub-a" "sub a" $ setting [switch (), long "flag"],+ command "sub-b" "sub b" $ pure (),+ defaultCommand "sub-a"+ ],+ command "other" "other" $ pure ()+ ]+ )+ 1+ ["top"]+ [ Completion "sub-a" (Just "sub a"),+ Completion "sub-b" (Just "sub b"),+ "--flag"+ ]++ describe "commands merged via alternative" $ do+ it "completes commands from both sides of an alternative" $+ parserCompletionTest+ ( commands [command "foo" "1" $ pure ()]+ <|> commands [command "bar" "2" $ pure ()]+ )+ 0+ []+ [ Completion "foo" (Just "1"),+ Completion "bar" (Just "2")+ ]++ describe "arguments after double dash" $ do+ -- A bare -- is consumed as the argument value "--" when it's+ -- the only argument remaining, so completion sees nothing left.+ it "consumes -- as the argument value" $+ parserCompletionTest+ (setting [argument, reader (str :: Reader String), completer $ listCompleter ["file1", "file2"]])+ 1+ ["--"]+ []++ -- After --, the completion should suggest positional arguments+ -- rather than switches. Currently it does not: the switch is+ -- still suggested because the completion engine does not+ -- account for -- separating options from arguments.+ it "suggests the switch even after -- (known bug)" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file1", "file2"]]+ )+ 2+ ["--", "fi"]+ ["--verbose"]++ describe "mixed arguments and options" $ do+ it "can complete an option after a positional argument" $+ parserCompletionTest+ ( (,)+ <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["input"]]+ <*> setting [switch (), long "verbose"]+ )+ 1+ ["input"]+ ["--verbose"]++ it "can complete a positional after an option" $+ parserCompletionDescriptionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> setting [argument, reader (str :: Reader String), help "file", completer $ listCompleter ["output"]]+ )+ 1+ ["--verbose"]+ ["file"]++ describe "multiple arguments" $ do+ -- When no args have been typed, both argument completers fire+ -- because the completion engine doesn't know which one will be+ -- consumed first. This is the documented TODO in Completion.hs.+ it "completes both arguments when none have been typed" $+ parserCompletionTest+ ( (,)+ <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["src"]]+ <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["dst"]]+ )+ 0+ []+ [ "src",+ "dst"+ ]++ it "completes the second argument after the first" $+ parserCompletionTest+ ( (,)+ <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["src"]]+ <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["dst"]]+ )+ 1+ ["something"]+ ["dst"]++ describe "empty parser" $ do+ it "produces no completions for empty" $+ parserCompletionTest+ (empty :: Parser ())+ 0+ []+ []++ describe "pure parser" $ do+ it "produces no completions for pure" $+ parserCompletionTest+ (pure () :: Parser ())+ 0+ []+ []++ describe "select" $ do+ -- Select uses andCompletions: both branches contribute because+ -- the completion engine cannot know at completion time whether+ -- the first parser will return Left or Right.+ it "completes through a select" $+ parserCompletionTest+ ( select+ (Left <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["hello"]])+ (const <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["world"]])+ )+ 0+ []+ [ "hello",+ "world"+ ]++ describe "withConfig" $ do+ it "completes the main parser through withConfig" $+ parserCompletionTest+ ( withConfig (pure Nothing) $+ setting [switch (), long "example"]+ )+ 0+ []+ ["--example"]++ it "completes the config file option together with the main parser" $+ parserCompletionTest+ ( withConfig+ (Nothing <$ setting [option, reader (str :: Reader String), long "config-file", completer $ listCompleter ["config.yaml"]])+ (setting [switch (), long "verbose"])+ )+ 0+ []+ -- Main parser completions come first (p2), then config parser completions (p1)+ [ "--verbose",+ "--config-file"+ ]++ it "completes the config file value along with main parser suggestions" $+ parserCompletionTest+ ( withConfig+ (Nothing <$ setting [option, reader (str :: Reader String), long "config-file", completer $ listCompleter ["config.yaml"]])+ (setting [switch (), long "verbose"])+ )+ 1+ ["--config-file"]+ -- The main parser (p2) still suggests --verbose since it hasn't+ -- consumed anything, and the config parser (p1) offers the completer.+ [ "--verbose",+ "config.yaml"+ ]++ it "completes the main parser after the config option is consumed" $+ parserCompletionTest+ ( withConfig+ (Nothing <$ setting [option, reader (str :: Reader String), long "config-file", completer $ listCompleter ["config.yaml"]])+ (setting [switch (), long "verbose"])+ )+ 2+ ["--config-file", "config.yaml"]+ ["--verbose"]++ describe "optional" $ do+ it "completes through an optional parser" $+ parserCompletionTest+ (optional $ setting [switch (), long "verbose"])+ 0+ []+ ["--verbose"]++ it "completes an optional with an applicative" $+ parserCompletionTest+ ( (,)+ <$> optional (setting [option, reader (str :: Reader String), long "name", completer $ listCompleter ["alice"]])+ <*> setting [switch (), long "verbose"]+ )+ 0+ []+ [ "--name",+ "--verbose"+ ]++ it "completes the second part when the optional is skipped" $+ parserCompletionTest+ ( (,)+ <$> optional (setting [option, reader (str :: Reader String), long "name"])+ <*> setting [switch (), long "verbose"]+ )+ 0+ []+ [ "--name",+ "--verbose"+ ]++ describe "many with other parsers" $ do+ it "completes both many and a following switch" $+ parserCompletionTest+ ( (,)+ <$> many (setting [option, reader (str :: Reader String), long "include", completer $ listCompleter ["foo"]])+ <*> setting [switch (), long "verbose"]+ )+ 0+ []+ [ "--include",+ "--verbose"+ ]++ it "completes both after consuming one many-option" $+ parserCompletionTest+ ( (,)+ <$> many (setting [option, reader (str :: Reader String), long "include", completer $ listCompleter ["foo"]])+ <*> setting [switch (), long "verbose"]+ )+ 2+ ["--include", "foo"]+ [ "--include",+ "--verbose"+ ]++ it "completes many switches combined with a command" $+ parserCompletionTest+ ( (,)+ <$> many (setting [switch (), long "verbose", short 'v'])+ <*> commands+ [ command "run" "run it" $ pure (),+ command "build" "build it" $ pure ()+ ]+ )+ 0+ []+ [ "--verbose",+ Completion "run" (Just "run it"),+ Completion "build" (Just "build it")+ ]++ it "completes commands after consuming many switches" $+ parserCompletionTest+ ( (,)+ <$> many (setting [switch (), long "verbose", short 'v'])+ <*> commands+ [ command "run" "run it" $ pure (),+ command "build" "build it" $ pure ()+ ]+ )+ 2+ ["-v", "-v"]+ [ "--verbose",+ Completion "run" (Just "run it"),+ Completion "build" (Just "build it")+ ]++ describe "folded short switches" $ do+ it "can complete after a folded short switch is consumed" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), short 'v']+ <*> setting [switch (), short 'n']+ )+ 1+ ["-v"]+ ["-n"]++ it "no longer suggests a switch consumed via folding" $+ parserCompletionTest+ ( (,,)+ <$> setting [switch (), short 'v']+ <*> setting [switch (), short 'n']+ <*> setting [switch (), short 'x']+ )+ 1+ ["-vn"]+ ["-x"]++ describe "short option shorthand" $ do+ it "completes after a short option consumed in shorthand" $+ parserCompletionTest+ ( (,)+ <$> setting [option, reader (str :: Reader String), short 'f']+ <*> setting [switch (), long "verbose"]+ )+ 1+ ["-ffoo.txt"]+ ["--verbose"]++ describe "deeply nested parsers" $ do+ it "completes through optional + many + applicative" $+ parserCompletionTest+ ( (,,)+ <$> optional (setting [option, reader (str :: Reader String), long "config"])+ <*> many (setting [switch (), long "verbose", short 'v'])+ <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file.txt"]]+ )+ 0+ []+ [ "--config",+ "--verbose",+ "file.txt"+ ]++ it "completes correctly after all optional+many consumed" $+ parserCompletionTest+ ( (,,)+ <$> optional (setting [option, reader (str :: Reader String), long "config"])+ <*> many (setting [switch (), long "verbose", short 'v'])+ <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file.txt"]]+ )+ 3+ [ "--config",+ "foo",+ "--verbose"+ ]+ [ "--verbose",+ "file.txt"+ ]++ describe "commands with options inside" $ do+ it "completes a command's many options" $+ parserCompletionTest+ ( commands+ [ command "run" "run it" $+ many (setting [option, reader (str :: Reader String), long "arg", completer $ listCompleter ["val"]])+ ]+ )+ 2+ ["run", "--arg"]+ ["val"]++ it "completes a command's many options after one is consumed" $+ parserCompletionTest+ ( commands+ [ command "run" "run it" $+ many (setting [option, reader (str :: Reader String), long "arg", completer $ listCompleter ["val"]])+ ]+ )+ 3+ [ "run",+ "--arg",+ "val"+ ]+ ["--arg"]++ describe "multiple commands with shared options" $ do+ it "completes options in the selected command only" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $ setting [switch (), long "alpha"],+ command "bar" "2" $ setting [switch (), long "beta"]+ ]+ )+ 1+ ["foo"]+ ["--alpha"]++ it "does not leak options from other commands" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $ setting [switch (), long "alpha"],+ command "bar" "2" $ setting [switch (), long "beta"]+ ]+ )+ 1+ ["bar"]+ ["--beta"]++ describe "alternative branch failures" $ do+ -- A parser that is not detected as isEmpty but still fails during+ -- completion (andCompletions fails because the inner empty fails).+ -- This avoids the (<|>) optimization that removes empty alternatives.+ let failingP = (empty :: Parser ()) *> pure ()+ it "restores state when first branch fails in an alternative" $+ parserCompletionTest+ (failingP <|> setting [switch (), long "foo"])+ 0+ []+ ["--foo"]++ it "returns first branch completions when second branch fails" $+ parserCompletionTest+ (setting [switch (), long "foo"] <|> failingP)+ 0+ []+ ["--foo"]++ it "returns no completions when both alternatives fail" $+ parserCompletionTest+ (failingP <|> failingP)+ 0+ []+ []++ describe "applicative branch failures" $ do+ it "returns nothing when first applicative branch fails" $+ parserCompletionTest+ ((,) <$> ((empty :: Parser ()) *> pure ()) <*> setting [switch (), long "foo"])+ 0+ []+ []++ it "returns nothing when second applicative branch fails" $+ parserCompletionTest+ ((,) <$> setting [switch (), long "foo"] <*> ((empty :: Parser String) *> pure ("" :: String)))+ 0+ []+ []++ describe "many edge cases" $ do+ it "handles many where the inner parser fails" $+ parserCompletionTest+ (many ((empty :: Parser ()) *> pure ()))+ 0+ []+ []++ it "returns first iteration completions when recursive call fails" $+ parserCompletionTest+ (many (commands [command "foo" "1" $ pure ()]))+ 1+ ["foo"]+ [Completion "foo" (Just "1")]++ describe "command not found" $ do+ it "returns no completions for an unknown command" $+ parserCompletionTest+ (commands [command "foo" "1" $ pure (), command "bar" "2" $ pure ()])+ 1+ ["unknown"]+ []++ describe "argument completer at end" $ do+ it "offers the argument completer at the end after a switch is consumed" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file"]]+ )+ 1+ ["--verbose"]+ ["file"]++ it "does not offer option completer when not at end of args" $+ parserCompletionTest+ ( (,,)+ <$> setting [switch (), long "other"]+ <*> setting [option, reader (str :: Reader String), long "name", completer $ listCompleter ["val"]]+ <*> setting [switch (), long "extra"]+ )+ 3+ [ "--name",+ "--other",+ "--extra"+ ]+ []++ describe "option with different dashed in args" $ do+ it "does not suggest an option when a different dashed is in the args" $+ parserCompletionTest+ ( (,)+ <$> setting [option, reader (str :: Reader String), long "output", completer $ listCompleter ["file.txt"]]+ <*> setting [option, reader (str :: Reader String), long "input", completer $ listCompleter ["data.csv"]]+ )+ 1+ ["--input"]+ ["data.csv"]++ describe "default command parser failure" $ do+ let emptyS = empty :: Parser String+ it "falls back to explicit commands when default command parser fails" $+ parserCompletionTest+ ( commands+ [ command "foo" "1" $ (,) <$> setting [argument, reader (str :: Reader String)] <*> emptyS,+ command "bar" "2" $ pure ("", ""),+ defaultCommand "foo"+ ]+ )+ 0+ []+ [ Completion "foo" (Just "1"),+ Completion "bar" (Just "2")+ ]++ describe "prefix filtering" $ do+ it "filters switches by typed prefix" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> setting [switch (), long "version"]+ )+ 0+ ["--verb"]+ ["--verbose"]++ it "returns nothing when prefix matches nothing" $+ parserCompletionTest+ (setting [switch (), long "verbose"])+ 0+ ["--xyz"]+ []++ it "filters commands by typed prefix" $+ parserCompletionDescriptionTest+ ( commands+ [ command "generate" "gen" $ pure (),+ command "get" "getter" $ pure (),+ command "build" "builder" $ pure ()+ ]+ )+ 0+ ["ge"]+ [ "gen",+ "getter"+ ]++ describe "argument completion with dashed values" $ do+ -- When a dashed-looking value is provided as a positional argument,+ -- the completion engine treats it as a potential switch (first+ -- consumeArgument possibility) instead of consuming it. This+ -- causes the argument's completer to fire even though a value was+ -- already provided.+ it "should not offer the argument completer after a dashed value is consumed" $+ parserCompletionTest+ (setting [argument, reader (str :: Reader String), completer $ listCompleter ["file"]])+ 1+ ["--foo"]+ []++ -- Same issue in a tuple: the first argument's completer fires+ -- alongside the second's, even though the first argument already+ -- has a value.+ it "should only complete the second argument after a dashed first argument" $+ parserCompletionTest+ ( (,)+ <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["file"]]+ <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["dest"]]+ )+ 1+ ["--foo"]+ ["dest"]++ describe "partial option value completion" $ do+ it "filters option values by typed prefix" $+ parserCompletionTest+ ( setting+ [ option,+ reader (str :: Reader String),+ long "name",+ completer $ listCompleter ["alice", "bob"]+ ]+ )+ 1+ ["--name", "al"]+ ["alice"]++ it "returns all option values when prefix is empty" $+ parserCompletionTest+ ( setting+ [ option,+ reader (str :: Reader String),+ long "name",+ completer $ listCompleter ["alice", "bob"]+ ]+ )+ 1+ ["--name", ""]+ [ "alice",+ "bob"+ ]++ describe "partial argument completion" $ do+ it "filters argument values by typed prefix" $+ parserCompletionTest+ ( setting+ [ argument,+ reader (str :: Reader String),+ completer $ listCompleter ["file1", "file2", "data"]+ ]+ )+ 0+ ["fi"]+ [ "file1",+ "file2"+ ]++ it "filters argument values by a different prefix" $+ parserCompletionTest+ ( setting+ [ argument,+ reader (str :: Reader String),+ completer $ listCompleter ["file1", "file2", "data"]+ ]+ )+ 0+ ["da"]+ ["data"]++ describe "partial command name with inner option completion" $ do+ it "completes a command's options after selecting by prefix" $+ parserCompletionTest+ ( commands+ [ command "deploy" "deploy it" $ setting [switch (), long "force"],+ command "debug" "debug it" $ pure ()+ ]+ )+ 1+ ["deploy"]+ ["--force"]++ describe "completion at a middle index" $ do+ it "completes the value of an option at its value position with a matching prefix" $+ parserCompletionTest+ ( (,)+ <$> setting+ [ option,+ reader (str :: Reader String),+ long "foo",+ completer $ listCompleter ["val1", "val2"]+ ]+ <*> setting [switch (), long "bar"]+ )+ 1+ ["--foo", "v", "--bar"]+ [ "val1",+ "val2"+ ]++ it "filters completions when cursor word narrows the match" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "alpha"]+ <*> setting [switch (), long "beta"]+ )+ 0+ ["--alpha", "--beta"]+ ["--alpha"]++ describe "partial long option prefix with multiple matches" $ do+ it "filters options by a short prefix matching multiple" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> setting [switch (), long "version"]+ )+ 0+ ["--ver"]+ [ "--verbose",+ "--version"+ ]++ it "filters options by a longer prefix matching one" $+ parserCompletionTest+ ( (,)+ <$> setting [switch (), long "verbose"]+ <*> setting [switch (), long "version"]+ )+ 0+ ["--verb"]+ ["--verbose"]++ describe "partial argument completion after consuming earlier args" $ do+ it "filters the second argument's completer by prefix after the first is consumed" $+ parserCompletionTest+ ( (,)+ <$> setting [argument, reader (str :: Reader String), completer $ listCompleter ["src"]]+ <*> setting [argument, reader (str :: Reader String), completer $ listCompleter ["dst", "data"]]+ )+ 1+ ["src", "ds"]+ ["dst"]++ describe "commands with partial option value inside" $ do+ it "filters a command's option values by prefix" $+ parserCompletionTest+ ( commands+ [ command "run" "run it" $+ setting+ [ option,+ reader (str :: Reader String),+ long "output",+ completer $ listCompleter ["file.txt", "final.out", "data.csv"]+ ]+ ]+ )+ 2+ ["run", "--output", "fi"]+ [ "file.txt",+ "final.out"+ ]++ describe "many with partial values" $ do+ it "filters many option values by prefix" $+ parserCompletionTest+ ( many+ ( setting+ [ option,+ reader (str :: Reader String),+ long "include",+ completer $ listCompleter ["foo", "bar"]+ ]+ )+ )+ 1+ ["--include", "fo"]+ ["foo"]++ it "filters many option values by prefix after one is consumed" $+ parserCompletionTest+ ( many+ ( setting+ [ option,+ reader (str :: Reader String),+ long "include",+ completer $ listCompleter ["foo", "bar"]+ ]+ )+ )+ 3+ ["--include", "foo", "--include", "ba"]+ ["bar"]++ sequential . doNotRandomiseExecutionOrder+ $ aroundAll+ ( \func -> withSystemTempDir "completion-spec" $ \tdir -> do+ exampleFile <- resolveFile tdir "example.txt"+ writeFile (fromAbsFile exampleFile) ""+ exampleDir <- resolveDir tdir "exampledir"+ createDir exampleDir+ nested <- resolveFile exampleDir "nested.txt"+ writeFile (fromAbsFile nested) ""+ func tdir+ )+ $ do+ let evalQuery p ix ws tdir = withCurrentDir tdir $ do+ let arg = fromMaybe "" $ listToMaybe $ drop ix ws+ let completions = pureCompletionQuery p ix ws+ evalCompletions arg completions+ suggestionValues = map (completionResultValue . completionSuggestion)++ describe "file and directory completion" $ do+ describe "filePathSetting" $ do+ itWithOuter "completes files and directories for a file argument" $ \tdir -> do+ results <- evalQuery (filePathSetting [help "f", argument]) 0 [] tdir+ suggestionValues results `shouldBe` ["example.txt", "exampledir/"]++ itWithOuter "completes files and directories after a file option's dashed" $ \tdir -> do+ results <- evalQuery (filePathSetting [help "f", option, long "file"]) 1 ["--file"] tdir+ suggestionValues results `shouldBe` ["example.txt", "exampledir/"]++ itWithOuter "filters by prefix" $ \tdir -> do+ results <- evalQuery (filePathSetting [help "f", argument]) 0 ["exampledi"] tdir+ suggestionValues results `shouldBe` ["exampledir/"]++ -- Directories end in /, files do not. This convention is how+ -- shells decide whether to append a trailing space after a+ -- completion. The exact values are checked by+ -- "completes files and directories for a file argument" above;+ -- this test re-states the invariant explicitly.+ itWithOuter "directories end in / and files do not" $ \tdir -> do+ results <- evalQuery (filePathSetting [help "f", argument]) 0 [] tdir+ suggestionValues results `shouldBe` ["example.txt", "exampledir/"]++ itWithOuter "files are final and directories are not final" $ \tdir -> do+ results <- evalQuery (filePathSetting [help "f", argument]) 0 [] tdir+ map (completionResultFinality . completionSuggestion) results+ `shouldBe` [CompletionFinal, CompletionNotFinal]++ itWithOuter "carries the description through to file completions" $ \tdir -> do+ results <- evalQuery (filePathSetting [help "my file", argument]) 0 [] tdir+ map completionDescription results `shouldBe` [Just "my file", Just "my file"]++ describe "directoryPathSetting" $ do+ itWithOuter "completes only directories for a directory argument" $ \tdir -> do+ results <- evalQuery (directoryPathSetting [help "d", argument]) 0 [] tdir+ suggestionValues results `shouldBe` ["exampledir/"]++ itWithOuter "completes only directories after a directory option's dashed" $ \tdir -> do+ results <- evalQuery (directoryPathSetting [help "d", option, long "dir"]) 1 ["--dir"] tdir+ suggestionValues results `shouldBe` ["exampledir/"]++ itWithOuter "never includes files" $ \tdir -> do+ results <- evalQuery (directoryPathSetting [help "d", argument]) 0 [] tdir+ suggestionValues results `shouldBe` ["exampledir/"]++ itWithOuter "directories are not final" $ \tdir -> do+ results <- evalQuery (directoryPathSetting [help "d", argument]) 0 [] tdir+ map (completionResultFinality . completionSuggestion) results+ `shouldBe` [CompletionNotFinal]++ itWithOuter "carries the description through to directory completions" $ \tdir -> do+ results <- evalQuery (directoryPathSetting [help "my dir", argument]) 0 [] tdir+ map completionDescription results `shouldBe` [Just "my dir"]++ describe "combined with other settings" $ do+ itWithOuter "includes file completions alongside other options" $ \tdir -> do+ let parser =+ (,)+ <$> filePathSetting [help "f", option, long "file"]+ <*> setting [switch (), long "verbose"]+ results <- evalQuery parser 1 ["--file"] tdir+ suggestionValues results `shouldBe` ["example.txt", "exampledir/", "--verbose"]++ itWithOuter "includes directory completions alongside other options" $ \tdir -> do+ let parser =+ (,)+ <$> directoryPathSetting [help "d", option, long "dir"]+ <*> setting [switch (), long "verbose"]+ results <- evalQuery parser 1 ["--dir"] tdir+ suggestionValues results `shouldBe` ["exampledir/", "--verbose"]++ -- After typing the dashed of an option that takes a value, the+ -- cursor is in the value position. The option's completer+ -- should fire first, then other options should follow.+ --+ -- Shell sends index pointing at the empty cursor word:+ -- exe --dir <tab> -> index=1, words=["--dir", ""]+ itWithOuter "completes directory option value first, then other options" $ \tdir -> do+ let parser =+ (,)+ <$> directoryPathSetting [help "d", option, long "dir"]+ <*> setting [switch (), long "verbose"]+ results <- evalQuery parser 1 ["--dir", ""] tdir+ suggestionValues results `shouldBe` ["exampledir/", "--verbose"]++ itWithOuter "completes file option value first, then other options" $ \tdir -> do+ let parser =+ (,)+ <$> filePathSetting [help "f", option, long "file"]+ <*> setting [switch (), long "verbose"]+ results <- evalQuery parser 1 ["--file", ""] tdir+ suggestionValues results `shouldBe` ["example.txt", "exampledir/", "--verbose"]++ -- A default command with an argument combined with a+ -- directory option causes the directory completer not to fire+ -- when completing the option's value.+ itWithOuter "completes directory option value alongside default command with argument" $ \tdir -> do+ let parser =+ (,)+ <$> commands+ [ command "file" "f" $+ setting [help "f", argument, reader (str :: Reader String)],+ defaultCommand "file"+ ]+ <*> directoryPathSetting [help "a", option, long "archive-dir"]+ results <- evalQuery parser 1 ["--archive-dir", ""] tdir+ suggestionValues results `shouldSatisfy` elem "exampledir/"
test/OptEnvConf/RunSpec.hs view
@@ -8,6 +8,7 @@ import Autodocodec import Control.Applicative import Control.Concurrent+import Control.Monad (void) import Data.Aeson as JSON (Object, Value (Null), toJSON) import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KeyMap@@ -25,6 +26,7 @@ import qualified OptEnvConf.EnvMap as EnvMap import OptEnvConf.EnvMap.Gen () import OptEnvConf.Error+import qualified System.Timeout as Timeout import Test.Syd import Test.Syd.Validity import Text.Colour@@ -129,6 +131,45 @@ let expected = NE.toList ls shouldParse p args e mConf expected + -- Regression test for an exponential-time bug.+ --+ -- A parser shaped like+ --+ -- choice [some argument, many --filter] <*> many --opt1 <*> many --opt2 <*> ...+ --+ -- combined with input that has both several @--opt1 VAL@ pairs and at+ -- least one @--opt2 VAL@ pair scaled super-linearly: in the broken+ -- version, every added @--opt2@ multiplied total parse time by ~3x.+ --+ -- The original report came from a sydtest mutation suite invocation+ -- with 6 @--mutation@ + 6 @--mutation-suite-exe@ flags taking+ -- > 30 seconds of CPU just to parse settings. With the parser shape+ -- below and 6 @--opt1@ + 6 @--opt2@ pairs the broken version takes+ -- many minutes; the fixed version completes in milliseconds.+ it "parses many-many input in bounded time (regression: no exponential blowup)" $ do+ let parser :: Parser ()+ parser =+ void $+ (,,)+ <$> choice+ [ some (setting [reader str, argument, help "F", metavar "F"] :: Parser String),+ many (setting [reader str, option, long "filter", help "F", metavar "F"])+ ]+ <*> many (setting [reader str, option, long "opt1", help "x", metavar "X"] :: Parser String)+ <*> many (setting [reader str, option, long "opt2", help "x", metavar "X"] :: Parser String)+ let args =+ parseArgs $+ concat (replicate 6 ["--opt1", "/v"])+ ++ concat (replicate 6 ["--opt2", "/v"])+ mResult <-+ Timeout.timeout+ (500 * 1000)+ (runParserOn allCapabilities Nothing parser args EnvMap.empty Nothing)+ case mResult of+ Nothing -> expectationFailure "parser took longer than 0.5 seconds; likely exponential blowup"+ Just (Left errs) -> expectationFailure $ T.unpack $ renderChunksText With24BitColours $ renderErrors errs+ Just (Right ()) -> pure ()+ describe "MapIO" $ do it "can run an IO action on the result of a parser" $ forAllValid $ \e ->@@ -526,16 +567,15 @@ -- Unfolding short options as well argParseSpecs- ( (,)- <$> ( length- <$> many- ( setting- [ help "verbosity",- switch (),- short 'v'- ]- )+ ( ( (,) . length+ <$> many+ ( setting+ [ help "verbosity",+ switch (),+ short 'v'+ ] )+ ) <*> setting [ reader str, option,@@ -650,8 +690,9 @@ verbosityArgsExamples argParseSpecs ( swap- <$> ( (,)- <$> (length <$> many (setting [switch (), short 'v', long "verbose"]))+ <$> ( ( (,) . length+ <$> many (setting [switch (), short 'v', long "verbose"])+ ) <*> many (setting [reader str, argument]) ) :: Parser ([String], Int)
test_resources/completion/fish-completion-script.fish view
@@ -8,10 +8,11 @@ set tmpline $tmpline --completion-word $arg end for opt in (/usr/bin/example-executable $tmpline)- if test -d $opt- echo -E "$opt/"+ set -l val (string split \t -- $opt)[1]+ if test -d $val+ echo -E "$val/" else- echo -E "$opt"+ echo -E "$val" end end end
test_resources/completion/zsh-completion-script.zsh view
@@ -22,11 +22,18 @@ if [[ $word[1] == "-" ]]; then local desc=("$parts[1] ($parts[2])") compadd -d desc -- $parts[1]+ elif [[ $parts[3] == 'N' ]]; then+ local desc=($(print -f "%-019s -- %s" $parts[1] $parts[2]))+ compadd -f -l -S '' -d desc -- $parts[1] else local desc=($(print -f "%-019s -- %s" $parts[1] $parts[2]))- compadd -l -d desc -- $parts[1]+ compadd -f -l -d desc -- $parts[1] fi else- compadd -f -- $word+ if [[ $parts[3] == 'N' ]]; then+ compadd -f -S '' -- $parts[1]+ else+ compadd -f -- $parts[1]+ fi fi done
test_resources/docs/big-config/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:25+# To update this it, run the test with --golden-reset { lib }: { big = lib.mkOption {
test_resources/docs/empty/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:21+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/enable-disable-optional/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:30+# To update this it, run the test with --golden-reset { lib }: { example = lib.mkOption {
test_resources/docs/enable-disable/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:28+# To update this it, run the test with --golden-reset { lib }: { example = lib.mkOption {
test_resources/docs/greet/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:37+# To update this it, run the test with --golden-reset { lib }: { greeting = lib.mkOption {
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:27+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/many-args/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:22+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/optional/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:24+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/same-help/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:33+# To update this it, run the test with --golden-reset { lib }: { int = lib.mkOption {
test_resources/docs/secret/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:35+# To update this it, run the test with --golden-reset { lib }: { first-secret-file = lib.mkOption {
test_resources/docs/some-args/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:23+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/sub-commands/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:39+# To update this it, run the test with --golden-reset { lib }: { name = lib.mkOption {
test_resources/docs/sub-settings/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:26+# To update this it, run the test with --golden-reset { lib }: { foo = lib.mkOption {
test_resources/docs/sum-type/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:34+# To update this it, run the test with --golden-reset { lib }: { sum-type = lib.mkOption {
test_resources/docs/three-commands/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:38+# To update this it, run the test with --golden-reset { lib }: { enable = lib.mkOption {
test_resources/docs/verbose/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:32+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/with-default/nix-options.nix view
@@ -1,2 +1,6 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:36+# To update this it, run the test with --golden-reset { lib }: { }
test_resources/docs/yes-no-optional/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:31+# To update this it, run the test with --golden-reset { lib }: { example = lib.mkOption {
test_resources/docs/yes-no/nix-options.nix view
@@ -1,3 +1,7 @@+# DO NOT EDIT THIS FILE DIRECTLY+# This file was generated by+# opt-env-conf-test:test/OptEnvConf/APISpec.hs:29+# To update this it, run the test with --golden-reset { lib }: { example = lib.mkOption {
test_resources/error/all-or-nothing-relevant.txt view
@@ -1,5 +1,5 @@ Missing option: [37m--bar[m [34mThis one will not exist[m You are seeing this error because at least one, but not all, of the settings in an allOrNothing (or subSettings) parser have been defined. The following settings have been parsed:-[36mtest/OptEnvConf/ErrorSpec.hs:153:21 in main:OptEnvConf.ErrorSpec[m+[36mtest/OptEnvConf/ErrorSpec.hs:153:21 in opt-env-conf-test:OptEnvConf.ErrorSpec[m Hit the 'empty' case of the Parser type, this should not happen.
test_resources/error/all-or-nothing.txt view
@@ -1,4 +1,4 @@ Missing option: [37m--bar[m [34mThis one will not exist[m You are seeing this error because at least one, but not all, of the settings in an allOrNothing (or subSettings) parser have been defined. The following settings have been parsed:-[36mtest/OptEnvConf/ErrorSpec.hs:141:19 in main:OptEnvConf.ErrorSpec[m+[36mtest/OptEnvConf/ErrorSpec.hs:141:19 in opt-env-conf-test:OptEnvConf.ErrorSpec[m
test_resources/help/toplevel-pure-with-invalid-port.txt view
@@ -1,4 +1,4 @@-[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mFILE_PATH[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND+[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND example program description @@ -37,8 +37,8 @@ [33m<string>[m [34mSecret key[m- option: [37m--payment-secret-key[m [33mFILE_PATH[m- env: [37mPAYMENT_SECRET_KEY[m [33mFILE_PATH[m+ option: [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m+ env: [37mPAYMENT_SECRET_KEY[m [33mSECRET_KEY_FILE[m config: [37mpayment.secret-key[m: # [32mor null[m [33m<string>[m
test_resources/help/toplevel-pure.txt view
@@ -1,4 +1,4 @@-[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mFILE_PATH[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND+[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND example program description @@ -37,8 +37,8 @@ [33m<string>[m [34mSecret key[m- option: [37m--payment-secret-key[m [33mFILE_PATH[m- env: [37mPAYMENT_SECRET_KEY[m [33mFILE_PATH[m+ option: [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m+ env: [37mPAYMENT_SECRET_KEY[m [33mSECRET_KEY_FILE[m config: [37mpayment.secret-key[m: # [32mor null[m [33m<string>[m
test_resources/help/wrong-command.txt view
@@ -1,4 +1,4 @@-[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mFILE_PATH[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND+[36mUsage: [m[33mexample[m [36m[[m[37m--config-file[m [33mFILE_PATH[m[36m][m [36m[[m[37m--port[m [33mPORT[m[36m][m [36m[[m[37m--log-level[m [33mLOG_LEVEL[m[36m][m [36m[[m[37m--payment-public-key[m [33mPUBLIC_KEY[m [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m [36m[[m[37m--payment-currency[m [33mCURRENCY[m[36m][m[36m][m COMMAND example program description @@ -37,8 +37,8 @@ [33m<string>[m [34mSecret key[m- option: [37m--payment-secret-key[m [33mFILE_PATH[m- env: [37mPAYMENT_SECRET_KEY[m [33mFILE_PATH[m+ option: [37m--payment-secret-key[m [33mSECRET_KEY_FILE[m+ env: [37mPAYMENT_SECRET_KEY[m [33mSECRET_KEY_FILE[m config: [37mpayment.secret-key[m: # [32mor null[m [33m<string>[m
test_resources/lint/config-without-load.txt view
@@ -3,4 +3,4 @@ [31mInvalid Setting:[m [33mconf[m or [33mname[m was called with no way to load configuration. You can load configuration with [33mwithConfig[m, or explicitly not load any configuration with [33mwithoutConfig[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:124:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:124:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/dash-in-long.txt view
@@ -4,4 +4,4 @@ [33mlong[m may not start with a '-'. Found [33mlong[m "--switch". Try [33mlong[m "switch" instead.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:33:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:33:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/dash-in-short.txt view
@@ -3,4 +3,4 @@ [31mInvalid Setting:[m [33mshort[m may not contain a '-'. Found [33mshort[m '-'.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:25:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:25:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/empty-setting.txt view
@@ -3,4 +3,4 @@ [31mInvalid Setting:[m This [33msetting[m parses nothing. Add an [33margument[m, [33mswitch[m, [33moption[m, [33menv[m, [33mconf[m, or [33mvalue[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:19:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:19:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-commands.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33mcommands[m was called with an empty list.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:121:6 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:121:6 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-dashed-for-option.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33moption[m has no [33mlong[m or [33mshort[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:67:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:67:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-dashed-for-switch.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33mswitch[m has no [33mlong[m or [33mshort[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:98:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:98:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-metavar-for-argument.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33margument[m has no [33mmetavar[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:49:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:49:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-metavar-for-env.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33menv[m or [33mname[m has no [33mmetavar[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:112:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:112:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-metavar-for-option.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33moption[m or [33mname[m has no [33mmetavar[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:88:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:88:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-option-or-switch-for-dashed.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33mlong[m or [33mshort[m has no [33moption[m or [33mswitch[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:77:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:77:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-reader-for-argument.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33margument[m has no [33mreader[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:41:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:41:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-reader-for-env.txt view
@@ -2,8 +2,8 @@ [31mInvalid Setting:[m [33menv[m or [33mname[m has no [33mreader[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:105:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:105:7 in opt-env-conf-test:OptEnvConf.LintSpec[m [31mInvalid Setting:[m [33menv[m or [33mname[m has no [33mmetavar[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:105:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:105:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/no-reader-for-option.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33moption[m or [33mname[m has no [33mreader[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:58:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:58:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/null-setting.txt view
@@ -2,9 +2,9 @@ [31mInvalid Setting:[m missing [33mhelp[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:16:6 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:16:6 in opt-env-conf-test:OptEnvConf.LintSpec[m [31mInvalid Setting:[m This [33msetting[m parses nothing. Add an [33margument[m, [33mswitch[m, [33moption[m, [33menv[m, [33mconf[m, or [33mvalue[m.- Defined at: [36mtest/OptEnvConf/LintSpec.hs:16:6 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:16:6 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/unknown-default-command.txt view
@@ -2,4 +2,4 @@ [31mInvalid Setting:[m [33mdefaultCommand[m was called with an unknown command: [35mb[m- Defined at: [36mtest/OptEnvConf/LintSpec.hs:185:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:185:7 in opt-env-conf-test:OptEnvConf.LintSpec[m
test_resources/lint/unreadable-example.txt view
@@ -3,4 +3,4 @@ [31mInvalid Setting:[m [33mexample[m was called with an example that none of the [33mreader[ms succeed in reading. Example: NaN- Defined at: [36mtest/OptEnvConf/LintSpec.hs:132:7 in main:OptEnvConf.LintSpec[m+ Defined at: [36mtest/OptEnvConf/LintSpec.hs:132:7 in opt-env-conf-test:OptEnvConf.LintSpec[m