1 package cli
2
3 import (
4 "bytes"
5 "flag"
6 "fmt"
7 "os"
8 "reflect"
9 "testing"
10 )
11
12 func ExampleMultiStringFlag() {
13 run := func(args ...string) {
14
15 args = append([]string{`-`}, args...)
16 type CustomStringSlice []string
17 type Config struct {
18 FlagOne []string
19 Two CustomStringSlice
20 }
21 cfg := Config{
22 Two: []string{
23 `default value 1`,
24 `default value 2`,
25 },
26 }
27 if err := (&App{
28 Flags: []Flag{
29 &MultiStringFlag{
30 Target: &StringSliceFlag{
31 Name: `flag-one`,
32 Category: `category1`,
33 Usage: `this is the first flag`,
34 Aliases: []string{`1`},
35 EnvVars: []string{`FLAG_ONE`},
36 },
37 Value: cfg.FlagOne,
38 Destination: &cfg.FlagOne,
39 },
40 &SliceFlag[*StringSliceFlag, CustomStringSlice, string]{
41 Target: &StringSliceFlag{
42 Name: `two`,
43 Category: `category2`,
44 Usage: `this is the second flag`,
45 Aliases: []string{`2`},
46 EnvVars: []string{`TWO`},
47 },
48 Value: cfg.Two,
49 Destination: &cfg.Two,
50 },
51 &MultiStringFlag{
52 Target: &StringSliceFlag{
53 Name: `flag-three`,
54 Category: `category1`,
55 Usage: `this is the third flag`,
56 Aliases: []string{`3`},
57 EnvVars: []string{`FLAG_THREE`},
58 },
59 Value: []string{`some value`},
60 },
61 &StringSliceFlag{
62 Name: `flag-four`,
63 Category: `category2`,
64 Usage: `this is the fourth flag`,
65 Aliases: []string{`4`},
66 EnvVars: []string{`FLAG_FOUR`},
67 Value: NewStringSlice(`d1`, `d2`),
68 },
69 },
70 Action: func(c *Context) error {
71 fmt.Printf("Flag names: %q\n", c.FlagNames())
72 fmt.Printf("Local flag names: %q\n", c.LocalFlagNames())
73 fmt.Println(`Context values:`)
74 for _, name := range [...]string{`flag-one`, `two`, `flag-three`, `flag-four`} {
75 fmt.Printf("%q=%q\n", name, c.StringSlice(name))
76 }
77 fmt.Println(`Destination values:`)
78 fmt.Printf("cfg.FlagOne=%q\n", cfg.FlagOne)
79 fmt.Printf("cfg.Two=%q\n", cfg.Two)
80 return nil
81 },
82 Writer: os.Stdout,
83 ErrWriter: os.Stdout,
84 Name: `app-name`,
85 }).Run(args); err != nil {
86 panic(err)
87 }
88 }
89
90 fmt.Printf("Show defaults...\n\n")
91 run()
92
93 fmt.Printf("---\nSetting all flags via command line...\n\n")
94 allFlagsArgs := []string{
95 `-1`, `v 1`,
96 `-1`, `v 2`,
97 `-2`, `v 3`,
98 `-2`, `v 4`,
99 `-3`, `v 5`,
100 `-3`, `v 6`,
101 `-4`, `v 7`,
102 `-4`, `v 8`,
103 }
104 run(allFlagsArgs...)
105
106 func() {
107 defer resetEnv(os.Environ())
108 os.Clearenv()
109 for _, args := range [...][2]string{
110 {`FLAG_ONE`, `v 9, v 10`},
111 {`TWO`, `v 11, v 12`},
112 {`FLAG_THREE`, `v 13, v 14`},
113 {`FLAG_FOUR`, `v 15, v 16`},
114 } {
115 if err := os.Setenv(args[0], args[1]); err != nil {
116 panic(err)
117 }
118 }
119
120 fmt.Printf("---\nSetting all flags via environment...\n\n")
121 run()
122
123 fmt.Printf("---\nWith the same environment + args from the previous example...\n\n")
124 run(allFlagsArgs...)
125 }()
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179 }
180
181 func TestSliceFlag_Apply_string(t *testing.T) {
182 normalise := func(v any) any {
183 switch v := v.(type) {
184 case *[]string:
185 if v == nil {
186 return nil
187 }
188 return *v
189 case *StringSlice:
190 if v == nil {
191 return nil
192 }
193 return v.Value()
194 }
195 return v
196 }
197 expectEqual := func(t *testing.T, actual, expected any) {
198 t.Helper()
199 actual = normalise(actual)
200 expected = normalise(expected)
201 if !reflect.DeepEqual(actual, expected) {
202 t.Errorf("actual: %#v\nexpected: %#v", actual, expected)
203 }
204 }
205 type Config struct {
206 Flag SliceFlagTarget[string]
207 Value *[]string
208 Destination **[]string
209 Context *Context
210 Check func()
211 }
212 for _, tc := range [...]struct {
213 Name string
214 Factory func(t *testing.T, f *StringSliceFlag) Config
215 }{
216 {
217 Name: `once`,
218 Factory: func(t *testing.T, f *StringSliceFlag) Config {
219 v := SliceFlag[*StringSliceFlag, []string, string]{Target: f}
220 return Config{
221 Flag: &v,
222 Value: &v.Value,
223 Destination: &v.Destination,
224 Check: func() {
225 expectEqual(t, v.Value, v.Target.Value)
226 expectEqual(t, v.Destination, v.Target.Destination)
227 },
228 }
229 },
230 },
231 {
232 Name: `twice`,
233 Factory: func(t *testing.T, f *StringSliceFlag) Config {
234 v := SliceFlag[*SliceFlag[*StringSliceFlag, []string, string], []string, string]{
235 Target: &SliceFlag[*StringSliceFlag, []string, string]{Target: f},
236 }
237 return Config{
238 Flag: &v,
239 Value: &v.Value,
240 Destination: &v.Destination,
241 Check: func() {
242 expectEqual(t, v.Value, v.Target.Value)
243 expectEqual(t, v.Destination, v.Target.Destination)
244
245 expectEqual(t, v.Value, v.Target.Target.Value)
246 expectEqual(t, v.Destination, v.Target.Target.Destination)
247 },
248 }
249 },
250 },
251 {
252 Name: `thrice`,
253 Factory: func(t *testing.T, f *StringSliceFlag) Config {
254 v := SliceFlag[*SliceFlag[*SliceFlag[*StringSliceFlag, []string, string], []string, string], []string, string]{
255 Target: &SliceFlag[*SliceFlag[*StringSliceFlag, []string, string], []string, string]{
256 Target: &SliceFlag[*StringSliceFlag, []string, string]{Target: f},
257 },
258 }
259 return Config{
260 Flag: &v,
261 Value: &v.Value,
262 Destination: &v.Destination,
263 Check: func() {
264 expectEqual(t, v.Value, v.Target.Value)
265 expectEqual(t, v.Destination, v.Target.Destination)
266
267 expectEqual(t, v.Value, v.Target.Target.Value)
268 expectEqual(t, v.Destination, v.Target.Target.Destination)
269
270 expectEqual(t, v.Value, v.Target.Target.Target.Value)
271 expectEqual(t, v.Destination, v.Target.Target.Target.Destination)
272 },
273 }
274 },
275 },
276 } {
277 t.Run(tc.Name, func(t *testing.T) {
278 t.Run(`destination`, func(t *testing.T) {
279 c := tc.Factory(t, &StringSliceFlag{
280 Name: `a`,
281 EnvVars: []string{`APP_A`},
282 })
283 defer c.Check()
284 vDefault := []string{`one`, ``, ``, `two`, ``}
285 var vTarget []string
286 *c.Value = vDefault
287 *c.Destination = &vTarget
288 if err := (&App{Action: func(c *Context) error { return nil }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=`, `--a=three`, `--a=`, `--a=`, `--a=four`, `--a=`, `--a=`}); err != nil {
289 t.Fatal(err)
290 }
291 expectEqual(t, vDefault, []string{`one`, ``, ``, `two`, ``})
292 expectEqual(t, vTarget, []string{"", "three", "", "", "four", "", ""})
293 })
294 t.Run(`context`, func(t *testing.T) {
295 c := tc.Factory(t, &StringSliceFlag{
296 Name: `a`,
297 EnvVars: []string{`APP_A`},
298 })
299 defer c.Check()
300 vDefault := []string{`one`, ``, ``, `two`, ``}
301 *c.Value = vDefault
302 var vTarget []string
303 if err := (&App{Action: func(c *Context) error {
304 vTarget = c.StringSlice(`a`)
305 return nil
306 }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=`, `--a=three`, `--a=`, `--a=`, `--a=four`, `--a=`, `--a=`}); err != nil {
307 t.Fatal(err)
308 }
309 expectEqual(t, vDefault, []string{`one`, ``, ``, `two`, ``})
310 expectEqual(t, vTarget, []string{"", "three", "", "", "four", "", ""})
311 })
312 t.Run(`context with destination`, func(t *testing.T) {
313 c := tc.Factory(t, &StringSliceFlag{
314 Name: `a`,
315 EnvVars: []string{`APP_A`},
316 })
317 defer c.Check()
318 vDefault := []string{`one`, ``, ``, `two`, ``}
319 *c.Value = vDefault
320 var vTarget []string
321 var destination []string
322 *c.Destination = &destination
323 if err := (&App{Action: func(c *Context) error {
324 vTarget = c.StringSlice(`a`)
325 return nil
326 }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=`, `--a=three`, `--a=`, `--a=`, `--a=four`, `--a=`, `--a=`}); err != nil {
327 t.Fatal(err)
328 }
329 expectEqual(t, vDefault, []string{`one`, ``, ``, `two`, ``})
330 expectEqual(t, vTarget, []string{"", "three", "", "", "four", "", ""})
331 expectEqual(t, destination, []string{"", "three", "", "", "four", "", ""})
332 })
333 t.Run(`stdlib flag usage with default`, func(t *testing.T) {
334 c := tc.Factory(t, &StringSliceFlag{Name: `a`})
335 *c.Value = []string{`one`, `two`}
336 var vTarget []string
337 *c.Destination = &vTarget
338 set := flag.NewFlagSet(`flagset`, flag.ContinueOnError)
339 var output bytes.Buffer
340 set.SetOutput(&output)
341 if err := c.Flag.Apply(set); err != nil {
342 t.Fatal(err)
343 }
344 if err := set.Parse([]string{`-h`}); err != flag.ErrHelp {
345 t.Fatal(err)
346 }
347 if s := output.String(); s != "Usage of flagset:\n -a value\n \t (default [one two])\n" {
348 t.Errorf("unexpected output: %q\n%s", s, s)
349 }
350 })
351 {
352 test := func(t *testing.T, value []string) {
353 c := tc.Factory(t, &StringSliceFlag{Name: `a`})
354 *c.Value = value
355 var vTarget []string
356 *c.Destination = &vTarget
357 set := flag.NewFlagSet(`flagset`, flag.ContinueOnError)
358 var output bytes.Buffer
359 set.SetOutput(&output)
360 if err := c.Flag.Apply(set); err != nil {
361 t.Fatal(err)
362 }
363 if err := set.Parse([]string{`-h`}); err != flag.ErrHelp {
364 t.Fatal(err)
365 }
366 if s := output.String(); s != "Usage of flagset:\n -a value\n \t\n" {
367 t.Errorf("unexpected output: %q\n%s", s, s)
368 }
369 }
370 t.Run(`stdlib flag usage without default nil`, func(t *testing.T) {
371 test(t, nil)
372 })
373 t.Run(`stdlib flag usage without default empty`, func(t *testing.T) {
374 test(t, make([]string, 0))
375 })
376 }
377 })
378 }
379 }
380
381 func TestSliceFlag_Apply_float64(t *testing.T) {
382 normalise := func(v any) any {
383 switch v := v.(type) {
384 case *[]float64:
385 if v == nil {
386 return nil
387 }
388 return *v
389 case *Float64Slice:
390 if v == nil {
391 return nil
392 }
393 return v.Value()
394 }
395 return v
396 }
397 expectEqual := func(t *testing.T, actual, expected any) {
398 t.Helper()
399 actual = normalise(actual)
400 expected = normalise(expected)
401 if !reflect.DeepEqual(actual, expected) {
402 t.Errorf("actual: %#v\nexpected: %#v", actual, expected)
403 }
404 }
405 type Config struct {
406 Flag SliceFlagTarget[float64]
407 Value *[]float64
408 Destination **[]float64
409 Context *Context
410 Check func()
411 }
412 for _, tc := range [...]struct {
413 Name string
414 Factory func(t *testing.T, f *Float64SliceFlag) Config
415 }{
416 {
417 Name: `once`,
418 Factory: func(t *testing.T, f *Float64SliceFlag) Config {
419 v := SliceFlag[*Float64SliceFlag, []float64, float64]{Target: f}
420 return Config{
421 Flag: &v,
422 Value: &v.Value,
423 Destination: &v.Destination,
424 Check: func() {
425 expectEqual(t, v.Value, v.Target.Value)
426 expectEqual(t, v.Destination, v.Target.Destination)
427 },
428 }
429 },
430 },
431 {
432 Name: `twice`,
433 Factory: func(t *testing.T, f *Float64SliceFlag) Config {
434 v := SliceFlag[*SliceFlag[*Float64SliceFlag, []float64, float64], []float64, float64]{
435 Target: &SliceFlag[*Float64SliceFlag, []float64, float64]{Target: f},
436 }
437 return Config{
438 Flag: &v,
439 Value: &v.Value,
440 Destination: &v.Destination,
441 Check: func() {
442 expectEqual(t, v.Value, v.Target.Value)
443 expectEqual(t, v.Destination, v.Target.Destination)
444
445 expectEqual(t, v.Value, v.Target.Target.Value)
446 expectEqual(t, v.Destination, v.Target.Target.Destination)
447 },
448 }
449 },
450 },
451 {
452 Name: `thrice`,
453 Factory: func(t *testing.T, f *Float64SliceFlag) Config {
454 v := SliceFlag[*SliceFlag[*SliceFlag[*Float64SliceFlag, []float64, float64], []float64, float64], []float64, float64]{
455 Target: &SliceFlag[*SliceFlag[*Float64SliceFlag, []float64, float64], []float64, float64]{
456 Target: &SliceFlag[*Float64SliceFlag, []float64, float64]{Target: f},
457 },
458 }
459 return Config{
460 Flag: &v,
461 Value: &v.Value,
462 Destination: &v.Destination,
463 Check: func() {
464 expectEqual(t, v.Value, v.Target.Value)
465 expectEqual(t, v.Destination, v.Target.Destination)
466
467 expectEqual(t, v.Value, v.Target.Target.Value)
468 expectEqual(t, v.Destination, v.Target.Target.Destination)
469
470 expectEqual(t, v.Value, v.Target.Target.Target.Value)
471 expectEqual(t, v.Destination, v.Target.Target.Target.Destination)
472 },
473 }
474 },
475 },
476 } {
477 t.Run(tc.Name, func(t *testing.T) {
478 t.Run(`destination`, func(t *testing.T) {
479 c := tc.Factory(t, &Float64SliceFlag{
480 Name: `a`,
481 EnvVars: []string{`APP_A`},
482 })
483 defer c.Check()
484 vDefault := []float64{1, 2, 3}
485 var vTarget []float64
486 *c.Value = vDefault
487 *c.Destination = &vTarget
488 if err := (&App{Action: func(c *Context) error { return nil }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
489 t.Fatal(err)
490 }
491 expectEqual(t, vDefault, []float64{1, 2, 3})
492 expectEqual(t, vTarget, []float64{4, 5})
493 })
494 t.Run(`context`, func(t *testing.T) {
495 c := tc.Factory(t, &Float64SliceFlag{
496 Name: `a`,
497 EnvVars: []string{`APP_A`},
498 })
499 defer c.Check()
500 vDefault := []float64{1, 2, 3}
501 *c.Value = vDefault
502 var vTarget []float64
503 if err := (&App{Action: func(c *Context) error {
504 vTarget = c.Float64Slice(`a`)
505 return nil
506 }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
507 t.Fatal(err)
508 }
509 expectEqual(t, vDefault, []float64{1, 2, 3})
510 expectEqual(t, vTarget, []float64{4, 5})
511 })
512 t.Run(`context with destination`, func(t *testing.T) {
513 c := tc.Factory(t, &Float64SliceFlag{
514 Name: `a`,
515 EnvVars: []string{`APP_A`},
516 })
517 defer c.Check()
518 vDefault := []float64{1, 2, 3}
519 *c.Value = vDefault
520 var vTarget []float64
521 var destination []float64
522 *c.Destination = &destination
523 if err := (&App{Action: func(c *Context) error {
524 vTarget = c.Float64Slice(`a`)
525 return nil
526 }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
527 t.Fatal(err)
528 }
529 expectEqual(t, vDefault, []float64{1, 2, 3})
530 expectEqual(t, vTarget, []float64{4, 5})
531 expectEqual(t, destination, []float64{4, 5})
532 })
533 t.Run(`stdlib flag usage with default`, func(t *testing.T) {
534 c := tc.Factory(t, &Float64SliceFlag{Name: `a`})
535 *c.Value = []float64{1, 2}
536 var vTarget []float64
537 *c.Destination = &vTarget
538 set := flag.NewFlagSet(`flagset`, flag.ContinueOnError)
539 var output bytes.Buffer
540 set.SetOutput(&output)
541 if err := c.Flag.Apply(set); err != nil {
542 t.Fatal(err)
543 }
544 if err := set.Parse([]string{`-h`}); err != flag.ErrHelp {
545 t.Fatal(err)
546 }
547 if s := output.String(); s != "Usage of flagset:\n -a value\n \t (default []float64{1, 2})\n" {
548 t.Errorf("unexpected output: %q\n%s", s, s)
549 }
550 })
551 {
552 test := func(t *testing.T, value []float64) {
553 c := tc.Factory(t, &Float64SliceFlag{Name: `a`})
554 *c.Value = value
555 var vTarget []float64
556 *c.Destination = &vTarget
557 set := flag.NewFlagSet(`flagset`, flag.ContinueOnError)
558 var output bytes.Buffer
559 set.SetOutput(&output)
560 if err := c.Flag.Apply(set); err != nil {
561 t.Fatal(err)
562 }
563 if err := set.Parse([]string{`-h`}); err != flag.ErrHelp {
564 t.Fatal(err)
565 }
566 if s := output.String(); s != "Usage of flagset:\n -a value\n \t\n" {
567 t.Errorf("unexpected output: %q\n%s", s, s)
568 }
569 }
570 t.Run(`stdlib flag usage without default nil`, func(t *testing.T) {
571 test(t, nil)
572 })
573 t.Run(`stdlib flag usage without default empty`, func(t *testing.T) {
574 test(t, make([]float64, 0))
575 })
576 }
577 })
578 }
579 }
580
581 func TestSliceFlag_Apply_int64(t *testing.T) {
582 normalise := func(v any) any {
583 switch v := v.(type) {
584 case *[]int64:
585 if v == nil {
586 return nil
587 }
588 return *v
589 case *Int64Slice:
590 if v == nil {
591 return nil
592 }
593 return v.Value()
594 }
595 return v
596 }
597 expectEqual := func(t *testing.T, actual, expected any) {
598 t.Helper()
599 actual = normalise(actual)
600 expected = normalise(expected)
601 if !reflect.DeepEqual(actual, expected) {
602 t.Errorf("actual: %#v\nexpected: %#v", actual, expected)
603 }
604 }
605 type Config struct {
606 Flag SliceFlagTarget[int64]
607 Value *[]int64
608 Destination **[]int64
609 Context *Context
610 Check func()
611 }
612 for _, tc := range [...]struct {
613 Name string
614 Factory func(t *testing.T, f *Int64SliceFlag) Config
615 }{
616 {
617 Name: `once`,
618 Factory: func(t *testing.T, f *Int64SliceFlag) Config {
619 v := SliceFlag[*Int64SliceFlag, []int64, int64]{Target: f}
620 return Config{
621 Flag: &v,
622 Value: &v.Value,
623 Destination: &v.Destination,
624 Check: func() {
625 expectEqual(t, v.Value, v.Target.Value)
626 expectEqual(t, v.Destination, v.Target.Destination)
627 },
628 }
629 },
630 },
631 {
632 Name: `twice`,
633 Factory: func(t *testing.T, f *Int64SliceFlag) Config {
634 v := SliceFlag[*SliceFlag[*Int64SliceFlag, []int64, int64], []int64, int64]{
635 Target: &SliceFlag[*Int64SliceFlag, []int64, int64]{Target: f},
636 }
637 return Config{
638 Flag: &v,
639 Value: &v.Value,
640 Destination: &v.Destination,
641 Check: func() {
642 expectEqual(t, v.Value, v.Target.Value)
643 expectEqual(t, v.Destination, v.Target.Destination)
644
645 expectEqual(t, v.Value, v.Target.Target.Value)
646 expectEqual(t, v.Destination, v.Target.Target.Destination)
647 },
648 }
649 },
650 },
651 {
652 Name: `thrice`,
653 Factory: func(t *testing.T, f *Int64SliceFlag) Config {
654 v := SliceFlag[*SliceFlag[*SliceFlag[*Int64SliceFlag, []int64, int64], []int64, int64], []int64, int64]{
655 Target: &SliceFlag[*SliceFlag[*Int64SliceFlag, []int64, int64], []int64, int64]{
656 Target: &SliceFlag[*Int64SliceFlag, []int64, int64]{Target: f},
657 },
658 }
659 return Config{
660 Flag: &v,
661 Value: &v.Value,
662 Destination: &v.Destination,
663 Check: func() {
664 expectEqual(t, v.Value, v.Target.Value)
665 expectEqual(t, v.Destination, v.Target.Destination)
666
667 expectEqual(t, v.Value, v.Target.Target.Value)
668 expectEqual(t, v.Destination, v.Target.Target.Destination)
669
670 expectEqual(t, v.Value, v.Target.Target.Target.Value)
671 expectEqual(t, v.Destination, v.Target.Target.Target.Destination)
672 },
673 }
674 },
675 },
676 } {
677 t.Run(tc.Name, func(t *testing.T) {
678 t.Run(`destination`, func(t *testing.T) {
679 c := tc.Factory(t, &Int64SliceFlag{
680 Name: `a`,
681 EnvVars: []string{`APP_A`},
682 })
683 defer c.Check()
684 vDefault := []int64{1, 2, 3}
685 var vTarget []int64
686 *c.Value = vDefault
687 *c.Destination = &vTarget
688 if err := (&App{Action: func(c *Context) error { return nil }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
689 t.Fatal(err)
690 }
691 expectEqual(t, vDefault, []int64{1, 2, 3})
692 expectEqual(t, vTarget, []int64{4, 5})
693 })
694 t.Run(`context`, func(t *testing.T) {
695 c := tc.Factory(t, &Int64SliceFlag{
696 Name: `a`,
697 EnvVars: []string{`APP_A`},
698 })
699 defer c.Check()
700 vDefault := []int64{1, 2, 3}
701 *c.Value = vDefault
702 var vTarget []int64
703 if err := (&App{Action: func(c *Context) error {
704 vTarget = c.Int64Slice(`a`)
705 return nil
706 }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
707 t.Fatal(err)
708 }
709 expectEqual(t, vDefault, []int64{1, 2, 3})
710 expectEqual(t, vTarget, []int64{4, 5})
711 })
712 t.Run(`context with destination`, func(t *testing.T) {
713 c := tc.Factory(t, &Int64SliceFlag{
714 Name: `a`,
715 EnvVars: []string{`APP_A`},
716 })
717 defer c.Check()
718 vDefault := []int64{1, 2, 3}
719 *c.Value = vDefault
720 var vTarget []int64
721 var destination []int64
722 *c.Destination = &destination
723 if err := (&App{Action: func(c *Context) error {
724 vTarget = c.Int64Slice(`a`)
725 return nil
726 }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
727 t.Fatal(err)
728 }
729 expectEqual(t, vDefault, []int64{1, 2, 3})
730 expectEqual(t, vTarget, []int64{4, 5})
731 expectEqual(t, destination, []int64{4, 5})
732 })
733 t.Run(`stdlib flag usage with default`, func(t *testing.T) {
734 c := tc.Factory(t, &Int64SliceFlag{Name: `a`})
735 *c.Value = []int64{1, 2}
736 var vTarget []int64
737 *c.Destination = &vTarget
738 set := flag.NewFlagSet(`flagset`, flag.ContinueOnError)
739 var output bytes.Buffer
740 set.SetOutput(&output)
741 if err := c.Flag.Apply(set); err != nil {
742 t.Fatal(err)
743 }
744 if err := set.Parse([]string{`-h`}); err != flag.ErrHelp {
745 t.Fatal(err)
746 }
747 if s := output.String(); s != "Usage of flagset:\n -a value\n \t (default []int64{1, 2})\n" {
748 t.Errorf("unexpected output: %q\n%s", s, s)
749 }
750 })
751 {
752 test := func(t *testing.T, value []int64) {
753 c := tc.Factory(t, &Int64SliceFlag{Name: `a`})
754 *c.Value = value
755 var vTarget []int64
756 *c.Destination = &vTarget
757 set := flag.NewFlagSet(`flagset`, flag.ContinueOnError)
758 var output bytes.Buffer
759 set.SetOutput(&output)
760 if err := c.Flag.Apply(set); err != nil {
761 t.Fatal(err)
762 }
763 if err := set.Parse([]string{`-h`}); err != flag.ErrHelp {
764 t.Fatal(err)
765 }
766 if s := output.String(); s != "Usage of flagset:\n -a value\n \t\n" {
767 t.Errorf("unexpected output: %q\n%s", s, s)
768 }
769 }
770 t.Run(`stdlib flag usage without default nil`, func(t *testing.T) {
771 test(t, nil)
772 })
773 t.Run(`stdlib flag usage without default empty`, func(t *testing.T) {
774 test(t, make([]int64, 0))
775 })
776 }
777 })
778 }
779 }
780
781 func TestSliceFlag_Apply_int(t *testing.T) {
782 normalise := func(v any) any {
783 switch v := v.(type) {
784 case *[]int:
785 if v == nil {
786 return nil
787 }
788 return *v
789 case *IntSlice:
790 if v == nil {
791 return nil
792 }
793 return v.Value()
794 }
795 return v
796 }
797 expectEqual := func(t *testing.T, actual, expected any) {
798 t.Helper()
799 actual = normalise(actual)
800 expected = normalise(expected)
801 if !reflect.DeepEqual(actual, expected) {
802 t.Errorf("actual: %#v\nexpected: %#v", actual, expected)
803 }
804 }
805 type Config struct {
806 Flag SliceFlagTarget[int]
807 Value *[]int
808 Destination **[]int
809 Context *Context
810 Check func()
811 }
812 for _, tc := range [...]struct {
813 Name string
814 Factory func(t *testing.T, f *IntSliceFlag) Config
815 }{
816 {
817 Name: `once`,
818 Factory: func(t *testing.T, f *IntSliceFlag) Config {
819 v := SliceFlag[*IntSliceFlag, []int, int]{Target: f}
820 return Config{
821 Flag: &v,
822 Value: &v.Value,
823 Destination: &v.Destination,
824 Check: func() {
825 expectEqual(t, v.Value, v.Target.Value)
826 expectEqual(t, v.Destination, v.Target.Destination)
827 },
828 }
829 },
830 },
831 {
832 Name: `twice`,
833 Factory: func(t *testing.T, f *IntSliceFlag) Config {
834 v := SliceFlag[*SliceFlag[*IntSliceFlag, []int, int], []int, int]{
835 Target: &SliceFlag[*IntSliceFlag, []int, int]{Target: f},
836 }
837 return Config{
838 Flag: &v,
839 Value: &v.Value,
840 Destination: &v.Destination,
841 Check: func() {
842 expectEqual(t, v.Value, v.Target.Value)
843 expectEqual(t, v.Destination, v.Target.Destination)
844
845 expectEqual(t, v.Value, v.Target.Target.Value)
846 expectEqual(t, v.Destination, v.Target.Target.Destination)
847 },
848 }
849 },
850 },
851 {
852 Name: `thrice`,
853 Factory: func(t *testing.T, f *IntSliceFlag) Config {
854 v := SliceFlag[*SliceFlag[*SliceFlag[*IntSliceFlag, []int, int], []int, int], []int, int]{
855 Target: &SliceFlag[*SliceFlag[*IntSliceFlag, []int, int], []int, int]{
856 Target: &SliceFlag[*IntSliceFlag, []int, int]{Target: f},
857 },
858 }
859 return Config{
860 Flag: &v,
861 Value: &v.Value,
862 Destination: &v.Destination,
863 Check: func() {
864 expectEqual(t, v.Value, v.Target.Value)
865 expectEqual(t, v.Destination, v.Target.Destination)
866
867 expectEqual(t, v.Value, v.Target.Target.Value)
868 expectEqual(t, v.Destination, v.Target.Target.Destination)
869
870 expectEqual(t, v.Value, v.Target.Target.Target.Value)
871 expectEqual(t, v.Destination, v.Target.Target.Target.Destination)
872 },
873 }
874 },
875 },
876 } {
877 t.Run(tc.Name, func(t *testing.T) {
878 t.Run(`destination`, func(t *testing.T) {
879 c := tc.Factory(t, &IntSliceFlag{
880 Name: `a`,
881 EnvVars: []string{`APP_A`},
882 })
883 defer c.Check()
884 vDefault := []int{1, 2, 3}
885 var vTarget []int
886 *c.Value = vDefault
887 *c.Destination = &vTarget
888 if err := (&App{Action: func(c *Context) error { return nil }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
889 t.Fatal(err)
890 }
891 expectEqual(t, vDefault, []int{1, 2, 3})
892 expectEqual(t, vTarget, []int{4, 5})
893 })
894 t.Run(`context`, func(t *testing.T) {
895 c := tc.Factory(t, &IntSliceFlag{
896 Name: `a`,
897 EnvVars: []string{`APP_A`},
898 })
899 defer c.Check()
900 vDefault := []int{1, 2, 3}
901 *c.Value = vDefault
902 var vTarget []int
903 if err := (&App{Action: func(c *Context) error {
904 vTarget = c.IntSlice(`a`)
905 return nil
906 }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
907 t.Fatal(err)
908 }
909 expectEqual(t, vDefault, []int{1, 2, 3})
910 expectEqual(t, vTarget, []int{4, 5})
911 })
912 t.Run(`context with destination`, func(t *testing.T) {
913 c := tc.Factory(t, &IntSliceFlag{
914 Name: `a`,
915 EnvVars: []string{`APP_A`},
916 })
917 defer c.Check()
918 vDefault := []int{1, 2, 3}
919 *c.Value = vDefault
920 var vTarget []int
921 var destination []int
922 *c.Destination = &destination
923 if err := (&App{Action: func(c *Context) error {
924 vTarget = c.IntSlice(`a`)
925 return nil
926 }, Flags: []Flag{c.Flag}}).Run([]string{`-`, `--a=4`, `--a=5`}); err != nil {
927 t.Fatal(err)
928 }
929 expectEqual(t, vDefault, []int{1, 2, 3})
930 expectEqual(t, vTarget, []int{4, 5})
931 expectEqual(t, destination, []int{4, 5})
932 })
933 t.Run(`stdlib flag usage with default`, func(t *testing.T) {
934 c := tc.Factory(t, &IntSliceFlag{Name: `a`})
935 *c.Value = []int{1, 2}
936 var vTarget []int
937 *c.Destination = &vTarget
938 set := flag.NewFlagSet(`flagset`, flag.ContinueOnError)
939 var output bytes.Buffer
940 set.SetOutput(&output)
941 if err := c.Flag.Apply(set); err != nil {
942 t.Fatal(err)
943 }
944 if err := set.Parse([]string{`-h`}); err != flag.ErrHelp {
945 t.Fatal(err)
946 }
947 if s := output.String(); s != "Usage of flagset:\n -a value\n \t (default []int{1, 2})\n" {
948 t.Errorf("unexpected output: %q\n%s", s, s)
949 }
950 })
951 {
952 test := func(t *testing.T, value []int) {
953 c := tc.Factory(t, &IntSliceFlag{Name: `a`})
954 *c.Value = value
955 var vTarget []int
956 *c.Destination = &vTarget
957 set := flag.NewFlagSet(`flagset`, flag.ContinueOnError)
958 var output bytes.Buffer
959 set.SetOutput(&output)
960 if err := c.Flag.Apply(set); err != nil {
961 t.Fatal(err)
962 }
963 if err := set.Parse([]string{`-h`}); err != flag.ErrHelp {
964 t.Fatal(err)
965 }
966 if s := output.String(); s != "Usage of flagset:\n -a value\n \t\n" {
967 t.Errorf("unexpected output: %q\n%s", s, s)
968 }
969 }
970 t.Run(`stdlib flag usage without default nil`, func(t *testing.T) {
971 test(t, nil)
972 })
973 t.Run(`stdlib flag usage without default empty`, func(t *testing.T) {
974 test(t, make([]int, 0))
975 })
976 }
977 })
978 }
979 }
980
981 type intSliceWrapperDefaultingNil struct {
982 *IntSlice
983 }
984
985 func (x intSliceWrapperDefaultingNil) String() string {
986 if x.IntSlice != nil {
987 return x.IntSlice.String()
988 }
989 return NewIntSlice().String()
990 }
991
992 func TestFlagValueHook_String_struct(t *testing.T) {
993 wrap := func(values ...int) *flagValueHook {
994 return &flagValueHook{value: intSliceWrapperDefaultingNil{NewIntSlice(values...)}}
995 }
996 if s := wrap().String(); s != `` {
997 t.Error(s)
998 }
999 if s := wrap(1).String(); s != `[]int{1}` {
1000 t.Error(s)
1001 }
1002 }
1003
View as plain text