1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ScopedTypeVariables #-}
module WithCli.Pure.RecordSpec where
import Prelude ()
import Prelude.Compat
import Control.Exception
import Data.Foldable (forM_)
import Data.List (isPrefixOf, isSuffixOf)
import System.Exit
import System.IO
import System.IO.Silently
import Test.Hspec
import Util
import WithCli.Pure
spec :: Spec
spec = do
part1
part2
part3
part4
part5
data Foo
= Foo {
bar :: Maybe Int,
baz :: String,
bool :: Bool
}
deriving (Generic, Show, Eq)
instance HasArguments Foo
data NotAllowed
= NotAllowed1
| NotAllowed2
deriving (Generic, Show, Eq)
instance HasArguments NotAllowed
part1 :: Spec
part1 = do
describe "withCliPure (record types)" $ do
it "allows optional arguments" $ do
parse "--baz foo" `shouldBe`
Success (Foo Nothing "foo" False)
it "allows boolean flags" $ do
parse "--bool --baz foo" `shouldBe`
Success (Foo Nothing "foo" True)
it "allows overwriting String options" $ do
parse "--baz one --baz two"
`shouldBe` Success (Foo Nothing "two" False)
context "with invalid arguments" $ do
it "prints out an error" $ do
let Errors messages = parse "--no-such-option" :: Result Foo
messages `shouldBe`
"unrecognized option `--no-such-option'\n" ++
"missing option: --baz=STRING\n"
it "prints errors for missing options" $ do
let Errors messages = parse [] :: Result Foo
messages `shouldBe` "missing option: --baz=STRING\n"
it "prints out an error for unparseable options" $ do
let Errors messages = parse "--bar foo --baz huhu" :: Result Foo
messages `shouldBe` "cannot parse as INTEGER (optional): foo\n"
it "complains about unused positional arguments" $ do
(parse "--baz foo unused" :: Result Foo)
`shouldBe` Errors "unknown argument: unused\n"
it "complains about invalid overwritten options" $ do
let Errors messages = parse "--bar foo --baz huhu --bar 12" :: Result Foo
messages `shouldBe` "cannot parse as INTEGER (optional): foo\n"
context "--help" $ do
it "implements --help" $ do
let OutputAndExit output = parse "--help" :: Result Foo
mapM_ (output `shouldContain`) $
"--bar=INTEGER" : "optional" :
"--baz=STRING" :
"--bool" :
[]
lines output `shouldSatisfy` (not . ("" `elem`))
it "contains help message about --help" $ do
let OutputAndExit output = parse "--help" :: Result Foo
output `shouldContain` "show help and exit"
it "does not contain trailing spaces" $ do
output <-
hCapture_ [stdout] $
handle (\ ExitSuccess -> return ()) $
handleResult $ ((parse "--help" :: Result Foo) >> return ())
forM_ (lines output) $ \ line -> do
line `shouldSatisfy` (not . (" " `isSuffixOf`))
it "complains when the options datatype is not allowed" $ do
let Errors messages = parse "--help" :: Result NotAllowed
messages `shouldSatisfy` ("getopt-generics doesn't support sum types" `isPrefixOf`)
it "outputs a header including \"[OPTIONS]\"" $ do
let OutputAndExit output = parse "--help" :: Result Foo
output `shouldSatisfy` ("prog-name [OPTIONS]\n" `isPrefixOf`)
data ListOptions
= ListOptions {
multiple :: [Int]
}
deriving (Generic, Show, Eq)
instance HasArguments ListOptions
part2 :: Spec
part2 = do
describe "parseArguments" $ do
it "allows interpreting multiple uses of the same option as lists" $ do
parse "--multiple 23 --multiple 42"
`shouldBe` Success (ListOptions [23, 42])
it "complains about invalid list arguments" $ do
let Errors errs =
parse "--multiple foo --multiple 13" :: Result ListOptions
errs `shouldBe` "cannot parse as INTEGER (multiple possible): foo\n"
data CamelCaseOptions
= CamelCaseOptions {
camelCase :: String
}
deriving (Generic, Show, Eq)
instance HasArguments CamelCaseOptions
part3 :: Spec
part3 = do
describe "parseArguments" $ do
it "turns camelCase selectors to lowercase and seperates with a dash" $ do
parse "--camel-case foo" `shouldBe` Success (CamelCaseOptions "foo")
it "help does not contain camelCase flags" $ do
let OutputAndExit output :: Result CamelCaseOptions
= parse "--help"
output `shouldNotContain` "camelCase"
output `shouldContain` "camel-case"
it "error messages don't contain camelCase flags" $ do
let Errors errs :: Result CamelCaseOptions
= parse "--bla"
show errs `shouldNotContain` "camelCase"
show errs `shouldContain` "camel-case"
data WithUnderscore
= WithUnderscore {
_withUnderscore :: String
}
deriving (Generic, Show, Eq)
instance HasArguments WithUnderscore
part4 :: Spec
part4 = do
describe "parseArguments" $ do
it "ignores leading underscores in field names" $ do
parse "--with-underscore foo"
`shouldBe` Success (WithUnderscore "foo")
data WithoutSelectors
= WithoutSelectors String Bool Int
deriving (Eq, Show, Generic)
instance HasArguments WithoutSelectors
part5 :: Spec
part5 = do
describe "parseArguments" $ do
context "WithoutSelectors" $ do
it "populates fields without selectors from positional arguments" $ do
parse "foo true 23"
`shouldBe` Success (WithoutSelectors "foo" True 23)
it "has good help output for positional arguments" $ do
let OutputAndExit output = parse "--help" :: Result WithoutSelectors
output `shouldSatisfy` ("prog-name [OPTIONS] STRING BOOL INTEGER" `isPrefixOf`)
it "has good error messages for missing positional arguments" $ do
(parse "foo" :: Result WithoutSelectors)
`shouldBe` Errors
("missing argument of type BOOL\n" ++
"missing argument of type INTEGER\n")
it "complains about additional positional arguments" $ do
(parse "foo true 5 bar" :: Result WithoutSelectors)
`shouldBe` Errors "unknown argument: bar\n"
it "allows using tuples" $ do
(parse "42 bar" :: Result (Int, String))
`shouldBe` Success (42, "bar")
|