...

Source file src/github.com/urfave/cli/v2/sliceflag_test.go

Documentation: github.com/urfave/cli/v2

     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  		// add $0 (the command being run)
    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  	//output:
   128  	//Show defaults...
   129  	//
   130  	//Flag names: []
   131  	//Local flag names: []
   132  	//Context values:
   133  	//"flag-one"=[]
   134  	//"two"=["default value 1" "default value 2"]
   135  	//"flag-three"=["some value"]
   136  	//"flag-four"=["d1" "d2"]
   137  	//Destination values:
   138  	//cfg.FlagOne=[]
   139  	//cfg.Two=["default value 1" "default value 2"]
   140  	//---
   141  	//Setting all flags via command line...
   142  	//
   143  	//Flag names: ["1" "2" "3" "4" "flag-four" "flag-one" "flag-three" "two"]
   144  	//Local flag names: ["1" "2" "3" "4" "flag-four" "flag-one" "flag-three" "two"]
   145  	//Context values:
   146  	//"flag-one"=["v 1" "v 2"]
   147  	//"two"=["v 3" "v 4"]
   148  	//"flag-three"=["v 5" "v 6"]
   149  	//"flag-four"=["v 7" "v 8"]
   150  	//Destination values:
   151  	//cfg.FlagOne=["v 1" "v 2"]
   152  	//cfg.Two=["v 3" "v 4"]
   153  	//---
   154  	//Setting all flags via environment...
   155  	//
   156  	//Flag names: ["flag-one" "1" "two" "2" "flag-three" "3" "flag-four" "4"]
   157  	//Local flag names: ["flag-one" "1" "two" "2" "flag-three" "3" "flag-four" "4"]
   158  	//Context values:
   159  	//"flag-one"=["v 9" "v 10"]
   160  	//"two"=["v 11" "v 12"]
   161  	//"flag-three"=["v 13" "v 14"]
   162  	//"flag-four"=["v 15" "v 16"]
   163  	//Destination values:
   164  	//cfg.FlagOne=["v 9" "v 10"]
   165  	//cfg.Two=["v 11" "v 12"]
   166  	//---
   167  	//With the same environment + args from the previous example...
   168  	//
   169  	//Flag names: ["1" "2" "3" "4" "flag-four" "flag-one" "flag-three" "two"]
   170  	//Local flag names: ["1" "2" "3" "4" "flag-four" "flag-one" "flag-three" "two"]
   171  	//Context values:
   172  	//"flag-one"=["v 1" "v 2"]
   173  	//"two"=["v 3" "v 4"]
   174  	//"flag-three"=["v 5" "v 6"]
   175  	//"flag-four"=["v 7" "v 8"]
   176  	//Destination values:
   177  	//cfg.FlagOne=["v 1" "v 2"]
   178  	//cfg.Two=["v 3" "v 4"]
   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