github.com/bobziuchkovski/writ@v0.8.9/command_test.go (about) 1 // Copyright (c) 2016 Bob Ziuchkovski 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package writ 22 23 import ( 24 "fmt" 25 "io" 26 "io/ioutil" 27 "math" 28 "os" 29 "reflect" 30 "strconv" 31 "strings" 32 "testing" 33 ) 34 35 func CompareField(structval interface{}, field string, value interface{}) (equal bool, fieldVal interface{}) { 36 rval := reflect.ValueOf(structval) 37 for rval.Kind() == reflect.Ptr { 38 rval = rval.Elem() 39 } 40 f := rval.FieldByName(field) 41 equal = reflect.DeepEqual(f.Interface(), value) 42 fieldVal = f.Interface() 43 return 44 } 45 46 /* 47 * Test command and option routing for multi-tier commands 48 */ 49 50 type topSpec struct { 51 MidSpec midSpec `command:"mid" alias:"second, 2nd" description:"a mid-level command"` 52 HelpFlag bool `flag:"h, help" description:"help flag on a top-level command"` 53 Top int `option:"t, topval" description:"an option on a top-level command"` 54 } 55 56 type midSpec struct { 57 Mid int `option:"m, midval" description:"an option on a mid-level command"` 58 HelpFlag bool `flag:"h, help" description:"help flag on a mid-level command"` 59 BottomSpec bottomSpec `command:"bottom" alias:"third" description:"a bottom-level command"` 60 } 61 62 type bottomSpec struct { 63 Bottom int `option:"b, bottomval" description:"an option on a bottom-level command"` 64 HelpFlag bool `flag:"h, help" description:"help flag on a bottom-level command"` 65 } 66 67 type commandFieldTest struct { 68 Args []string 69 Valid bool 70 Err string // TODO: rewrite all Valid: false cases with Err: message 71 Path string 72 Positional []string 73 Field string 74 Value interface{} 75 SkipReason string 76 } 77 78 var commandFieldTests = []commandFieldTest{ 79 // Path: top 80 {Args: []string{}, Valid: true, Path: "top", Positional: []string{}}, 81 {Args: []string{"-"}, Valid: true, Path: "top", Positional: []string{"-"}}, 82 {Args: []string{"-", "mid"}, Valid: true, Path: "top", Positional: []string{"-", "mid"}}, 83 {Args: []string{"--"}, Valid: true, Path: "top", Positional: []string{}}, 84 {Args: []string{"--", "mid"}, Valid: true, Path: "top", Positional: []string{"mid"}}, 85 {Args: []string{"-t", "1"}, Valid: true, Path: "top", Positional: []string{}, Field: "Top", Value: 1}, 86 {Args: []string{"foo", "-t", "1"}, Valid: true, Path: "top", Positional: []string{"foo"}, Field: "Top", Value: 1}, 87 {Args: []string{"-t", "1", "foo"}, Valid: true, Path: "top", Positional: []string{"foo"}, Field: "Top", Value: 1}, 88 {Args: []string{"foo", "bar"}, Valid: true, Path: "top", Positional: []string{"foo", "bar"}}, 89 {Args: []string{"foo", "-t", "1", "bar"}, Valid: true, Path: "top", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 90 {Args: []string{"-t", "1", "foo", "bar"}, Valid: true, Path: "top", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 91 {Args: []string{"--", "mid"}, Valid: true, Path: "top", Positional: []string{"mid"}}, 92 {Args: []string{"-t", "1", "--", "mid"}, Valid: true, Path: "top", Positional: []string{"mid"}, Field: "Top", Value: 1}, 93 {Args: []string{"-", "-t", "1", "--", "mid"}, Valid: true, Path: "top", Positional: []string{"-", "mid"}, Field: "Top", Value: 1}, 94 {Args: []string{"--", "-t", "1", "mid"}, Valid: true, Path: "top", Positional: []string{"-t", "1", "mid"}, Field: "Top", Value: 0}, 95 {Args: []string{"--", "-t", "1", "-", "mid"}, Valid: true, Path: "top", Positional: []string{"-t", "1", "-", "mid"}, Field: "Top", Value: 0}, 96 {Args: []string{"bottom"}, Valid: true, Path: "top", Positional: []string{"bottom"}}, 97 {Args: []string{"third"}, Valid: true, Path: "top", Positional: []string{"third"}}, 98 {Args: []string{"bottom", "mid"}, Valid: true, Path: "top", Positional: []string{"bottom", "mid"}}, 99 {Args: []string{"bottom", "second"}, Valid: true, Path: "top", Positional: []string{"bottom", "second"}}, 100 {Args: []string{"bottom", "-", "second"}, Valid: true, Path: "top", Positional: []string{"bottom", "-", "second"}}, 101 {Args: []string{"-m", "2"}, Valid: false}, 102 {Args: []string{"--midval", "2"}, Valid: false}, 103 {Args: []string{"-b", "3"}, Valid: false}, 104 {Args: []string{"--bottomval", "3"}, Valid: false}, 105 {Args: []string{"--bogus", "4"}, Valid: false}, 106 {Args: []string{"--foo"}, Valid: false}, 107 {Args: []string{"--foo=bar"}, Valid: false}, 108 {Args: []string{"-f"}, Valid: false}, 109 {Args: []string{"-f", "bar"}, Valid: false}, 110 {Args: []string{"-fbar"}, Valid: false}, 111 {Args: []string{"--help", "--help"}, Valid: false, Err: `option "--help" specified too many times`}, 112 {Args: []string{"-h", "-h"}, Valid: false, Err: `option "-h" specified too many times`}, 113 {Args: []string{"-hh"}, Valid: false, Err: `option "-h" specified too many times`}, 114 115 // Path: top mid 116 {Args: []string{"mid"}, Valid: true, Path: "top mid", Positional: []string{}}, 117 {Args: []string{"mid", "-"}, Valid: true, Path: "top mid", Positional: []string{"-"}}, 118 {Args: []string{"mid", "--"}, Valid: true, Path: "top mid", Positional: []string{}}, 119 {Args: []string{"mid", "-", "bottom"}, Valid: true, Path: "top mid", Positional: []string{"-", "bottom"}}, 120 {Args: []string{"mid", "--", "bottom"}, Valid: true, Path: "top mid", Positional: []string{"bottom"}}, 121 {Args: []string{"mid", "-t", "1"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "Top", Value: 1}, 122 {Args: []string{"mid", "-", "-t", "1"}, Valid: true, Path: "top mid", Positional: []string{"-"}, Field: "Top", Value: 1}, 123 {Args: []string{"mid", "--", "-t", "1"}, Valid: true, Path: "top mid", Positional: []string{"-t", "1"}, Field: "Top", Value: 0}, 124 {Args: []string{"-t", "1", "mid"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "Top", Value: 1}, 125 {Args: []string{"mid", "foo", "-t", "1"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Top", Value: 1}, 126 {Args: []string{"-t", "1", "mid", "foo"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Top", Value: 1}, 127 {Args: []string{"mid", "-t", "1", "foo"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Top", Value: 1}, 128 {Args: []string{"-t", "1", "mid", "foo"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Top", Value: 1}, 129 {Args: []string{"mid", "foo", "-m", "2"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Mid", Value: 2}, 130 {Args: []string{"mid", "-m", "2", "foo"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Mid", Value: 2}, 131 {Args: []string{"mid", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}}, 132 {Args: []string{"second", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}}, 133 {Args: []string{"2nd", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}}, 134 {Args: []string{"mid", "foo", "-t", "1", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 135 {Args: []string{"mid", "-t", "1", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 136 {Args: []string{"-t", "1", "mid", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 137 {Args: []string{"mid", "foo", "-m", "2", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Mid", Value: 2}, 138 {Args: []string{"-t", "1", "second", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 139 {Args: []string{"second", "foo", "-m", "2", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Mid", Value: 2}, 140 {Args: []string{"-t", "1", "2nd", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 141 {Args: []string{"2nd", "foo", "-m", "2", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Mid", Value: 2}, 142 {Args: []string{"mid", "-m", "2", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Mid", Value: 2}, 143 {Args: []string{"mid", "-m", "2", "foo", "-t", "1", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 144 {Args: []string{"mid", "-m", "2", "foo", "-t", "1", "bar"}, Valid: true, Field: "Mid", Value: 2}, 145 {Args: []string{"mid", "-t", "1", "foo", "-m", "2", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 146 {Args: []string{"mid", "-t", "1", "foo", "-m", "2", "bar"}, Valid: true, Field: "Mid", Value: 2}, 147 {Args: []string{"-t", "1", "mid", "foo", "bar", "-m", "2"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 148 {Args: []string{"-t", "1", "mid", "foo", "bar", "-m", "2"}, Valid: true, Field: "Mid", Value: 2}, 149 {Args: []string{"mid", "-m", "2", "--"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "Mid", Value: 2}, 150 {Args: []string{"mid", "--", "-m", "2"}, Valid: true, Path: "top mid", Positional: []string{"-m", "2"}, Field: "Mid", Value: 0}, 151 {Args: []string{"mid", "--", "bottom", "-b", "3"}, Valid: true, Path: "top mid", Positional: []string{"bottom", "-b", "3"}}, 152 {Args: []string{"mid", "--", "bottom", "-b", "3", "--"}, Valid: true, Path: "top mid", Positional: []string{"bottom", "-b", "3", "--"}}, 153 {Args: []string{"mid", "--", "bottom", "--", "-b", "3"}, Valid: true, Path: "top mid", Positional: []string{"bottom", "--", "-b", "3"}}, 154 {Args: []string{"-m", "2", "mid"}, Valid: false}, 155 {Args: []string{"--midval", "2", "mid"}, Valid: false}, 156 {Args: []string{"-m", "2", "mid", "foo"}, Valid: false}, 157 {Args: []string{"-b", "3", "mid"}, Valid: false}, 158 {Args: []string{"-b", "3", "mid", "foo"}, Valid: false}, 159 {Args: []string{"mid", "-b", "3"}, Valid: false}, 160 {Args: []string{"mid", "-b", "3", "foo"}, Valid: false}, 161 {Args: []string{"mid", "--bogus", "4"}, Valid: false}, 162 {Args: []string{"mid", "--foo"}, Valid: false}, 163 {Args: []string{"mid", "--foo=bar"}, Valid: false}, 164 {Args: []string{"mid", "-f"}, Valid: false}, 165 {Args: []string{"mid", "-f", "bar"}, Valid: false}, 166 {Args: []string{"mid", "-fbar"}, Valid: false}, 167 {Args: []string{"mid", "--help", "--help"}, Valid: false, Err: `option "--help" specified too many times`}, 168 {Args: []string{"mid", "-h", "-h"}, Valid: false, Err: `option "-h" specified too many times`}, 169 {Args: []string{"mid", "-hh"}, Valid: false, Err: `option "-h" specified too many times`}, 170 171 // Path: top mid bottom 172 {Args: []string{"mid", "bottom"}, Valid: true, Path: "top mid bottom", Positional: []string{}}, 173 {Args: []string{"mid", "bottom", "-"}, Valid: true, Path: "top mid bottom", Positional: []string{"-"}}, 174 {Args: []string{"mid", "bottom", "--"}, Valid: true, Path: "top mid bottom", Positional: []string{}}, 175 {Args: []string{"mid", "bottom", "-t", "1"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "Top", Value: 1}, 176 {Args: []string{"mid", "-t", "1", "bottom"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "Top", Value: 1}, 177 {Args: []string{"-t", "1", "mid", "bottom"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "Top", Value: 1}, 178 {Args: []string{"mid", "bottom", "foo", "-t", "1"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Top", Value: 1}, 179 {Args: []string{"mid", "-t", "1", "bottom", "foo"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Top", Value: 1}, 180 {Args: []string{"-t", "1", "mid", "bottom", "foo"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Top", Value: 1}, 181 {Args: []string{"mid", "bottom", "foo", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Bottom", Value: 3}, 182 {Args: []string{"mid", "bottom", "-b", "3", "foo"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Bottom", Value: 3}, 183 {Args: []string{"mid", "bottom", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}}, 184 {Args: []string{"mid", "third", "-b", "3", "foo"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Bottom", Value: 3}, 185 {Args: []string{"2nd", "third", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}}, 186 {Args: []string{"-t", "1", "mid", "bottom", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1}, 187 {Args: []string{"mid", "bottom", "foo", "-b", "3", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3}, 188 {Args: []string{"mid", "bottom", "-b", "3", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3}, 189 {Args: []string{"mid", "-t", "1", "bottom", "-b", "3", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3}, 190 {Args: []string{"mid", "-t", "1", "bottom", "-b", "3", "foo", "bar"}, Valid: true, Field: "Top", Value: 1}, 191 {Args: []string{"mid", "-m", "2", "bottom", "foo", "-b", "3", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3}, 192 {Args: []string{"mid", "-m", "2", "bottom", "foo", "-b", "3", "bar"}, Valid: true, Field: "Mid", Value: 2}, 193 {Args: []string{"-t", "1", "mid", "bottom", "foo", "bar", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3}, 194 {Args: []string{"-t", "1", "mid", "bottom", "foo", "bar", "-b", "3"}, Valid: true, Field: "Top", Value: 1}, 195 {Args: []string{"-t", "1", "2nd", "bottom", "foo", "bar", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3}, 196 {Args: []string{"-t", "1", "2nd", "bottom", "foo", "bar", "-b", "3"}, Valid: true, Field: "Top", Value: 1}, 197 {Args: []string{"-t", "1", "mid", "third", "foo", "bar", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3}, 198 {Args: []string{"-t", "1", "mid", "third", "foo", "bar", "-b", "3"}, Valid: true, Field: "Top", Value: 1}, 199 {Args: []string{"-t", "1", "second", "third", "foo", "bar", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3}, 200 {Args: []string{"-t", "1", "second", "third", "foo", "bar", "-b", "3"}, Valid: true, Field: "Top", Value: 1}, 201 {Args: []string{"mid", "bottom", "-b", "3", "--"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "Bottom", Value: 3}, 202 {Args: []string{"mid", "bottom", "-", "-b", "3", "--"}, Valid: true, Path: "top mid bottom", Positional: []string{"-"}, Field: "Bottom", Value: 3}, 203 {Args: []string{"mid", "bottom", "--", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"-b", "3"}, Field: "Bottom", Value: 0}, 204 {Args: []string{"mid", "bottom", "-", "--", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"-", "-b", "3"}, Field: "Bottom", Value: 0}, 205 {Args: []string{"mid", "-b", "3", "bottom"}, Valid: false}, 206 {Args: []string{"bottom", "-b", "3"}, Valid: false}, 207 {Args: []string{"-b", "3", "bottom"}, Valid: false}, 208 {Args: []string{"-b", "3", "mid", "bottom"}, Valid: false}, 209 {Args: []string{"mid", "--help", "--help", "bottom"}, Valid: false, Err: `option "--help" specified too many times`}, 210 {Args: []string{"mid", "-h", "-h", "bottom"}, Valid: false, Err: `option "-h" specified too many times`}, 211 {Args: []string{"mid", "-hh", "bottom"}, Valid: false, Err: `option "-h" specified too many times`}, 212 213 // Duplicate option routing (HelpFlag) 214 {Args: []string{"-h"}, Valid: true, Path: "top", Positional: []string{}, Field: "HelpFlagTop", Value: true}, 215 {Args: []string{"-h"}, Valid: true, Field: "HelpFlagMid", Value: false}, 216 {Args: []string{"-h"}, Valid: true, Field: "HelpFlagBottom", Value: false}, 217 {Args: []string{"-h", "mid"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "HelpFlagTop", Value: true}, 218 {Args: []string{"-h", "mid"}, Valid: true, Field: "HelpFlagMid", Value: false}, 219 {Args: []string{"-h", "mid"}, Valid: true, Field: "HelpFlagBottom", Value: false}, 220 {Args: []string{"mid", "-h"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "HelpFlagMid", Value: true}, 221 {Args: []string{"mid", "-h"}, Valid: true, Field: "HelpFlagTop", Value: false}, 222 {Args: []string{"mid", "-h"}, Valid: true, Field: "HelpFlagBottom", Value: false}, 223 {Args: []string{"mid", "-h", "bottom"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "HelpFlagMid", Value: true}, 224 {Args: []string{"mid", "-h", "bottom"}, Valid: true, Field: "HelpFlagTop", Value: false}, 225 {Args: []string{"mid", "-h", "bottom"}, Valid: true, Field: "HelpFlagBottom", Value: false}, 226 {Args: []string{"mid", "bottom", "-h"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "HelpFlagBottom", Value: true}, 227 {Args: []string{"mid", "bottom", "-h"}, Valid: true, Field: "HelpFlagTop", Value: false}, 228 {Args: []string{"mid", "bottom", "-h"}, Valid: true, Field: "HelpFlagMid", Value: false}, 229 } 230 231 func TestCommandFields(t *testing.T) { 232 for _, test := range commandFieldTests { 233 spec := &topSpec{} 234 runCommandFieldTest(t, spec, test) 235 } 236 } 237 238 func runCommandFieldTest(t *testing.T, spec *topSpec, test commandFieldTest) { 239 if test.SkipReason != "" { 240 t.Logf("Test skipped. Args: %q, Field: %s, Reason: %s", test.Args, test.Field, test.SkipReason) 241 return 242 } 243 244 cmd := New("top", spec) 245 path, positional, err := cmd.Decode(test.Args) 246 values := map[string]interface{}{ 247 "Top": spec.Top, 248 "Mid": spec.MidSpec.Mid, 249 "Bottom": spec.MidSpec.BottomSpec.Bottom, 250 "HelpFlagTop": spec.HelpFlag, 251 "HelpFlagMid": spec.MidSpec.HelpFlag, 252 "HelpFlagBottom": spec.MidSpec.BottomSpec.HelpFlag, 253 } 254 if !test.Valid { 255 if err == nil { 256 t.Errorf("Expected error but none received. Args: %q", test.Args) 257 } 258 if test.Err != "" && err.Error() != test.Err { 259 t.Errorf("Invalid error message. Expected: %s, Received: %s", test.Err, err.Error()) 260 } 261 return 262 } 263 if err != nil { 264 t.Errorf("Received unexpected error. Field: %s, Args: %q, Error: %s", test.Field, test.Args, err) 265 return 266 } 267 if test.Positional != nil && !reflect.DeepEqual(positional, test.Positional) { 268 t.Errorf("Positional args are incorrect. Args: %q, Expected: %s, Received: %s", test.Args, test.Positional, positional) 269 return 270 } 271 if test.Field != "" && !reflect.DeepEqual(values[test.Field], test.Value) { 272 t.Errorf("Decoded value is incorrect. Field: %s, Args: %q, Expected: %#v, Received: %#v", test.Field, test.Args, test.Value, values[test.Field]) 273 return 274 } 275 if path.First() != cmd { 276 t.Errorf("Expected first command in path to be top-level command, but got %s instead.", path.First().Name) 277 return 278 } 279 if test.Path != "" && path.String() != test.Path { 280 t.Errorf("Command path is incorrect. Args: %q, Expected: %s, Received: %s", test.Args, test.Path, path) 281 return 282 } 283 } 284 285 func TestCommandString(t *testing.T) { 286 cmd := New("top", &topSpec{}) 287 if cmd.String() != "top" { 288 t.Errorf("Invalid Command.String() value. Expected: %q, received: %q", "top", cmd.String()) 289 } 290 if cmd.Subcommand("mid").String() != "mid" { 291 t.Errorf("Invalid Command.String() value. Expected: %q, received: %q", "mid", cmd.Subcommand("mid").String()) 292 } 293 if cmd.Subcommand("mid").Subcommand("bottom").String() != "bottom" { 294 t.Errorf("Invalid Command.String() value. Expected: %q, received: %q", "bottom", cmd.Subcommand("mid").Subcommand("bottom").String()) 295 } 296 } 297 298 /* 299 * Test parsing of description metadata 300 */ 301 302 func TestSpecDescriptions(t *testing.T) { 303 type Spec struct { 304 Flag bool `flag:"flag" description:"a flag"` 305 Option int `option:"option" description:"an option"` 306 Command struct{} `command:"command" description:"a command"` 307 } 308 cmd := New("test", &Spec{}) 309 if cmd.Option("flag").Description != "a flag" { 310 t.Errorf("Flag description is incorrect. Expected: %q, Received: %q", "a flag", cmd.Option("flag").Description) 311 } 312 if cmd.Option("option").Description != "an option" { 313 t.Errorf("Option description is incorrect. Expected: %q, Received: %q", "an option", cmd.Option("option").Description) 314 } 315 if cmd.Subcommand("command").Description != "a command" { 316 t.Errorf("Command description is incorrect. Expected: %q, Received: %q", "a command", cmd.Subcommand("command").Description) 317 } 318 } 319 320 /* 321 * Test parsing of placeholder metadata 322 */ 323 324 func TestSpecPlaceholders(t *testing.T) { 325 type Spec struct { 326 Option int `option:"option" description:"an option" placeholder:"VALUE"` 327 } 328 cmd := New("test", &Spec{}) 329 if cmd.Option("option").Placeholder != "VALUE" { 330 t.Errorf("Option placeholder is incorrect. Expected: %q, Received: %q", "VALUE", cmd.Option("option").Placeholder) 331 } 332 } 333 334 /* 335 * Test default values on fields 336 */ 337 338 type defaultFieldSpec struct { 339 Default int `option:"d" description:"An int field with a default" default:"42"` 340 EnvDefault int `option:"e" description:"An int field with an environment default" env:"ENV_DEFAULT"` 341 StackedDefault int `option:"s" description:"An int field with both a default and environment default" default:"84" env:"STACKED_DEFAULT"` 342 } 343 344 type defaultFieldTest struct { 345 Args []string 346 Valid bool 347 Field string 348 Value interface{} 349 EnvKey string 350 EnvValue string 351 SkipReason string 352 } 353 354 var defaultFieldTests = []defaultFieldTest{ 355 // Field with a default value 356 {Args: []string{""}, Valid: true, Field: "Default", Value: 42}, 357 {Args: []string{"-d", "2"}, Valid: true, Field: "Default", Value: 2}, 358 {Args: []string{"-d", "foo"}, Valid: false}, 359 360 // Field with an environment default 361 {Args: []string{""}, Valid: true, Field: "EnvDefault", Value: 0}, 362 {Args: []string{""}, Valid: true, EnvKey: "ENV_DEFAULT", EnvValue: "2", Field: "EnvDefault", Value: 2}, 363 {Args: []string{""}, Valid: true, EnvKey: "ENV_DEFAULT", EnvValue: "foo", Field: "EnvDefault", Value: 0}, 364 {Args: []string{"-e", "4"}, Valid: true, EnvKey: "ENV_DEFAULT", EnvValue: "2", Field: "EnvDefault", Value: 4}, 365 {Args: []string{"-e", "4"}, Valid: true, EnvKey: "ENV_DEFAULT", EnvValue: "foo", Field: "EnvDefault", Value: 4}, 366 {Args: []string{"-e", "foo"}, Valid: false, EnvKey: "ENV_DEFAULT", EnvValue: "2"}, 367 368 // Field with both a default value and an environment default 369 {Args: []string{""}, Valid: true, Field: "StackedDefault", Value: 84}, 370 {Args: []string{""}, Valid: true, EnvKey: "STACKED_DEFAULT", EnvValue: "2", Field: "StackedDefault", Value: 2}, 371 {Args: []string{""}, Valid: true, EnvKey: "STACKED_DEFAULT", EnvValue: "foo", Field: "StackedDefault", Value: 84}, 372 {Args: []string{"-s", "4"}, Valid: true, EnvKey: "STACKED_DEFAULT", EnvValue: "2", Field: "StackedDefault", Value: 4}, 373 {Args: []string{"-s", "4"}, Valid: true, EnvKey: "STACKED_DEFAULT", EnvValue: "foo", Field: "StackedDefault", Value: 4}, 374 {Args: []string{"-s", "foo"}, Valid: false, EnvKey: "STACKED_DEFAULT", EnvValue: "foo"}, 375 {Args: []string{"-s", "foo"}, Valid: false}, 376 } 377 378 func TestDefaultFields(t *testing.T) { 379 for _, test := range defaultFieldTests { 380 spec := &defaultFieldSpec{} 381 runDefaultFieldTest(t, spec, test) 382 } 383 } 384 385 func runDefaultFieldTest(t *testing.T, spec interface{}, test defaultFieldTest) { 386 if test.SkipReason != "" { 387 t.Logf("Test skipped. Args: %q, Field: %s, Reason: %s", test.Args, test.Field, test.SkipReason) 388 return 389 } 390 391 if test.EnvKey != "" { 392 realval := os.Getenv(test.EnvKey) 393 defer (func() { os.Setenv(test.EnvKey, realval) })() 394 os.Setenv(test.EnvKey, test.EnvValue) 395 } 396 cmd := New("test", spec) 397 _, _, err := cmd.Decode(test.Args) 398 399 if !test.Valid { 400 if err == nil { 401 t.Errorf("Expected error but none received. Args: %q", test.Args) 402 } 403 return 404 } 405 if err != nil { 406 t.Errorf("Received unexpected error. Field: %s, Args: %q, Error: %s", test.Field, test.Args, err) 407 return 408 } 409 equal, fieldval := CompareField(spec, test.Field, test.Value) 410 if !equal { 411 t.Errorf("Decoded value is incorrect. Field: %s, Args: %q, Expected: %#v, Received: %#v", test.Field, test.Args, test.Value, fieldval) 412 return 413 } 414 } 415 416 func TestBogusDefaultField(t *testing.T) { 417 var spec = &struct { 418 BogusDefault int `option:"b" description:"An int field with a bogus default" default:"bogus"` 419 }{} 420 421 defer func() { 422 r := recover() 423 if r != nil { 424 switch r.(type) { 425 case commandError, optionError: 426 // Intentional No-op 427 default: 428 panic(r) 429 } 430 } 431 }() 432 433 cmd := New("test", spec) 434 cmd.Decode([]string{}) 435 t.Errorf("Expected decoding to panic on bogus default value, but this didn't happen.") 436 } 437 438 /* 439 * Generic field test helpers 440 */ 441 442 type fieldTest struct { 443 Args []string 444 Valid bool 445 Field string 446 Value interface{} 447 SkipReason string 448 } 449 450 func runFieldTest(t *testing.T, spec interface{}, test fieldTest) { 451 if test.SkipReason != "" { 452 t.Logf("Test skipped. Args: %q, Field: %s, Reason: %s", test.Args, test.Field, test.SkipReason) 453 return 454 } 455 456 cmd := New("test", spec) 457 _, _, err := cmd.Decode(test.Args) 458 if !test.Valid { 459 if err == nil { 460 t.Errorf("Expected error but none received. Args: %q", test.Args) 461 } 462 return 463 } 464 if err != nil { 465 t.Errorf("Received unexpected error. Field: %s, Args: %q, Error: %s", test.Field, test.Args, err) 466 return 467 } 468 equal, fieldval := CompareField(spec, test.Field, test.Value) 469 if !equal { 470 t.Errorf("Decoded value is incorrect. Field: %s, Args: %q, Expected: %#v, Received: %#v", test.Field, test.Args, test.Value, fieldval) 471 return 472 } 473 } 474 475 /* 476 * Test option name variations 477 */ 478 479 type optNameSpec struct { 480 Bool bool `flag:" b, bool" description:"A bool flag"` 481 Accumulator int `flag:"a,acc,A, accum" description:"An accumulator field"` 482 Int int `option:"int, I" description:"An int field"` 483 Float float32 `option:" float,F, FloaT, f " description:"A float field"` 484 } 485 486 var optNameTests = []fieldTest{ 487 // Bool Flag 488 {Args: []string{"-b"}, Valid: true, Field: "Bool", Value: true}, 489 {Args: []string{"--bool"}, Valid: true, Field: "Bool", Value: true}, 490 491 // Accumulator Flag 492 {Args: []string{"-A", "--accum", "-a", "--acc", "-Aa", "-aA"}, Valid: true, Field: "Accumulator", Value: 8}, 493 494 // Int Option 495 {Args: []string{"-I2"}, Valid: true, Field: "Int", Value: 2}, 496 {Args: []string{"-I", "2"}, Valid: true, Field: "Int", Value: 2}, 497 {Args: []string{"--int", "2"}, Valid: true, Field: "Int", Value: 2}, 498 {Args: []string{"--int=2"}, Valid: true, Field: "Int", Value: 2}, 499 500 // Float Option 501 {Args: []string{"-F2"}, Valid: true, Field: "Float", Value: float32(2.0)}, 502 {Args: []string{"-F2.5"}, Valid: true, Field: "Float", Value: float32(2.5)}, 503 {Args: []string{"-F", "2"}, Valid: true, Field: "Float", Value: float32(2.0)}, 504 {Args: []string{"-F", "2.5"}, Valid: true, Field: "Float", Value: float32(2.5)}, 505 {Args: []string{"-f2"}, Valid: true, Field: "Float", Value: float32(2.0)}, 506 {Args: []string{"-f2.5"}, Valid: true, Field: "Float", Value: float32(2.5)}, 507 {Args: []string{"-f", "2"}, Valid: true, Field: "Float", Value: float32(2.0)}, 508 {Args: []string{"-f", "2.5"}, Valid: true, Field: "Float", Value: float32(2.5)}, 509 {Args: []string{"--FloaT", "2"}, Valid: true, Field: "Float", Value: float32(2.0)}, 510 {Args: []string{"--FloaT", "2.5"}, Valid: true, Field: "Float", Value: float32(2.5)}, 511 {Args: []string{"--FloaT=2"}, Valid: true, Field: "Float", Value: float32(2.0)}, 512 {Args: []string{"--FloaT=2.5"}, Valid: true, Field: "Float", Value: float32(2.5)}, 513 {Args: []string{"--float", "2"}, Valid: true, Field: "Float", Value: float32(2.0)}, 514 {Args: []string{"--float", "2.5"}, Valid: true, Field: "Float", Value: float32(2.5)}, 515 {Args: []string{"--float=2"}, Valid: true, Field: "Float", Value: float32(2.0)}, 516 {Args: []string{"--float=2.5"}, Valid: true, Field: "Float", Value: float32(2.5)}, 517 } 518 519 func TestOptionNames(t *testing.T) { 520 for _, test := range optNameTests { 521 spec := &optNameSpec{} 522 runFieldTest(t, spec, test) 523 } 524 } 525 526 /* 527 * Test flag fields 528 */ 529 530 type flagFieldSpec struct { 531 Bool bool `flag:"b, bool" description:"A bool flag"` 532 Accumulator int `flag:"a, acc" description:"An accumulator flag"` 533 } 534 535 var flagTests = []fieldTest{ 536 // Bool flag 537 {Args: []string{}, Valid: true, Field: "Bool", Value: false}, 538 {Args: []string{"-b"}, Valid: true, Field: "Bool", Value: true}, 539 {Args: []string{"--bool"}, Valid: true, Field: "Bool", Value: true}, 540 {Args: []string{"-b", "-b"}, Valid: false}, 541 {Args: []string{"-b2"}, Valid: false}, 542 {Args: []string{"--bool=2"}, Valid: false}, 543 544 // Accumulator flag 545 {Args: []string{}, Valid: true, Field: "Accumulator", Value: 0}, 546 {Args: []string{"-a"}, Valid: true, Field: "Accumulator", Value: 1}, 547 {Args: []string{"-a", "-a"}, Valid: true, Field: "Accumulator", Value: 2}, 548 {Args: []string{"-aaa"}, Valid: true, Field: "Accumulator", Value: 3}, 549 {Args: []string{"--acc", "-a"}, Valid: true, Field: "Accumulator", Value: 2}, 550 {Args: []string{"-a", "--acc", "-aa"}, Valid: true, Field: "Accumulator", Value: 4}, 551 {Args: []string{"-a3"}, Valid: false}, 552 {Args: []string{"--acc=3"}, Valid: false}, 553 } 554 555 func TestFlagFields(t *testing.T) { 556 for _, test := range flagTests { 557 spec := &flagFieldSpec{} 558 runFieldTest(t, spec, test) 559 } 560 } 561 562 /* 563 * Test map and slice field types 564 */ 565 566 type mapSliceFieldSpec struct { 567 StringSlice []string `option:"s" description:"A string slice option" placeholder:"STRINGSLICE"` 568 StringMap map[string]string `option:"m" description:"A map of strings option" placeholder:"KEY=VALUE"` 569 } 570 571 var mapSliceFieldTests = []fieldTest{ 572 // String Slice 573 {Args: []string{"-s", "1", "-s", "-1", "-s", "+1"}, Valid: true, Field: "StringSlice", Value: []string{"1", "-1", "+1"}}, 574 {Args: []string{"-s", " a b", "-s", "\n", "-s", "\t"}, Valid: true, Field: "StringSlice", Value: []string{" a b", "\n", "\t"}}, 575 {Args: []string{"-s", "日本", "-s", "-日本", "-s", "--日本"}, Valid: true, Field: "StringSlice", Value: []string{"日本", "-日本", "--日本"}}, 576 {Args: []string{"-s", "1"}, Valid: true, Field: "StringSlice", Value: []string{"1"}}, 577 {Args: []string{"-s", "-1"}, Valid: true, Field: "StringSlice", Value: []string{"-1"}}, 578 {Args: []string{"-s", "+1"}, Valid: true, Field: "StringSlice", Value: []string{"+1"}}, 579 {Args: []string{"-s", "1.0"}, Valid: true, Field: "StringSlice", Value: []string{"1.0"}}, 580 {Args: []string{"-s", "0x01"}, Valid: true, Field: "StringSlice", Value: []string{"0x01"}}, 581 {Args: []string{"-s", "-"}, Valid: true, Field: "StringSlice", Value: []string{"-"}}, 582 {Args: []string{"-s", "-a"}, Valid: true, Field: "StringSlice", Value: []string{"-a"}}, 583 {Args: []string{"-s", "--"}, Valid: true, Field: "StringSlice", Value: []string{"--"}}, 584 {Args: []string{"-s", "--a"}, Valid: true, Field: "StringSlice", Value: []string{"--a"}}, 585 {Args: []string{"-s", ""}, Valid: true, Field: "StringSlice", Value: []string{""}}, 586 {Args: []string{"-s", " "}, Valid: true, Field: "StringSlice", Value: []string{" "}}, 587 {Args: []string{"-s", " a"}, Valid: true, Field: "StringSlice", Value: []string{" a"}}, 588 {Args: []string{"-s", "a "}, Valid: true, Field: "StringSlice", Value: []string{"a "}}, 589 {Args: []string{"-s", "a b "}, Valid: true, Field: "StringSlice", Value: []string{"a b "}}, 590 {Args: []string{"-s", " a b"}, Valid: true, Field: "StringSlice", Value: []string{" a b"}}, 591 {Args: []string{"-s", "\n"}, Valid: true, Field: "StringSlice", Value: []string{"\n"}}, 592 {Args: []string{"-s", "\t"}, Valid: true, Field: "StringSlice", Value: []string{"\t"}}, 593 {Args: []string{"-s", "日本"}, Valid: true, Field: "StringSlice", Value: []string{"日本"}}, 594 {Args: []string{"-s", "-日本"}, Valid: true, Field: "StringSlice", Value: []string{"-日本"}}, 595 {Args: []string{"-s", "--日本"}, Valid: true, Field: "StringSlice", Value: []string{"--日本"}}, 596 {Args: []string{"-s", " 日本"}, Valid: true, Field: "StringSlice", Value: []string{" 日本"}}, 597 {Args: []string{"-s", "日本 "}, Valid: true, Field: "StringSlice", Value: []string{"日本 "}}, 598 {Args: []string{"-s", "日 本"}, Valid: true, Field: "StringSlice", Value: []string{"日 本"}}, 599 {Args: []string{"-s", "A relatively long string to make sure we aren't doing any silly truncation anywhere, since that would be bad..."}, Valid: true, Field: "StringSlice", Value: []string{"A relatively long string to make sure we aren't doing any silly truncation anywhere, since that would be bad..."}}, 600 {Args: []string{"-s"}, Valid: false}, 601 602 // String Map 603 {Args: []string{"-m", "a=b"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "b"}}, 604 {Args: []string{"-m", "a=b=c"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "b=c"}}, 605 {Args: []string{"-m", "a=b "}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "b "}}, 606 {Args: []string{"-m", "a= b"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": " b"}}, 607 {Args: []string{"-m", "a =b"}, Valid: true, Field: "StringMap", Value: map[string]string{"a ": "b"}}, 608 {Args: []string{"-m", " a=b"}, Valid: true, Field: "StringMap", Value: map[string]string{" a": "b"}}, 609 {Args: []string{"-m", " a=b "}, Valid: true, Field: "StringMap", Value: map[string]string{" a": "b "}}, 610 {Args: []string{"-m", "a = b "}, Valid: true, Field: "StringMap", Value: map[string]string{"a ": " b "}}, 611 {Args: []string{"-m", " a = b "}, Valid: true, Field: "StringMap", Value: map[string]string{" a ": " b "}}, 612 {Args: []string{"-m", "a=b", "-m", "a=c"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "c"}}, 613 {Args: []string{"-m", "a=b", "-m", "c=d"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "b", "c": "d"}}, 614 {Args: []string{"-m", "日=本", "-m", "-日=本", "-m", "--日=--本"}, Valid: true, Field: "StringMap", Value: map[string]string{"日": "本", "-日": "本", "--日": "--本"}}, 615 {Args: []string{"-m", "a", "=b"}, Valid: false}, 616 {Args: []string{"-m", "a", "b"}, Valid: false}, 617 {Args: []string{"-m", "foo"}, Valid: false}, 618 {Args: []string{"-m", "a:b"}, Valid: false}, 619 {Args: []string{"-m"}, Valid: false}, 620 } 621 622 func TestMapSliceFields(t *testing.T) { 623 for _, test := range mapSliceFieldTests { 624 spec := &mapSliceFieldSpec{} 625 runFieldTest(t, spec, test) 626 } 627 } 628 629 /* 630 * Test io field types 631 */ 632 633 const ioTestText = "test IO" 634 635 type ioFieldSpec struct { 636 Reader io.Reader `option:"reader" description:"An io.Reader input option"` 637 ReadCloser io.ReadCloser `option:"readcloser" description:"An io.ReadCloser input option"` 638 Writer io.Writer `option:"writer" description:"An io.Writer output option"` 639 WriteCloser io.WriteCloser `option:"writecloser" description:"An io.WriteCloser output option"` 640 } 641 642 func (r *ioFieldSpec) PerformIO() error { 643 input := []io.Reader{r.Reader, r.ReadCloser} 644 for _, in := range input { 645 if in != nil { 646 bytes, err := ioutil.ReadAll(in) 647 if err != nil { 648 return err 649 } 650 if string(bytes) != ioTestText { 651 return fmt.Errorf("Expected to read %q. Read %q instead.", ioTestText, string(bytes)) 652 } 653 closer, ok := in.(io.Closer) 654 if ok { 655 err = closer.Close() 656 if err != nil { 657 return err 658 } 659 } 660 } 661 } 662 output := []io.Writer{r.Writer, r.WriteCloser} 663 for _, out := range output { 664 if out != nil { 665 _, err := io.WriteString(out, ioTestText) 666 if err != nil { 667 return err 668 } 669 closer, ok := out.(io.Closer) 670 if ok { 671 err = closer.Close() 672 if err != nil { 673 return err 674 } 675 } 676 } 677 } 678 return nil 679 } 680 681 type ioFieldTest struct { 682 Args []string 683 Valid bool 684 Field string 685 InFiles []string 686 OutFiles []string 687 SkipReason string 688 } 689 690 var ioFieldTests = []ioFieldTest{ 691 // No-op 692 {Args: []string{}, Valid: true, InFiles: []string{}, OutFiles: []string{}}, 693 694 // io.Reader 695 {Args: []string{"--reader", "-"}, Valid: true, Field: "Reader", InFiles: []string{"stdin"}, OutFiles: []string{}}, 696 {Args: []string{"--reader", "infile"}, Valid: true, Field: "Reader", InFiles: []string{"infile"}, OutFiles: []string{}}, 697 {Args: []string{"--reader", "bogus/infile"}, Valid: false}, 698 {Args: []string{"--reader", ""}, Valid: false}, 699 {Args: []string{"--reader", "infile1", "--reader", "intput2"}, Valid: false}, 700 {Args: []string{"--reader", "-", "--reader", "intput"}, Valid: false}, 701 {Args: []string{"--reader", "infile", "--reader", "-"}, Valid: false}, 702 {Args: []string{"--reader"}, Valid: false}, 703 704 // io.ReadCloser 705 {Args: []string{"--readcloser", "-"}, Valid: true, Field: "ReadCloser", InFiles: []string{"stdin"}, OutFiles: []string{}}, 706 {Args: []string{"--readcloser", "infile"}, Valid: true, Field: "ReadCloser", InFiles: []string{"infile"}, OutFiles: []string{}}, 707 {Args: []string{"--readcloser", "bogus/infile"}, Valid: false}, 708 {Args: []string{"--readcloser", ""}, Valid: false}, 709 {Args: []string{"--readcloser", "infile1", "--readcloser", "intput2"}, Valid: false}, 710 {Args: []string{"--readcloser", "-", "--readcloser", "intput"}, Valid: false}, 711 {Args: []string{"--readcloser", "infile", "--readcloser", "-"}, Valid: false}, 712 {Args: []string{"--readcloser"}, Valid: false}, 713 714 // io.Writer 715 {Args: []string{"--writer", "-"}, Valid: true, Field: "Writer", InFiles: []string{}, OutFiles: []string{"stdout"}}, 716 {Args: []string{"--writer", "outfile"}, Valid: true, Field: "Writer", InFiles: []string{}, OutFiles: []string{"outfile"}}, 717 {Args: []string{"--writer", ""}, Valid: false}, 718 {Args: []string{"--writer", "bogus/outfile"}, Valid: false}, 719 {Args: []string{"--writer", "outfile1", "--writer", "outfile2"}, Valid: false}, 720 {Args: []string{"--writer", "-", "--writer", "outfile"}, Valid: false}, 721 {Args: []string{"--writer", "outfile", "--writer", "-"}, Valid: false}, 722 {Args: []string{"--writer"}, Valid: false}, 723 724 // io.WriteCloser 725 {Args: []string{"--writecloser", "-"}, Valid: true, Field: "WriteCloser", InFiles: []string{}, OutFiles: []string{"stdout"}}, 726 {Args: []string{"--writecloser", "outfile"}, Valid: true, Field: "WriteCloser", InFiles: []string{}, OutFiles: []string{"outfile"}}, 727 {Args: []string{"--writecloser", ""}, Valid: false}, 728 {Args: []string{"--writecloser", "bogus/outfile"}, Valid: false}, 729 {Args: []string{"--writecloser", "outfile1", "--writecloser", "outfile2"}, Valid: false}, 730 {Args: []string{"--writecloser", "-", "--writecloser", "outfile"}, Valid: false}, 731 {Args: []string{"--writecloser", "outfile", "--writecloser", "-"}, Valid: false}, 732 {Args: []string{"--writecloser"}, Valid: false}, 733 } 734 735 func TestIOFields(t *testing.T) { 736 for _, test := range ioFieldTests { 737 spec := &ioFieldSpec{} 738 runIOFieldTest(t, spec, test) 739 } 740 } 741 742 func runIOFieldTest(t *testing.T, spec *ioFieldSpec, test ioFieldTest) { 743 if test.SkipReason != "" { 744 t.Logf("Test skipped. Args: %q, Field: %s, Reason: %s", test.Args, test.Field, test.SkipReason) 745 return 746 } 747 748 realin, realout := os.Stdin, os.Stdout 749 defer restoreStdinStdout(realin, realout) 750 751 realdir, err := os.Getwd() 752 if err != nil { 753 t.Errorf("Failed to get working dir. Args: %q, Field: %s, Error: %s", test.Args, test.Field, err) 754 return 755 } 756 defer restoreWorkingDir(realdir) 757 758 err = setupIOFieldTest(test) 759 if err != nil { 760 t.Errorf("Failed to setup test. Args: %q, Field: %s, Error: %s", test.Args, test.Field, err) 761 return 762 } 763 764 cmd := New("test", spec) 765 _, _, err = cmd.Decode(test.Args) 766 if !test.Valid { 767 if err == nil { 768 t.Errorf("Expected error but none received. Args: %q", test.Args) 769 } 770 return 771 } 772 if err != nil { 773 t.Errorf("Received unexpected decode error. Args: %q, Field: %s, Error: %s", test.Args, test.Field, err) 774 return 775 } 776 777 err = validateIOFieldTest(spec, test) 778 if err != nil { 779 t.Errorf("Validation failed during IO field test. Args: %q, Field: %s, Error: %s", test.Args, test.Field, err) 780 return 781 } 782 } 783 784 func restoreStdinStdout(stdin *os.File, stdout *os.File) { 785 os.Stdin = stdin 786 os.Stdout = stdout 787 } 788 789 func restoreWorkingDir(dir string) { 790 err := os.Chdir(dir) 791 if err != nil { 792 panic(fmt.Sprintf("Failed to restore working dir. Dir: %q, Error: %s", dir, err)) 793 } 794 } 795 796 func setupIOFieldTest(test ioFieldTest) error { 797 tmpdir, err := ioutil.TempDir("", "writ-iofieldtest") 798 if err != nil { 799 return err 800 } 801 err = os.Chdir(tmpdir) 802 if err != nil { 803 return err 804 } 805 806 for _, name := range test.InFiles { 807 f, err := os.Create(name) 808 if err != nil { 809 return err 810 } 811 _, err = io.WriteString(f, ioTestText) 812 if err != nil { 813 return err 814 } 815 err = f.Close() 816 if err != nil { 817 return err 818 } 819 if name == "stdin" { 820 in, err := os.Open(name) 821 if err != nil { 822 return err 823 } 824 os.Stdin = in 825 } 826 } 827 for _, name := range test.OutFiles { 828 if name == "stdout" { 829 out, err := os.Create(name) 830 if err != nil { 831 return err 832 } 833 os.Stdout = out 834 } 835 } 836 return nil 837 } 838 839 func validateIOFieldTest(spec *ioFieldSpec, test ioFieldTest) error { 840 err := spec.PerformIO() 841 if err != nil { 842 return err 843 } 844 for _, name := range test.OutFiles { 845 in, err := os.Open(name) 846 if err != nil { 847 return err 848 } 849 bytes, err := ioutil.ReadAll(in) 850 if err != nil { 851 return err 852 } 853 if string(bytes) != ioTestText { 854 return fmt.Errorf("Expected to read %q. Read %q instead.", ioTestText, string(bytes)) 855 } 856 err = in.Close() 857 if err != nil { 858 return err 859 } 860 } 861 return nil 862 } 863 864 /* 865 * Test custom flag and option decoders 866 */ 867 868 type customTestFlag struct { 869 val *bool 870 } 871 872 func (d customTestFlag) Decode(arg string) error { 873 *d.val = true 874 return nil 875 } 876 877 type customTestFlagPtr struct { 878 val bool 879 } 880 881 func (d *customTestFlagPtr) Decode(arg string) error { 882 d.val = true 883 return nil 884 } 885 886 type customTestOption struct { 887 val *string 888 } 889 890 func (d customTestOption) Decode(arg string) error { 891 if strings.HasPrefix(arg, "foo") { 892 *d.val = arg 893 return nil 894 } 895 return fmt.Errorf("customTestOption values must begin with foo") 896 } 897 898 type customTestOptionPtr struct { 899 val string 900 } 901 902 func (d *customTestOptionPtr) Decode(arg string) error { 903 if strings.HasPrefix(arg, "foo") { 904 d.val = arg 905 return nil 906 } 907 return fmt.Errorf("customTestOptionPtr values must begin with foo") 908 } 909 910 type customDecoderFieldSpec struct { 911 CustomFlag customTestFlag `flag:"flag" description:"a custom flag field"` 912 CustomFlagPtr customTestFlagPtr `flag:"flagptr" description:"a custom flag field with pointer receiver"` 913 CustomOption customTestOption `option:"opt" description:"a custom option field"` 914 CustomOptionPtr customTestOptionPtr `option:"optptr" description:"a custom option field with pointer receiver"` 915 } 916 917 var trueval = true 918 var foobarval = "foobar" 919 920 var customDecoderFieldTests = []fieldTest{ 921 // Custom flag 922 {Args: []string{"--flag"}, Valid: true, Field: "CustomFlag", Value: customTestFlag{val: &trueval}}, 923 {Args: []string{"--flag", "--flag"}, Valid: false}, // Plural must be set explicitly 924 925 // Custom flag with pointer receiver 926 {Args: []string{"--flagptr"}, Valid: true, Field: "CustomFlagPtr", Value: customTestFlagPtr{val: true}}, 927 {Args: []string{"--flagptr", "--flagptr"}, Valid: false}, // Plural must be set explicitly 928 929 // Custom option 930 {Args: []string{"--opt", "foobar"}, Valid: true, Field: "CustomOption", Value: customTestOption{val: &foobarval}}, 931 {Args: []string{"--opt=foobar"}, Valid: true, Field: "CustomOption", Value: customTestOption{val: &foobarval}}, 932 {Args: []string{"-opt=puppies"}, Valid: false}, 933 {Args: []string{"-opt", "puppies"}, Valid: false}, 934 {Args: []string{"--opt"}, Valid: false}, 935 {Args: []string{"--opt", "foobar", "-opt", "foobar"}, Valid: false}, // Plural must be set explicitly 936 937 // Custom option with pointer receiver 938 {Args: []string{"--optptr", "foobar"}, Valid: true, Field: "CustomOptionPtr", Value: customTestOptionPtr{val: "foobar"}}, 939 {Args: []string{"--optptr=foobar"}, Valid: true, Field: "CustomOptionPtr", Value: customTestOptionPtr{val: "foobar"}}, 940 {Args: []string{"-optptr=puppies"}, Valid: false}, 941 {Args: []string{"-optptr", "puppies"}, Valid: false}, 942 {Args: []string{"--optptr"}, Valid: false}, 943 {Args: []string{"--optptr", "foobar", "-optptr", "foobar"}, Valid: false}, // Plural must be set explicitly 944 } 945 946 func TestCustomDecoderFields(t *testing.T) { 947 for _, test := range customDecoderFieldTests { 948 var flagval bool 949 var optval string 950 spec := &customDecoderFieldSpec{ 951 CustomFlag: customTestFlag{&flagval}, 952 CustomOption: customTestOption{&optval}, 953 } 954 runFieldTest(t, spec, test) 955 } 956 } 957 958 /* 959 * Test basic field types 960 */ 961 962 type basicFieldSpec struct { 963 Int int `option:"int" description:"An int option" placeholder:"INT"` 964 Int8 int8 `option:"int8" description:"An int8 option" placeholder:"INT8"` 965 Int16 int16 `option:"int16" description:"An int16 option" placeholder:"INT16"` 966 Int32 int32 `option:"int32" description:"An int32 option" placeholder:"INT32"` 967 Int64 int64 `option:"int64" description:"An int64 option" placeholder:"INT64"` 968 Uint uint `option:"uint" description:"A uint option" placeholder:"UINT"` 969 Uint8 uint8 `option:"uint8" description:"A uint8 option" placeholder:"UINT8"` 970 Uint16 uint16 `option:"uint16" description:"A uint16 option" placeholder:"UINT16"` 971 Uint32 uint32 `option:"uint32" description:"A uint32 option" placeholder:"UINT32"` 972 Uint64 uint64 `option:"uint64" description:"A uint64 option" placeholder:"UINT64"` 973 Float32 float32 `option:"float32" description:"A float32 option" placeholder:"FLOAT32"` 974 Float64 float64 `option:"float64" description:"A float64 option" placeholder:"FLOAT64"` 975 String string `option:"string" description:"A string option" placeholder:"STRING"` 976 } 977 978 var basicFieldTests = []fieldTest{ 979 // String 980 {Args: []string{"--string", "1"}, Valid: true, Field: "String", Value: "1"}, 981 {Args: []string{"--string", "-1"}, Valid: true, Field: "String", Value: "-1"}, 982 {Args: []string{"--string", "+1"}, Valid: true, Field: "String", Value: "+1"}, 983 {Args: []string{"--string", "1.0"}, Valid: true, Field: "String", Value: "1.0"}, 984 {Args: []string{"--string", "0x01"}, Valid: true, Field: "String", Value: "0x01"}, 985 {Args: []string{"--string", "-"}, Valid: true, Field: "String", Value: "-"}, 986 {Args: []string{"--string", "-a"}, Valid: true, Field: "String", Value: "-a"}, 987 {Args: []string{"--string", "--"}, Valid: true, Field: "String", Value: "--"}, 988 {Args: []string{"--string", "--a"}, Valid: true, Field: "String", Value: "--a"}, 989 {Args: []string{"--string", ""}, Valid: true, Field: "String", Value: ""}, 990 {Args: []string{"--string", " "}, Valid: true, Field: "String", Value: " "}, 991 {Args: []string{"--string", " a"}, Valid: true, Field: "String", Value: " a"}, 992 {Args: []string{"--string", "a "}, Valid: true, Field: "String", Value: "a "}, 993 {Args: []string{"--string", "a b "}, Valid: true, Field: "String", Value: "a b "}, 994 {Args: []string{"--string", " a b"}, Valid: true, Field: "String", Value: " a b"}, 995 {Args: []string{"--string", "\n"}, Valid: true, Field: "String", Value: "\n"}, 996 {Args: []string{"--string", "\t"}, Valid: true, Field: "String", Value: "\t"}, 997 {Args: []string{"--string", "日本"}, Valid: true, Field: "String", Value: "日本"}, 998 {Args: []string{"--string", "-日本"}, Valid: true, Field: "String", Value: "-日本"}, 999 {Args: []string{"--string", "--日本"}, Valid: true, Field: "String", Value: "--日本"}, 1000 {Args: []string{"--string", " 日本"}, Valid: true, Field: "String", Value: " 日本"}, 1001 {Args: []string{"--string", "日本 "}, Valid: true, Field: "String", Value: "日本 "}, 1002 {Args: []string{"--string", "日 本"}, Valid: true, Field: "String", Value: "日 本"}, 1003 {Args: []string{"--string", "A relatively long string to make sure we aren't doing any silly truncation anywhere, since that would be bad..."}, Valid: true, Field: "String", Value: "A relatively long string to make sure we aren't doing any silly truncation anywhere, since that would be bad..."}, 1004 {Args: []string{"--string", "a", "--string", "b"}, Valid: false}, 1005 {Args: []string{"--string"}, Valid: false}, 1006 1007 // Int8 1008 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int8", Value: int8(math.MinInt8)}, 1009 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int8", Value: int8(math.MaxInt8)}, 1010 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false}, 1011 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: false}, 1012 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false}, 1013 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false}, 1014 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: false}, 1015 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: false}, 1016 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false}, 1017 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1018 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: false}, 1019 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false}, 1020 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false}, 1021 {Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false}, 1022 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false}, 1023 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: false}, 1024 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: false}, 1025 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: false}, 1026 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: false}, 1027 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false}, 1028 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false}, 1029 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1030 {Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1031 {Args: []string{"--int8", "1", "--int8", "2"}, Valid: false}, 1032 {Args: []string{"--int8", "1.0"}, Valid: false}, 1033 {Args: []string{"--int8", ""}, Valid: false}, 1034 {Args: []string{"--int8"}, Valid: false}, 1035 1036 // Int16 1037 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int16", Value: int16(math.MinInt8)}, 1038 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: true, Field: "Int16", Value: int16(math.MinInt8 - 1)}, 1039 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int16", Value: int16(math.MaxInt8)}, 1040 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Int16", Value: int16(math.MaxInt8 + 1)}, 1041 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: true, Field: "Int16", Value: int16(math.MinInt16)}, 1042 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Int16", Value: int16(math.MaxInt16)}, 1043 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Int16", Value: int16(math.MaxUint8)}, 1044 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Int16", Value: int16(math.MaxUint8 + 1)}, 1045 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false}, 1046 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: false}, 1047 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false}, 1048 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1049 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: false}, 1050 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false}, 1051 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false}, 1052 {Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false}, 1053 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false}, 1054 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: false}, 1055 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: false}, 1056 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false}, 1057 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false}, 1058 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1059 {Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1060 {Args: []string{"--int16", "1", "--int16", "2"}, Valid: false}, 1061 {Args: []string{"--int16", "1.0"}, Valid: false}, 1062 {Args: []string{"--int16", ""}, Valid: false}, 1063 {Args: []string{"--int16"}, Valid: false}, 1064 1065 // Int32 1066 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int32", Value: int32(math.MinInt8)}, 1067 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: true, Field: "Int32", Value: int32(math.MinInt8 - 1)}, 1068 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt8)}, 1069 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt8 + 1)}, 1070 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: true, Field: "Int32", Value: int32(math.MinInt16)}, 1071 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: true, Field: "Int32", Value: int32(math.MinInt16 - 1)}, 1072 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt16)}, 1073 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt16 + 1)}, 1074 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: true, Field: "Int32", Value: int32(math.MinInt32)}, 1075 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt32)}, 1076 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Int32", Value: int32(math.MaxUint8)}, 1077 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Int32", Value: int32(math.MaxUint8 + 1)}, 1078 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Int32", Value: int32(math.MaxUint16)}, 1079 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Int32", Value: int32(math.MaxUint16 + 1)}, 1080 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1081 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false}, 1082 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false}, 1083 {Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false}, 1084 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false}, 1085 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false}, 1086 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false}, 1087 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1088 {Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1089 {Args: []string{"--int32", "1", "--int32", "2"}, Valid: false}, 1090 {Args: []string{"--int32", "1.0"}, Valid: false}, 1091 {Args: []string{"--int32", ""}, Valid: false}, 1092 {Args: []string{"--int32"}, Valid: false}, 1093 1094 // Int64 1095 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int64", Value: int64(math.MinInt8)}, 1096 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: true, Field: "Int64", Value: int64(math.MinInt8 - 1)}, 1097 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt8)}, 1098 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt8 + 1)}, 1099 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: true, Field: "Int64", Value: int64(math.MinInt16)}, 1100 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: true, Field: "Int64", Value: int64(math.MinInt16 - 1)}, 1101 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt16)}, 1102 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt16 + 1)}, 1103 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: true, Field: "Int64", Value: int64(math.MinInt32)}, 1104 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: true, Field: "Int64", Value: int64(math.MinInt32 - 1)}, 1105 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt32)}, 1106 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt32 + 1)}, 1107 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: true, Field: "Int64", Value: int64(math.MinInt64)}, 1108 {Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt64)}, 1109 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint8)}, 1110 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint8 + 1)}, 1111 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint16)}, 1112 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint16 + 1)}, 1113 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint32)}, 1114 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint32 + 1)}, 1115 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false}, 1116 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1117 {Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1118 {Args: []string{"--int64", "1", "--int64", "2"}, Valid: false}, 1119 {Args: []string{"--int64", "1.0"}, Valid: false}, 1120 {Args: []string{"--int64", ""}, Valid: false}, 1121 {Args: []string{"--int64"}, Valid: false}, 1122 1123 // Int 1124 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int", Value: int(math.MinInt8)}, 1125 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: true, Field: "Int", Value: int(math.MinInt8 - 1)}, 1126 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int", Value: int(math.MaxInt8)}, 1127 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Int", Value: int(math.MaxInt8 + 1)}, 1128 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: true, Field: "Int", Value: int(math.MinInt16)}, 1129 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: true, Field: "Int", Value: int(math.MinInt16 - 1)}, 1130 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Int", Value: int(math.MaxInt16)}, 1131 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Int", Value: int(math.MaxInt16 + 1)}, 1132 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: true, Field: "Int", Value: int(math.MinInt32)}, 1133 {Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Int", Value: int(math.MaxInt32)}, 1134 {Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Int", Value: int(math.MaxUint8)}, 1135 {Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Int", Value: int(math.MaxUint8 + 1)}, 1136 {Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Int", Value: int(math.MaxUint16)}, 1137 {Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Int", Value: int(math.MaxUint16 + 1)}, 1138 {Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false}, 1139 {Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1140 {Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1141 {Args: []string{"--int", "1", "--int", "2"}, Valid: false}, 1142 {Args: []string{"--int", "1.0"}, Valid: false}, 1143 {Args: []string{"--int", ""}, Valid: false}, 1144 {Args: []string{"--int"}, Valid: false}, 1145 1146 // Uint8 1147 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint8", Value: uint8(math.MaxInt8)}, 1148 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint8", Value: uint8(math.MaxInt8 + 1)}, 1149 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint8", Value: uint8(math.MaxUint8)}, 1150 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false}, 1151 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false}, 1152 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false}, 1153 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false}, 1154 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: false}, 1155 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: false}, 1156 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false}, 1157 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1158 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: false}, 1159 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false}, 1160 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false}, 1161 {Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false}, 1162 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false}, 1163 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: false}, 1164 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: false}, 1165 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: false}, 1166 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false}, 1167 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false}, 1168 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1169 {Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1170 {Args: []string{"--uint8", "1", "--uint8", "2"}, Valid: false}, 1171 {Args: []string{"--uint8", "1.0"}, Valid: false}, 1172 {Args: []string{"--uint8", ""}, Valid: false}, 1173 {Args: []string{"--uint8"}, Valid: false}, 1174 1175 // Uint16 1176 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxInt8)}, 1177 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxInt8 + 1)}, 1178 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxInt16)}, 1179 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxInt16 + 1)}, 1180 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxUint8)}, 1181 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxUint8 + 1)}, 1182 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxUint16)}, 1183 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false}, 1184 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false}, 1185 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false}, 1186 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false}, 1187 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false}, 1188 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1189 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: false}, 1190 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false}, 1191 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false}, 1192 {Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false}, 1193 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false}, 1194 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: false}, 1195 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false}, 1196 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false}, 1197 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1198 {Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1199 {Args: []string{"--uint16", "1", "--uint16", "2"}, Valid: false}, 1200 {Args: []string{"--uint16", "1.0"}, Valid: false}, 1201 {Args: []string{"--uint16", ""}, Valid: false}, 1202 {Args: []string{"--uint16"}, Valid: false}, 1203 1204 // Uint32 1205 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt8)}, 1206 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt8 + 1)}, 1207 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt16)}, 1208 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt16 + 1)}, 1209 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt32)}, 1210 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt32 + 1)}, 1211 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint8)}, 1212 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint8 + 1)}, 1213 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint16)}, 1214 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint16 + 1)}, 1215 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint32)}, 1216 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false}, 1217 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false}, 1218 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false}, 1219 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false}, 1220 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false}, 1221 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1222 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false}, 1223 {Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false}, 1224 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false}, 1225 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false}, 1226 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1227 {Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false}, 1228 {Args: []string{"--uint32", "1", "--uint32", "1"}, Valid: false}, 1229 {Args: []string{"--uint32", "1.0"}, Valid: false}, 1230 {Args: []string{"--uint32", ""}, Valid: false}, 1231 {Args: []string{"--uint32"}, Valid: false}, 1232 1233 // Uint64 1234 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt8)}, 1235 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt8 + 1)}, 1236 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt16)}, 1237 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt16 + 1)}, 1238 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt32)}, 1239 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt32 + 1)}, 1240 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt64)}, 1241 {Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt64 + 1)}, 1242 {Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint8)}, 1243 {Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint8 + 1)}, 1244 {Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint16)}, 1245 {Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint16 + 1)}, 1246 {Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint32)}, 1247 {Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint32 + 1)}, 1248 {Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint64)}, 1249 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false}, 1250 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false}, 1251 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false}, 1252 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false}, 1253 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false}, 1254 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1255 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false}, 1256 {Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false}, 1257 {Args: []string{"--uint64", "1", "--uint64", "1"}, Valid: false}, 1258 {Args: []string{"--uint64", "1.0"}, Valid: false}, 1259 {Args: []string{"--uint64", ""}, Valid: false}, 1260 {Args: []string{"--uint64"}, Valid: false}, 1261 1262 // Uint 1263 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt8)}, 1264 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt8 + 1)}, 1265 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt16)}, 1266 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt16 + 1)}, 1267 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt32)}, 1268 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt32 + 1)}, 1269 {Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint8)}, 1270 {Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint8 + 1)}, 1271 {Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint16)}, 1272 {Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint16 + 1)}, 1273 {Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint32)}, 1274 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false}, 1275 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false}, 1276 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false}, 1277 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false}, 1278 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false}, 1279 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1280 {Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false}, 1281 {Args: []string{"--uint", "1", "--uint", "2"}, Valid: false}, 1282 {Args: []string{"--uint", "1.0"}, Valid: false}, 1283 {Args: []string{"--uint", ""}, Valid: false}, 1284 {Args: []string{"--uint"}, Valid: false}, 1285 1286 // Float32 1287 {Args: []string{"--float32", "-1.23"}, Valid: true, Field: "Float32", Value: float32(-1.23)}, 1288 {Args: []string{"--float32", "4.56"}, Valid: true, Field: "Float32", Value: float32(4.56)}, 1289 {Args: []string{"--float32", "-1.2e3"}, Valid: true, Field: "Float32", Value: float32(-1.2e3)}, 1290 {Args: []string{"--float32", "4.5e6"}, Valid: true, Field: "Float32", Value: float32(4.5e6)}, 1291 {Args: []string{"--float32", "-1.2E3"}, Valid: true, Field: "Float32", Value: float32(-1.2e3)}, 1292 {Args: []string{"--float32", "4.5E6"}, Valid: true, Field: "Float32", Value: float32(4.5e6)}, 1293 {Args: []string{"--float32", "-1.2e+3"}, Valid: true, Field: "Float32", Value: float32(-1.2e3)}, 1294 {Args: []string{"--float32", "4.5e+6"}, Valid: true, Field: "Float32", Value: float32(4.5e6)}, 1295 {Args: []string{"--float32", "-1.2E+3"}, Valid: true, Field: "Float32", Value: float32(-1.2e3)}, 1296 {Args: []string{"--float32", "4.5E+6"}, Valid: true, Field: "Float32", Value: float32(4.5e6)}, 1297 {Args: []string{"--float32", "-1.2e-3"}, Valid: true, Field: "Float32", Value: float32(-1.2e-3)}, 1298 {Args: []string{"--float32", "4.5e-6"}, Valid: true, Field: "Float32", Value: float32(4.5e-6)}, 1299 {Args: []string{"--float32", "-1.2E-3"}, Valid: true, Field: "Float32", Value: float32(-1.2e-3)}, 1300 {Args: []string{"--float32", "4.5E-6"}, Valid: true, Field: "Float32", Value: float32(4.5e-6)}, 1301 {Args: []string{"--float32", strconv.FormatFloat(math.SmallestNonzeroFloat32, 'f', -1, 64)}, Valid: true, Field: "Float32", Value: float32(math.SmallestNonzeroFloat32)}, 1302 {Args: []string{"--float32", strconv.FormatFloat(math.MaxFloat32, 'f', -1, 64)}, Valid: true, Field: "Float32", Value: float32(math.MaxFloat32)}, 1303 {Args: []string{"--float32", strconv.FormatFloat(math.MaxFloat32, 'f', -1, 64)}, Valid: true, Field: "Float32", Value: float32(math.MaxFloat32)}, 1304 // XXX Skipped -- Not sure how to handle this!! 1305 {Args: []string{"--float32", strconv.FormatFloat(math.SmallestNonzeroFloat64, 'f', -1, 64)}, Field: "Float32", SkipReason: "Not sure how to handle the precision on this"}, 1306 {Args: []string{"--float32", strconv.FormatFloat(math.MaxFloat64, 'f', -1, 64)}, Valid: false}, 1307 {Args: []string{"--float32", strconv.FormatFloat(math.MaxFloat64, 'f', -1, 64)}, Valid: false}, 1308 {Args: []string{"--float32", "1"}, Valid: true, Field: "Float32", Value: float32(1)}, 1309 {Args: []string{"--float32", "-1"}, Valid: true, Field: "Float32", Value: float32(-1)}, 1310 {Args: []string{"--float32", "1.0", "--float32", "2.0"}, Valid: false}, 1311 {Args: []string{"--float32", ""}, Valid: false}, 1312 {Args: []string{"--float32"}, Valid: false}, 1313 1314 // Float64 1315 {Args: []string{"--float64", "-1.23"}, Valid: true, Field: "Float64", Value: float64(-1.23)}, 1316 {Args: []string{"--float64", "4.56"}, Valid: true, Field: "Float64", Value: float64(4.56)}, 1317 {Args: []string{"--float64", "-1.2e3"}, Valid: true, Field: "Float64", Value: float64(-1.2e3)}, 1318 {Args: []string{"--float64", "4.5e6"}, Valid: true, Field: "Float64", Value: float64(4.5e6)}, 1319 {Args: []string{"--float64", "-1.2E3"}, Valid: true, Field: "Float64", Value: float64(-1.2e3)}, 1320 {Args: []string{"--float64", "4.5E6"}, Valid: true, Field: "Float64", Value: float64(4.5e6)}, 1321 {Args: []string{"--float64", "-1.2e+3"}, Valid: true, Field: "Float64", Value: float64(-1.2e3)}, 1322 {Args: []string{"--float64", "4.5e+6"}, Valid: true, Field: "Float64", Value: float64(4.5e6)}, 1323 {Args: []string{"--float64", "-1.2E+3"}, Valid: true, Field: "Float64", Value: float64(-1.2e3)}, 1324 {Args: []string{"--float64", "4.5E+6"}, Valid: true, Field: "Float64", Value: float64(4.5e6)}, 1325 {Args: []string{"--float64", "-1.2e-3"}, Valid: true, Field: "Float64", Value: float64(-1.2e-3)}, 1326 {Args: []string{"--float64", "4.5e-6"}, Valid: true, Field: "Float64", Value: float64(4.5e-6)}, 1327 {Args: []string{"--float64", "-1.2E-3"}, Valid: true, Field: "Float64", Value: float64(-1.2e-3)}, 1328 {Args: []string{"--float64", "4.5E-6"}, Valid: true, Field: "Float64", Value: float64(4.5e-6)}, 1329 {Args: []string{"--float64", strconv.FormatFloat(math.SmallestNonzeroFloat32, 'f', -1, 64)}, Valid: true, Field: "Float64", Value: float64(math.SmallestNonzeroFloat32)}, 1330 {Args: []string{"--float64", strconv.FormatFloat(math.MaxFloat32, 'f', -1, 64)}, Valid: true, Field: "Float64", Value: float64(math.MaxFloat32)}, 1331 {Args: []string{"--float64", strconv.FormatFloat(math.SmallestNonzeroFloat64, 'f', -1, 64)}, Valid: true, Field: "Float64", Value: float64(math.SmallestNonzeroFloat64)}, 1332 {Args: []string{"--float64", strconv.FormatFloat(math.MaxFloat64, 'f', -1, 64)}, Valid: true, Field: "Float64", Value: float64(math.MaxFloat64)}, 1333 {Args: []string{"--float64", "1"}, Valid: true, Field: "Float64", Value: float64(1)}, 1334 {Args: []string{"--float64", "-1"}, Valid: true, Field: "Float64", Value: float64(-1)}, 1335 {Args: []string{"--float64", "1.0", "--float64", "2.0"}, Valid: false}, 1336 {Args: []string{"--float64", ""}, Valid: false}, 1337 {Args: []string{"--float64"}, Valid: false}, 1338 } 1339 1340 func TestBasicFields(t *testing.T) { 1341 for _, test := range basicFieldTests { 1342 spec := &basicFieldSpec{} 1343 runFieldTest(t, spec, test) 1344 } 1345 } 1346 1347 /* 1348 * Test invalid specs 1349 */ 1350 1351 var invalidSpecTests = []struct { 1352 Description string 1353 Spec interface{} 1354 }{ 1355 // Invalid command specs 1356 { 1357 Description: "Commands must have a name 1", 1358 Spec: &struct { 1359 Command struct{} `command:","` 1360 }{}, 1361 }, 1362 { 1363 Description: "Commands must have a name 2", 1364 Spec: &struct { 1365 Command struct{} `command:" "` 1366 }{}, 1367 }, 1368 { 1369 Description: "Commands must have a single name", 1370 Spec: &struct { 1371 Command struct{} `command:"one,two"` 1372 }{}, 1373 }, 1374 { 1375 Description: "Command names cannot have a leading '-' prefix", 1376 Spec: &struct { 1377 Command struct{} `command:"-command"` 1378 }{}, 1379 }, 1380 { 1381 Description: "Command aliases cannot have a leading '-' prefix", 1382 Spec: &struct { 1383 Command struct{} `command:"command" alias:"-alias"` 1384 }{}, 1385 }, 1386 { 1387 Description: "Commands cannot have placeholders", 1388 Spec: &struct { 1389 Command struct{} `command:"command" placeholder:"PLACEHOLDER"` 1390 }{}, 1391 }, 1392 { 1393 Description: "Commands cannot have default values", 1394 Spec: &struct { 1395 Command struct{} `command:"command" default:"default"` 1396 }{}, 1397 }, 1398 { 1399 Description: "Commands cannot have env values", 1400 Spec: &struct { 1401 Command struct{} `command:"command" env:"ENV_VALUE"` 1402 }{}, 1403 }, 1404 { 1405 Description: "Command fields must be exported", 1406 Spec: &struct { 1407 command struct{} `command:"command"` 1408 }{}, 1409 }, 1410 { 1411 Description: "Command and alias names must be unique 1", 1412 Spec: &struct { 1413 Command struct{} `command:"foo" alias:"foo"` 1414 }{}, 1415 }, 1416 { 1417 Description: "Command and alias names must be unique 2", 1418 Spec: &struct { 1419 Command1 struct{} `command:"foo"` 1420 Command2 struct{} `command:"foo"` 1421 }{}, 1422 }, 1423 { 1424 Description: "Command and alias names must be unique 3", 1425 Spec: &struct { 1426 Command1 struct{} `command:"foo"` 1427 Command2 struct{} `command:"b" alias:"foo"` 1428 }{}, 1429 }, 1430 { 1431 Description: "Command and alias names must be unique 4", 1432 Spec: &struct { 1433 Command1 struct{} `command:"a" alias:"foo"` 1434 Command2 struct{} `command:"b" alias:"foo"` 1435 }{}, 1436 }, 1437 { 1438 Description: "Command specs must be a pointer to struct 1", 1439 Spec: struct{}{}, 1440 }, 1441 { 1442 Description: "Command specs must be a pointer to struct 2", 1443 Spec: 42, 1444 }, 1445 { 1446 Description: "Command specs must be a pointer to struct 3", 1447 Spec: (*int)(nil), 1448 }, 1449 1450 // Invalid option specs 1451 { 1452 Description: "Options cannot have aliases", 1453 Spec: &struct { 1454 Option int `option:"option" alias:"alias" description:"option with an alias"` 1455 }{}, 1456 }, 1457 { 1458 Description: "Options must have a name 1", 1459 Spec: &struct { 1460 Option int `option:"," description:"option with no name"` 1461 }{}, 1462 }, 1463 { 1464 Description: "Options must have a name 2", 1465 Spec: &struct { 1466 Option int `option:" " description:"option with no name"` 1467 }{}, 1468 }, 1469 { 1470 Description: "Long option names cannot have a leading '-' prefix", 1471 Spec: &struct { 1472 Option int `option:"-option" description:"leading dash prefix"` 1473 }{}, 1474 }, 1475 { 1476 Description: "Short option names cannot have a leading '-' prefix", 1477 Spec: &struct { 1478 Option int `option:"-o" description:"leading dash prefix"` 1479 }{}, 1480 }, 1481 { 1482 Description: "Option fields must be exported", 1483 Spec: &struct { 1484 option int `option:"option" description:"non-exported field"` 1485 }{}, 1486 }, 1487 { 1488 Description: "Bools cannot be options", 1489 Spec: &struct { 1490 Option bool `option:"b" description:"boolean option"` 1491 }{}, 1492 }, 1493 { 1494 Description: "Option names must be unique 1", 1495 Spec: &struct { 1496 Option1 int `option:"foo"` 1497 Option2 int `option:"foo"` 1498 }{}, 1499 }, 1500 { 1501 Description: "Option names must be unique 2", 1502 Spec: &struct { 1503 Option1 int `option:"a, foo"` 1504 Option2 int `option:"b, foo"` 1505 }{}, 1506 }, 1507 { 1508 Description: "Option names must be unique 3", 1509 Spec: &struct { 1510 Flag bool `flag:"foo"` 1511 Option int `option:"foo"` 1512 }{}, 1513 }, 1514 { 1515 Description: "Not a supported option type", 1516 Spec: &struct { 1517 Option map[string]int `option:"foo"` 1518 }{}, 1519 }, 1520 1521 // Invalid flag specs 1522 { 1523 Description: "Flags cannot have aliases", 1524 Spec: &struct { 1525 Flag bool `flag:"flag" alias:"alias" description:"flag with an alias"` 1526 }{}, 1527 }, 1528 { 1529 Description: "Flags cannot have placeholders", 1530 Spec: &struct { 1531 Flag bool `flag:"flag" placeholder:"PLACEHOLDER" description:"placeholder on flag"` 1532 }{}, 1533 }, 1534 { 1535 Description: "Flags cannot have default values", 1536 Spec: &struct { 1537 Flag bool `flag:"flag" default:"default" description:"default on flag"` 1538 }{}, 1539 }, 1540 { 1541 Description: "Flags cannot have env values", 1542 Spec: &struct { 1543 Flag bool `flag:"flag" env:"ENV_VALUE" description:"env on flag"` 1544 }{}, 1545 }, 1546 { 1547 Description: "Flags must have a name 1", 1548 Spec: &struct { 1549 Flag bool `flag:"," description:"flag with no name"` 1550 }{}, 1551 }, 1552 { 1553 Description: "Flags must have a name 2", 1554 Spec: &struct { 1555 Flag bool `flag:" " description:"flag with no name"` 1556 }{}, 1557 }, 1558 { 1559 Description: "Long flag names cannot have a leading '-' prefix", 1560 Spec: &struct { 1561 Flag bool `flag:"-flag" description:"leading dash prefix"` 1562 }{}, 1563 }, 1564 { 1565 Description: "Short flag names cannot have a leading '-' prefix", 1566 Spec: &struct { 1567 Flag bool `flag:"-f" description:"leading dash prefix"` 1568 }{}, 1569 }, 1570 { 1571 Description: "Flag fields must be exported", 1572 Spec: &struct { 1573 flag int `flag:"flag" description:"non-exported field"` 1574 }{}, 1575 }, 1576 { 1577 Description: "Flag names must be unique 1", 1578 Spec: &struct { 1579 Flag1 bool `flag:"foo"` 1580 Flag2 bool `flag:"foo"` 1581 }{}, 1582 }, 1583 { 1584 Description: "Flag names must be unique 2", 1585 Spec: &struct { 1586 Flag1 bool `flag:"a, foo"` 1587 Flag2 bool `flag:"b, foo"` 1588 }{}, 1589 }, 1590 { 1591 Description: "Flag names must be unique 3", 1592 Spec: &struct { 1593 Flag bool `flag:"foo"` 1594 Option int `option:"foo"` 1595 }{}, 1596 }, 1597 { 1598 Description: "Flag may only be bools, ints, and OptionDecoders 1", 1599 Spec: &struct { 1600 Flag string `flag:"foo"` 1601 }{}, 1602 }, 1603 { 1604 Description: "Flag may only be bools, ints, and OptionDecoders 2", 1605 Spec: &struct { 1606 Flag int32 `flag:"foo"` 1607 }{}, 1608 }, 1609 { 1610 Description: "Flag may only be bools, ints, and OptionDecoders 3", 1611 Spec: &struct { 1612 Flag struct{} `flag:"foo"` 1613 }{}, 1614 }, 1615 1616 // Invalid mixes of command, flag, and option 1617 { 1618 Description: "Commands cannot be options", 1619 Spec: &struct { 1620 Command struct{} `command:"command" option:"option" description:"command as option"` 1621 }{}, 1622 }, 1623 { 1624 Description: "Commands cannot be flags", 1625 Spec: &struct { 1626 Command struct{} `command:"command" flag:"flag" description:"command as flag"` 1627 }{}, 1628 }, 1629 { 1630 Description: "Options cannot be commands", 1631 Spec: &struct { 1632 Option int `option:"option" command:"command" description:"option as command"` 1633 }{}, 1634 }, 1635 { 1636 Description: "Options cannot be flags", 1637 Spec: &struct { 1638 Option int `option:"option" flag:"flag" description:"option as flag"` 1639 }{}, 1640 }, 1641 { 1642 Description: "Flags cannot be commands", 1643 Spec: &struct { 1644 Flag bool `flag:"flag" command:"command" description:"flag as command"` 1645 }{}, 1646 }, 1647 { 1648 Description: "Flags cannot be options", 1649 Spec: &struct { 1650 Flag bool `flag:"flag" option:"option" description:"flag as option"` 1651 }{}, 1652 }, 1653 } 1654 1655 func TestInvalidSpecs(t *testing.T) { 1656 for _, test := range invalidSpecTests { 1657 err := newInvalidCommand(test.Spec) 1658 if err == nil { 1659 t.Errorf("Expected error creating spec, but none received. Test: %s", test.Description) 1660 continue 1661 } 1662 } 1663 } 1664 1665 func newInvalidCommand(spec interface{}) (err error) { 1666 defer func() { 1667 r := recover() 1668 if r != nil { 1669 switch e := r.(type) { 1670 case commandError: 1671 err = e 1672 case optionError: 1673 err = e 1674 default: 1675 panic(e) 1676 } 1677 } 1678 }() 1679 New("test", spec) 1680 return nil 1681 } 1682 1683 var invalidCommandTests = []struct { 1684 Description string 1685 Command *Command 1686 }{ 1687 { 1688 Description: "Command name cannot be empty", 1689 Command: &Command{Name: ""}, 1690 }, 1691 { 1692 Description: "Command names cannot begin with -", 1693 Command: &Command{Name: "-command"}, 1694 }, 1695 { 1696 Description: "Command aliases cannot begin with -", 1697 Command: &Command{Name: "command", Aliases: []string{"-alias"}}, 1698 }, 1699 { 1700 Description: "Command names cannot have spaces 1", 1701 Command: &Command{Name: " command"}, 1702 }, 1703 { 1704 Description: "Command names cannot have spaces 2", 1705 Command: &Command{Name: "command "}, 1706 }, 1707 { 1708 Description: "Command names cannot have spaces 3", 1709 Command: &Command{Name: "command spaces"}, 1710 }, 1711 { 1712 Description: "Command aliases cannot begin with -", 1713 Command: &Command{Name: "command", Aliases: []string{"-alias"}}, 1714 }, 1715 { 1716 Description: "Command aliases cannot have spaces 1", 1717 Command: &Command{Name: "command", Aliases: []string{" alias"}}, 1718 }, 1719 { 1720 Description: "Command aliases cannot have spaces 2", 1721 Command: &Command{Name: "command", Aliases: []string{"alias "}}, 1722 }, 1723 { 1724 Description: "Command aliases cannot have spaces 3", 1725 Command: &Command{Name: "command", Aliases: []string{"alias spaces"}}, 1726 }, 1727 } 1728 1729 func TestDirectCommandValidation(t *testing.T) { 1730 for _, test := range invalidCommandTests { 1731 err := checkInvalidCommand(test.Command) 1732 if err == nil { 1733 t.Errorf("Expected error validating command, but none received. Test: %s", test.Description) 1734 continue 1735 } 1736 } 1737 } 1738 1739 func checkInvalidCommand(cmd *Command) (err error) { 1740 defer func() { 1741 r := recover() 1742 if r != nil { 1743 switch e := r.(type) { 1744 case commandError: 1745 err = e 1746 case optionError: 1747 err = e 1748 default: 1749 panic(e) 1750 } 1751 } 1752 }() 1753 cmd.validate() 1754 return nil 1755 } 1756 1757 func TestGroupCommands(t *testing.T) { 1758 spec := &struct { 1759 Command1 struct{} `command:"command1"` 1760 Command2 struct{} `command:"command2"` 1761 }{} 1762 cmd := New("test", spec) 1763 1764 group := cmd.GroupCommands("command1") 1765 if len(group.Commands) != 1 || group.Commands[0].Name != "command1" { 1766 t.Errorf("Expected a single command group with command %q", "command1") 1767 } 1768 group = cmd.GroupCommands("command2") 1769 if len(group.Commands) != 1 || group.Commands[0].Name != "command2" { 1770 t.Errorf("Expected a single command group with command %q", "command2") 1771 } 1772 group = cmd.GroupCommands("command1", "command2") 1773 if len(group.Commands) != 2 || group.Commands[0].Name != "command1" || group.Commands[1].Name != "command2" { 1774 t.Errorf("Expected a single command group with commands %q and %q", "command1", "command2") 1775 } 1776 group = cmd.GroupCommands("command2", "command1") 1777 if len(group.Commands) != 2 || group.Commands[0].Name != "command2" || group.Commands[1].Name != "command1" { 1778 t.Errorf("Expected a single command group with commands %q and %q", "command2", "command1") 1779 } 1780 err := checkInvalidCommandGroup(cmd, "command3") 1781 if err == nil { 1782 t.Errorf("Expected an error to occur grouping an unknown command, but none encountered.") 1783 } 1784 err = checkInvalidCommandGroup(cmd, "command1", "command3") 1785 if err == nil { 1786 t.Errorf("Expected an error to occur grouping an unknown command, but none encountered.") 1787 } 1788 } 1789 1790 func checkInvalidCommandGroup(cmd *Command, name ...string) (err error) { 1791 defer func() { 1792 r := recover() 1793 if r != nil { 1794 switch e := r.(type) { 1795 case commandError: 1796 err = e 1797 case optionError: 1798 err = e 1799 default: 1800 panic(e) 1801 } 1802 } 1803 }() 1804 cmd.GroupCommands(name...) 1805 return nil 1806 } 1807 1808 func TestGroupOptions(t *testing.T) { 1809 spec := &struct { 1810 Option1 int `option:"option1"` 1811 Option2 int `option:"option2"` 1812 }{} 1813 cmd := New("test", spec) 1814 1815 group := cmd.GroupOptions("option1") 1816 if len(group.Options) != 1 || group.Options[0].Names[0] != "option1" { 1817 t.Errorf("Expected a single option group with option %q", "option1") 1818 } 1819 group = cmd.GroupOptions("option2") 1820 if len(group.Options) != 1 || group.Options[0].Names[0] != "option2" { 1821 t.Errorf("Expected a single option group with option %q", "option2") 1822 } 1823 group = cmd.GroupOptions("option1", "option2") 1824 if len(group.Options) != 2 || group.Options[0].Names[0] != "option1" || group.Options[1].Names[0] != "option2" { 1825 t.Errorf("Expected a single option group with options %q and %q", "option1", "option2") 1826 } 1827 group = cmd.GroupOptions("option2", "option1") 1828 if len(group.Options) != 2 || group.Options[0].Names[0] != "option2" || group.Options[1].Names[0] != "option1" { 1829 t.Errorf("Expected a single option group with options %q and %q", "option2", "option1") 1830 } 1831 err := checkInvalidOptionGroup(cmd, "option3") 1832 if err == nil { 1833 t.Errorf("Expected an error to occur grouping an unknown option, but none encountered.") 1834 } 1835 err = checkInvalidOptionGroup(cmd, "option1", "option3") 1836 if err == nil { 1837 t.Errorf("Expected an error to occur grouping an unknown option, but none encountered.") 1838 } 1839 } 1840 1841 func checkInvalidOptionGroup(cmd *Command, name ...string) (err error) { 1842 defer func() { 1843 r := recover() 1844 if r != nil { 1845 switch e := r.(type) { 1846 case commandError: 1847 err = e 1848 case optionError: 1849 err = e 1850 default: 1851 panic(e) 1852 } 1853 } 1854 }() 1855 cmd.GroupOptions(name...) 1856 return nil 1857 } 1858 1859 func TestCheckUnknownTagType(t *testing.T) { 1860 defer func() { 1861 spec := struct { 1862 Bogus int `bogus:"bogus"` 1863 }{} 1864 rval := reflect.ValueOf(spec) 1865 field, present := rval.Type().FieldByName("Bogus") 1866 if !present { 1867 t.Errorf("Expected Bogus field to be present") 1868 return 1869 } 1870 1871 defer func() { recover() }() 1872 checkTags(field, "bogus") 1873 t.Errorf("Expected checkFields() to panic on unknown tag %q, but it didn't happen", "bogus") 1874 }() 1875 } 1876 1877 /* 1878 * Misc coverage tests to ensure code doesn't panic/blow-up 1879 */ 1880 1881 func TestCommandError(t *testing.T) { 1882 err := commandError{fmt.Errorf("test")} 1883 if err.Error() != "test" { 1884 t.Errorf("Expected commandError to return underlying error string. Expected: %q, Received: %q", "test", err.Error()) 1885 } 1886 }