github.com/puellanivis/breton@v0.2.16/lib/gnuflag/struct_test.go (about) 1 package gnuflag 2 3 import ( 4 "net/url" 5 "reflect" 6 "testing" 7 "time" 8 ) 9 10 type mockFlag struct { 11 Value string 12 } 13 14 func (m mockFlag) String() string { 15 return m.Value 16 } 17 18 func (m *mockFlag) Set(s string) error { 19 m.Value = s 20 return nil 21 } 22 23 func (m mockFlag) Get() interface{} { 24 return m.Value 25 } 26 27 var _ Value = &mockFlag{} 28 29 type badType chan struct{} 30 31 func TestStructVar(t *testing.T) { 32 var Flags struct { 33 ignored bool 34 True bool `flag:"" desc:"a bool value" default:"true"` 35 ZeroValueBool bool `flag:"" desc:"a zero value bool"` 36 PiApprox float64 `flag:",def=3.1415" desc:"a float64 with an approximation for pi"` 37 NegativeFortyTwo int `flag:",default=-42" desc:"an int with -42"` 38 SkipEvenThoughBadType badType `flag:"-"` 39 NegativeSixtyFour int64 `desc:"an int64 with -64" default:"-64"` 40 Foo string `desc:"a string with foo" default:"foo"` 41 Renamed string `flag:"bar" desc:"a field renamed to bar"` 42 FortyTwo uint `desc:"a uint with 42" default:"42"` 43 SixtyFour uint64 `desc:"a uint64 with 64" default:"64"` 44 ANamedFlagValue mockFlag 45 AnUnnamedIntType int 46 47 SubFlags struct { 48 Value int 49 Pointer *int 50 } 51 } 52 53 var fs FlagSet 54 55 if err := fs.structVar("", reflect.ValueOf(&Flags).Elem()); err != nil { 56 t.Fatal("unexpected error running structVar:", err) 57 } 58 59 if len(fs.formal)+len(fs.short) == 0 { 60 t.Fatal("no flags set on FlagSet") 61 } 62 63 checkedKeys := make(map[string]bool) 64 checkFlag := func(name, value, usage string, val interface{}) { 65 checkedKeys[name] = true 66 67 f, ok := fs.formal[name] 68 if !ok { 69 t.Errorf("expected flag %q to exist", name) 70 return 71 } 72 if f.DefValue != value { 73 t.Errorf("flag %q has default value %q, but epected %q", name, f.Value, value) 74 } 75 if f.Usage != usage { 76 t.Errorf("flag %q has usage %q, but expected %q", name, f.Usage, usage) 77 } 78 79 ptr := reflect.ValueOf(val).Elem().Addr().Pointer() 80 p := reflect.ValueOf(f.Value).Elem().Addr().Pointer() 81 if ptr != p { 82 t.Errorf("flag %q points to %#v, but expected %#v", name, p, ptr) 83 } 84 } 85 86 checkFlag("true", "true", "a bool value", &(Flags.True)) 87 checkFlag("zero-value-bool", "false", "a zero value bool", &(Flags.ZeroValueBool)) 88 checkFlag("pi-approx", "3.1415", "a float64 with an approximation for pi", &(Flags.PiApprox)) 89 checkFlag("negative-forty-two", "-42", "an int with -42", &(Flags.NegativeFortyTwo)) 90 checkFlag("negative-sixty-four", "-64", "an int64 with -64", &(Flags.NegativeSixtyFour)) 91 checkFlag("foo", "foo", "a string with foo", &(Flags.Foo)) 92 checkFlag("bar", "", "a field renamed to bar", &(Flags.Renamed)) 93 checkFlag("forty-two", "42", "a uint with 42", &(Flags.FortyTwo)) 94 checkFlag("sixty-four", "64", "a uint64 with 64", &(Flags.SixtyFour)) 95 checkFlag("a-named-flag-value", "", "ANamedFlagValue `gnuflag.mockFlag`", &(Flags.ANamedFlagValue)) 96 checkFlag("an-unnamed-int-type", "0", "AnUnnamedIntType `int`", &(Flags.AnUnnamedIntType)) 97 checkFlag("sub-flags-value", "0", "Value `int`", &(Flags.SubFlags.Value)) 98 checkFlag("sub-flags-pointer", "0", "Pointer `*int`", Flags.SubFlags.Pointer) 99 100 for k := range fs.formal { 101 if !checkedKeys[k] { 102 t.Errorf("unexpected key found: %q", k) 103 } 104 } 105 } 106 107 func TestSpecialValues(t *testing.T) { 108 var Flags struct { 109 AUint8Alias byte 110 MyURL *url.URL 111 } 112 113 Flags.AUint8Alias = 42 114 MyURL, err := url.Parse("scheme://hostname:12345/path") 115 if err != nil { 116 t.Fatal("unexpected error parsing url:", err) 117 } 118 Flags.MyURL = MyURL 119 120 var fs FlagSet 121 122 if err := fs.structVar("", reflect.ValueOf(&Flags).Elem()); err != nil { 123 t.Fatal("unexpected error running structVar:", err) 124 } 125 126 if len(fs.formal)+len(fs.short) == 0 { 127 t.Fatal("no flags set on FlagSet") 128 } 129 130 checkedKeys := make(map[string]bool) 131 checkFlag := func(name, value, usage string, val, expect interface{}) { 132 checkedKeys[name] = true 133 134 f, ok := fs.formal[name] 135 if !ok { 136 t.Errorf("expected flag %q to exist", name) 137 return 138 } 139 if f.DefValue != value { 140 t.Errorf("flag %q has default value %q, but epected %q", name, f.Value, value) 141 } 142 if f.Usage != usage { 143 t.Errorf("flag %q has usage %q, but expected %q", name, f.Usage, usage) 144 } 145 if !reflect.DeepEqual(val, expect) { 146 t.Errorf("flag %q is %#v, but expected %#v", name, val, expect) 147 } 148 } 149 150 checkFlag("a-uint8-alias", "42", "AUint8Alias `uint8`", Flags.AUint8Alias, byte(42)) 151 checkFlag("my-url", "scheme://hostname:12345/path", "MyURL `*url.URL`", Flags.MyURL, MyURL) 152 153 for k := range fs.formal { 154 if !checkedKeys[k] { 155 t.Errorf("unexpected key found: %q", k) 156 } 157 } 158 159 fs.Set("a-uint8-alias", "13") 160 if Flags.AUint8Alias != 13 { 161 t.Errorf("expected %q to be set to 13, but got %d", "AUint8Alias", 13) 162 } 163 164 expect := &url.URL{ 165 Scheme: "http", 166 Host: "example.com:80", 167 Path: "/a/different/path", 168 } 169 fs.Set("my-url", "http://example.com:80/a/different/path") 170 if !reflect.DeepEqual(Flags.MyURL, expect) { 171 t.Errorf("expected %q to be set to %q, but got %q", "MyURL", expect, Flags.MyURL) 172 } 173 } 174 175 func TestStructVar_BadInputs(t *testing.T) { 176 type test struct { 177 name string 178 s interface{} 179 } 180 181 tests := []test{ 182 { 183 name: "bad name: contains '='", 184 s: &struct { 185 F int `flag:"="` 186 }{}, 187 }, 188 { 189 name: "bad name: begins with -", 190 s: &struct { 191 F int `flag:"-flag"` 192 }{}, 193 }, 194 { 195 name: "unsupported flag type", 196 s: &struct { 197 F badType 198 }{}, 199 }, 200 { 201 name: "bad bool default", 202 s: &struct { 203 F bool `default:"foo"` 204 }{}, 205 }, 206 { 207 name: "bad time.Duration default", 208 s: &struct { 209 F time.Duration `default:"foo"` 210 }{}, 211 }, 212 { 213 name: "bad float64 default", 214 s: &struct { 215 F float64 `default:"foo"` 216 }{}, 217 }, 218 { 219 name: "bad int default", 220 s: &struct { 221 F int `default:"foo"` 222 }{}, 223 }, 224 { 225 name: "bad int64 default", 226 s: &struct { 227 F int64 `default:"foo"` 228 }{}, 229 }, 230 { 231 name: "bad uint default", 232 s: &struct { 233 F uint `default:"foo"` 234 }{}, 235 }, 236 { 237 name: "bad uint64 default", 238 s: &struct { 239 F uint64 `default:"foo"` 240 }{}, 241 }, 242 { 243 name: "uint8 default too large", 244 s: &struct { 245 F uint8 `default:"256"` 246 }{}, 247 }, 248 { 249 name: "uint16 default too large", 250 s: &struct { 251 F uint16 `default:"65536"` 252 }{}, 253 }, 254 { 255 name: "uint32 default too large", 256 s: &struct { 257 F uint32 `default:"4294967296"` 258 }{}, 259 }, 260 { 261 name: "int8 default too large", 262 s: &struct { 263 F int8 `default:"128"` 264 }{}, 265 }, 266 { 267 name: "int16 default too large", 268 s: &struct { 269 F int16 `default:"32768"` 270 }{}, 271 }, 272 { 273 name: "int32 default too large", 274 s: &struct { 275 F int32 `default:"2147483648"` 276 }{}, 277 }, 278 { 279 name: "float32 default too large", 280 s: &struct { 281 F int32 `default:"1e39"` 282 }{}, 283 }, 284 } 285 286 for _, tt := range tests { 287 var fs FlagSet 288 289 if err := fs.structVar("", reflect.ValueOf(tt.s).Elem()); err == nil { 290 t.Errorf("%s: expected error running structVar, but got none", tt.name) 291 } 292 } 293 294 } 295 296 func Test_flagName(t *testing.T) { 297 type test struct { 298 name string 299 flagName string 300 want string 301 } 302 303 tests := []test{ 304 { 305 name: "create DSN variable for a country", 306 flagName: "DNS", 307 want: "dns", 308 }, 309 { 310 name: "create variable lower-casing the first character", 311 flagName: "Name", 312 want: "name", 313 }, 314 { 315 name: "one letter variable with no prefix", 316 flagName: "N", 317 want: "n", 318 }, 319 { 320 name: "split flag name depending on cases", 321 flagName: "NaNo", 322 want: "na-no", 323 }, 324 { 325 name: "split flag name, but acronym together", 326 flagName: "GetMyURLFromString", 327 want: "get-my-url-from-string", 328 }, 329 { 330 name: "split flag name, but acronym last", 331 flagName: "GetMyURL", 332 want: "get-my-url", 333 }, 334 { 335 name: "split flag name, but acronym first", 336 flagName: "URLFromString", 337 want: "url-from-string", 338 }, 339 // Legend: a = expected acronym, n = word inital capital, x = rest of non-acronym words 340 { 341 name: "pathological acronym tests", 342 flagName: "NxNxxANxANxxAANxAANxx", 343 want: "nx-nxx-a-nx-a-nxx-aa-nx-aa-nxx", 344 }, 345 { 346 name: "pathological acronym tests, with initial one-letter 'acronym'", 347 flagName: "ANxNxxANxANxxAANxAANxx", 348 want: "a-nx-nxx-a-nx-a-nxx-aa-nx-aa-nxx", 349 }, 350 } 351 for _, tt := range tests { 352 t.Run(tt.name, func(t *testing.T) { 353 if got := flagName(tt.flagName); got != tt.want { 354 t.Errorf("flagName() = %v, want %v", got, tt.want) 355 } 356 }) 357 } 358 }