github.com/alecthomas/kong@v0.9.1-0.20240410131203-2ab5733f1179/kong_test.go (about)

     1  package kong_test
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/alecthomas/assert/v2"
    11  	"github.com/alecthomas/repr"
    12  
    13  	"github.com/alecthomas/kong"
    14  )
    15  
    16  func mustNew(t *testing.T, cli interface{}, options ...kong.Option) *kong.Kong {
    17  	t.Helper()
    18  	options = append([]kong.Option{
    19  		kong.Name("test"),
    20  		kong.Exit(func(int) {
    21  			t.Helper()
    22  			t.Fatalf("unexpected exit()")
    23  		}),
    24  	}, options...)
    25  	parser, err := kong.New(cli, options...)
    26  	assert.NoError(t, err)
    27  	return parser
    28  }
    29  
    30  func TestPositionalArguments(t *testing.T) {
    31  	var cli struct {
    32  		User struct {
    33  			Create struct {
    34  				ID    int    `kong:"arg"`
    35  				First string `kong:"arg"`
    36  				Last  string `kong:"arg"`
    37  			} `kong:"cmd"`
    38  		} `kong:"cmd"`
    39  	}
    40  	p := mustNew(t, &cli)
    41  	ctx, err := p.Parse([]string{"user", "create", "10", "Alec", "Thomas"})
    42  	assert.NoError(t, err)
    43  	assert.Equal(t, "user create <id> <first> <last>", ctx.Command())
    44  	t.Run("Missing", func(t *testing.T) {
    45  		_, err := p.Parse([]string{"user", "create", "10"})
    46  		assert.Error(t, err)
    47  	})
    48  }
    49  
    50  func TestBranchingArgument(t *testing.T) {
    51  	/*
    52  		app user create <id> <first> <last>
    53  		app	user <id> delete
    54  		app	user <id> rename <to>
    55  
    56  	*/
    57  	var cli struct {
    58  		User struct {
    59  			Create struct {
    60  				ID    string `kong:"arg"`
    61  				First string `kong:"arg"`
    62  				Last  string `kong:"arg"`
    63  			} `kong:"cmd"`
    64  
    65  			// Branching argument.
    66  			ID struct {
    67  				ID     int `kong:"arg"`
    68  				Flag   int
    69  				Delete struct{} `kong:"cmd"`
    70  				Rename struct {
    71  					To string
    72  				} `kong:"cmd"`
    73  			} `kong:"arg"`
    74  		} `kong:"cmd,help='User management.'"`
    75  	}
    76  	p := mustNew(t, &cli)
    77  	ctx, err := p.Parse([]string{"user", "10", "delete"})
    78  	assert.NoError(t, err)
    79  	assert.Equal(t, 10, cli.User.ID.ID)
    80  	assert.Equal(t, "user <id> delete", ctx.Command())
    81  	t.Run("Missing", func(t *testing.T) {
    82  		_, err = p.Parse([]string{"user"})
    83  		assert.Error(t, err)
    84  	})
    85  }
    86  
    87  func TestResetWithDefaults(t *testing.T) {
    88  	var cli struct {
    89  		Flag            string
    90  		FlagWithDefault string `kong:"default='default'"`
    91  	}
    92  	cli.Flag = "BLAH"
    93  	cli.FlagWithDefault = "BLAH"
    94  	parser := mustNew(t, &cli)
    95  	_, err := parser.Parse([]string{})
    96  	assert.NoError(t, err)
    97  	assert.Equal(t, "", cli.Flag)
    98  	assert.Equal(t, "default", cli.FlagWithDefault)
    99  }
   100  
   101  func TestFlagSlice(t *testing.T) {
   102  	var cli struct {
   103  		Slice []int
   104  	}
   105  	parser := mustNew(t, &cli)
   106  	_, err := parser.Parse([]string{"--slice=1,2,3"})
   107  	assert.NoError(t, err)
   108  	assert.Equal(t, []int{1, 2, 3}, cli.Slice)
   109  }
   110  
   111  func TestFlagSliceWithSeparator(t *testing.T) {
   112  	var cli struct {
   113  		Slice []string
   114  	}
   115  	parser := mustNew(t, &cli)
   116  	_, err := parser.Parse([]string{`--slice=a\,b,c`})
   117  	assert.NoError(t, err)
   118  	assert.Equal(t, []string{"a,b", "c"}, cli.Slice)
   119  }
   120  
   121  func TestArgSlice(t *testing.T) {
   122  	var cli struct {
   123  		Slice []int `arg`
   124  		Flag  bool
   125  	}
   126  	parser := mustNew(t, &cli)
   127  	_, err := parser.Parse([]string{"1", "2", "3", "--flag"})
   128  	assert.NoError(t, err)
   129  	assert.Equal(t, []int{1, 2, 3}, cli.Slice)
   130  	assert.Equal(t, true, cli.Flag)
   131  }
   132  
   133  func TestArgSliceWithSeparator(t *testing.T) {
   134  	var cli struct {
   135  		Slice []string `arg`
   136  		Flag  bool
   137  	}
   138  	parser := mustNew(t, &cli)
   139  	_, err := parser.Parse([]string{"a,b", "c", "--flag"})
   140  	assert.NoError(t, err)
   141  	assert.Equal(t, []string{"a,b", "c"}, cli.Slice)
   142  	assert.Equal(t, true, cli.Flag)
   143  }
   144  
   145  func TestUnsupportedFieldErrors(t *testing.T) {
   146  	var cli struct {
   147  		Keys struct{}
   148  	}
   149  	_, err := kong.New(&cli)
   150  	assert.Error(t, err)
   151  }
   152  
   153  func TestMatchingArgField(t *testing.T) {
   154  	var cli struct {
   155  		ID struct {
   156  			NotID int `kong:"arg"`
   157  		} `kong:"arg"`
   158  	}
   159  
   160  	_, err := kong.New(&cli)
   161  	assert.Error(t, err)
   162  }
   163  
   164  func TestCantMixPositionalAndBranches(t *testing.T) {
   165  	var cli struct {
   166  		Arg     string `kong:"arg"`
   167  		Command struct {
   168  		} `kong:"cmd"`
   169  	}
   170  	_, err := kong.New(&cli)
   171  	assert.Error(t, err)
   172  }
   173  
   174  func TestPropagatedFlags(t *testing.T) {
   175  	var cli struct {
   176  		Flag1    string
   177  		Command1 struct {
   178  			Flag2    bool
   179  			Command2 struct{} `kong:"cmd"`
   180  		} `kong:"cmd"`
   181  	}
   182  
   183  	parser := mustNew(t, &cli)
   184  	_, err := parser.Parse([]string{"command-1", "command-2", "--flag-2", "--flag-1=moo"})
   185  	assert.NoError(t, err)
   186  	assert.Equal(t, "moo", cli.Flag1)
   187  	assert.Equal(t, true, cli.Command1.Flag2)
   188  }
   189  
   190  func TestRequiredFlag(t *testing.T) {
   191  	var cli struct {
   192  		Flag string `kong:"required"`
   193  	}
   194  
   195  	parser := mustNew(t, &cli)
   196  	_, err := parser.Parse([]string{})
   197  	assert.Error(t, err)
   198  }
   199  
   200  func TestOptionalArg(t *testing.T) {
   201  	var cli struct {
   202  		Arg string `kong:"arg,optional"`
   203  	}
   204  
   205  	parser := mustNew(t, &cli)
   206  	_, err := parser.Parse([]string{})
   207  	assert.NoError(t, err)
   208  }
   209  
   210  func TestOptionalArgWithDefault(t *testing.T) {
   211  	var cli struct {
   212  		Arg string `kong:"arg,optional,default='moo'"`
   213  	}
   214  
   215  	parser := mustNew(t, &cli)
   216  	_, err := parser.Parse([]string{})
   217  	assert.NoError(t, err)
   218  	assert.Equal(t, "moo", cli.Arg)
   219  }
   220  
   221  func TestArgWithDefaultIsOptional(t *testing.T) {
   222  	var cli struct {
   223  		Arg string `kong:"arg,default='moo'"`
   224  	}
   225  
   226  	parser := mustNew(t, &cli)
   227  	_, err := parser.Parse([]string{})
   228  	assert.NoError(t, err)
   229  	assert.Equal(t, "moo", cli.Arg)
   230  }
   231  
   232  func TestRequiredArg(t *testing.T) {
   233  	var cli struct {
   234  		Arg string `kong:"arg"`
   235  	}
   236  
   237  	parser := mustNew(t, &cli)
   238  	_, err := parser.Parse([]string{})
   239  	assert.Error(t, err)
   240  }
   241  
   242  func TestInvalidRequiredAfterOptional(t *testing.T) {
   243  	var cli struct {
   244  		ID   int    `kong:"arg,optional"`
   245  		Name string `kong:"arg"`
   246  	}
   247  
   248  	_, err := kong.New(&cli)
   249  	assert.Error(t, err)
   250  }
   251  
   252  func TestOptionalStructArg(t *testing.T) {
   253  	var cli struct {
   254  		Name struct {
   255  			Name    string `kong:"arg,optional"`
   256  			Enabled bool
   257  		} `kong:"arg,optional"`
   258  	}
   259  
   260  	parser := mustNew(t, &cli)
   261  
   262  	t.Run("WithFlag", func(t *testing.T) {
   263  		_, err := parser.Parse([]string{"gak", "--enabled"})
   264  		assert.NoError(t, err)
   265  		assert.Equal(t, "gak", cli.Name.Name)
   266  		assert.Equal(t, true, cli.Name.Enabled)
   267  	})
   268  
   269  	t.Run("WithoutFlag", func(t *testing.T) {
   270  		_, err := parser.Parse([]string{"gak"})
   271  		assert.NoError(t, err)
   272  		assert.Equal(t, "gak", cli.Name.Name)
   273  	})
   274  
   275  	t.Run("WithNothing", func(t *testing.T) {
   276  		_, err := parser.Parse([]string{})
   277  		assert.NoError(t, err)
   278  	})
   279  }
   280  
   281  func TestMixedRequiredArgs(t *testing.T) {
   282  	var cli struct {
   283  		Name string `kong:"arg"`
   284  		ID   int    `kong:"arg,optional"`
   285  	}
   286  
   287  	parser := mustNew(t, &cli)
   288  
   289  	t.Run("SingleRequired", func(t *testing.T) {
   290  		_, err := parser.Parse([]string{"gak", "5"})
   291  		assert.NoError(t, err)
   292  		assert.Equal(t, "gak", cli.Name)
   293  		assert.Equal(t, 5, cli.ID)
   294  	})
   295  
   296  	t.Run("ExtraOptional", func(t *testing.T) {
   297  		_, err := parser.Parse([]string{"gak"})
   298  		assert.NoError(t, err)
   299  		assert.Equal(t, "gak", cli.Name)
   300  	})
   301  }
   302  
   303  func TestInvalidDefaultErrors(t *testing.T) {
   304  	var cli struct {
   305  		Flag int `kong:"default='foo'"`
   306  	}
   307  	p := mustNew(t, &cli)
   308  	_, err := p.Parse(nil)
   309  	assert.Error(t, err)
   310  }
   311  
   312  func TestCommandMissingTagIsInvalid(t *testing.T) {
   313  	var cli struct {
   314  		One struct{}
   315  	}
   316  	_, err := kong.New(&cli)
   317  	assert.Error(t, err)
   318  }
   319  
   320  func TestDuplicateFlag(t *testing.T) {
   321  	var cli struct {
   322  		Flag bool
   323  		Cmd  struct {
   324  			Flag bool
   325  		} `kong:"cmd"`
   326  	}
   327  	_, err := kong.New(&cli)
   328  	assert.Error(t, err)
   329  }
   330  
   331  func TestDuplicateFlagOnPeerCommandIsOkay(t *testing.T) {
   332  	var cli struct {
   333  		Cmd1 struct {
   334  			Flag bool
   335  		} `kong:"cmd"`
   336  		Cmd2 struct {
   337  			Flag bool
   338  		} `kong:"cmd"`
   339  	}
   340  	_, err := kong.New(&cli)
   341  	assert.NoError(t, err)
   342  }
   343  
   344  func TestTraceErrorPartiallySucceeds(t *testing.T) {
   345  	var cli struct {
   346  		One struct {
   347  			Two struct {
   348  			} `kong:"cmd"`
   349  		} `kong:"cmd"`
   350  	}
   351  	p := mustNew(t, &cli)
   352  	ctx, err := kong.Trace(p, []string{"one", "bad"})
   353  	assert.NoError(t, err)
   354  	assert.Error(t, ctx.Error)
   355  	assert.Equal(t, "one", ctx.Command())
   356  }
   357  
   358  type commandWithNegatableFlag struct {
   359  	Flag bool `kong:"default='true',negatable"`
   360  	ran  bool
   361  }
   362  
   363  func (c *commandWithNegatableFlag) Run() error {
   364  	c.ran = true
   365  	return nil
   366  }
   367  
   368  func TestNegatableFlag(t *testing.T) {
   369  	tests := []struct {
   370  		name     string
   371  		args     []string
   372  		expected bool
   373  	}{
   374  		{
   375  			name:     "no flag",
   376  			args:     []string{"cmd"},
   377  			expected: true,
   378  		},
   379  		{
   380  			name:     "boolean flag",
   381  			args:     []string{"cmd", "--flag"},
   382  			expected: true,
   383  		},
   384  		{
   385  			name:     "inverted boolean flag",
   386  			args:     []string{"cmd", "--flag=false"},
   387  			expected: false,
   388  		},
   389  		{
   390  			name:     "negated boolean flag",
   391  			args:     []string{"cmd", "--no-flag"},
   392  			expected: false,
   393  		},
   394  		{
   395  			name:     "inverted negated boolean flag",
   396  			args:     []string{"cmd", "--no-flag=false"},
   397  			expected: true,
   398  		},
   399  	}
   400  	for _, tt := range tests {
   401  		tt := tt
   402  		t.Run(tt.name, func(t *testing.T) {
   403  			var cli struct {
   404  				Cmd commandWithNegatableFlag `kong:"cmd"`
   405  			}
   406  
   407  			p := mustNew(t, &cli)
   408  			kctx, err := p.Parse(tt.args)
   409  			assert.NoError(t, err)
   410  			assert.Equal(t, tt.expected, cli.Cmd.Flag)
   411  
   412  			err = kctx.Run()
   413  			assert.NoError(t, err)
   414  			assert.Equal(t, tt.expected, cli.Cmd.Flag)
   415  			assert.True(t, cli.Cmd.ran)
   416  		})
   417  	}
   418  }
   419  
   420  func TestExistingNoFlag(t *testing.T) {
   421  	var cli struct {
   422  		Cmd struct {
   423  			Flag   bool `kong:"default='true'"`
   424  			NoFlag string
   425  		} `kong:"cmd"`
   426  	}
   427  
   428  	p := mustNew(t, &cli)
   429  	_, err := p.Parse([]string{"cmd", "--no-flag=none"})
   430  	assert.NoError(t, err)
   431  	assert.Equal(t, true, cli.Cmd.Flag)
   432  	assert.Equal(t, "none", cli.Cmd.NoFlag)
   433  }
   434  
   435  func TestInvalidNegatedNonBool(t *testing.T) {
   436  	var cli struct {
   437  		Cmd struct {
   438  			Flag string `kong:"negatable"`
   439  		} `kong:"cmd"`
   440  	}
   441  
   442  	_, err := kong.New(&cli)
   443  	assert.Error(t, err)
   444  }
   445  
   446  type hookContext struct {
   447  	cmd    bool
   448  	values []string
   449  }
   450  
   451  type hookValue string
   452  
   453  func (h *hookValue) BeforeApply(ctx *hookContext) error {
   454  	ctx.values = append(ctx.values, "before:"+string(*h))
   455  	return nil
   456  }
   457  
   458  func (h *hookValue) AfterApply(ctx *hookContext) error {
   459  	ctx.values = append(ctx.values, "after:"+string(*h))
   460  	return nil
   461  }
   462  
   463  type hookCmd struct {
   464  	Two   hookValue `kong:"arg,optional"`
   465  	Three hookValue
   466  }
   467  
   468  func (h *hookCmd) BeforeApply(ctx *hookContext) error {
   469  	ctx.cmd = true
   470  	return nil
   471  }
   472  
   473  func (h *hookCmd) AfterApply(ctx *hookContext) error {
   474  	ctx.cmd = true
   475  	return nil
   476  }
   477  
   478  func TestHooks(t *testing.T) {
   479  	var tests = []struct {
   480  		name   string
   481  		input  string
   482  		values hookContext
   483  	}{
   484  		{"Command", "one", hookContext{true, nil}},
   485  		{"Arg", "one two", hookContext{true, []string{"before:", "after:two"}}},
   486  		{"Flag", "one --three=THREE", hookContext{true, []string{"before:", "after:THREE"}}},
   487  		{"ArgAndFlag", "one two --three=THREE", hookContext{true, []string{"before:", "before:", "after:two", "after:THREE"}}},
   488  	}
   489  
   490  	var cli struct {
   491  		One hookCmd `cmd:""`
   492  	}
   493  
   494  	ctx := &hookContext{}
   495  	p := mustNew(t, &cli, kong.Bind(ctx))
   496  
   497  	for _, test := range tests {
   498  		test := test
   499  		*ctx = hookContext{}
   500  		cli.One = hookCmd{}
   501  		t.Run(test.name, func(t *testing.T) {
   502  			_, err := p.Parse(strings.Split(test.input, " "))
   503  			assert.NoError(t, err)
   504  			assert.Equal(t, &test.values, ctx)
   505  		})
   506  	}
   507  }
   508  
   509  func TestShort(t *testing.T) {
   510  	var cli struct {
   511  		Bool   bool   `short:"b"`
   512  		String string `short:"s"`
   513  	}
   514  	app := mustNew(t, &cli)
   515  	_, err := app.Parse([]string{"-b", "-shello"})
   516  	assert.NoError(t, err)
   517  	assert.True(t, cli.Bool)
   518  	assert.Equal(t, "hello", cli.String)
   519  }
   520  
   521  func TestAlias(t *testing.T) {
   522  	var cli struct {
   523  		String string `aliases:"str"`
   524  	}
   525  	app := mustNew(t, &cli)
   526  	_, err := app.Parse([]string{"--str", "hello"})
   527  	assert.NoError(t, err)
   528  	assert.Equal(t, "hello", cli.String)
   529  }
   530  
   531  func TestDuplicateFlagChoosesLast(t *testing.T) {
   532  	var cli struct {
   533  		Flag int
   534  	}
   535  
   536  	_, err := mustNew(t, &cli).Parse([]string{"--flag=1", "--flag=2"})
   537  	assert.NoError(t, err)
   538  	assert.Equal(t, 2, cli.Flag)
   539  }
   540  
   541  func TestDuplicateSliceAccumulates(t *testing.T) {
   542  	var cli struct {
   543  		Flag []int
   544  	}
   545  
   546  	args := []string{"--flag=1,2", "--flag=3,4"}
   547  	_, err := mustNew(t, &cli).Parse(args)
   548  	assert.NoError(t, err)
   549  	assert.Equal(t, []int{1, 2, 3, 4}, cli.Flag)
   550  }
   551  
   552  func TestMapFlag(t *testing.T) {
   553  	var cli struct {
   554  		Set map[string]int
   555  	}
   556  	_, err := mustNew(t, &cli).Parse([]string{"--set", "a=10", "--set", "b=20"})
   557  	assert.NoError(t, err)
   558  	assert.Equal(t, map[string]int{"a": 10, "b": 20}, cli.Set)
   559  }
   560  
   561  func TestMapFlagWithSliceValue(t *testing.T) {
   562  	var cli struct {
   563  		Set map[string][]int
   564  	}
   565  	_, err := mustNew(t, &cli).Parse([]string{"--set", "a=1,2", "--set", "b=3"})
   566  	assert.NoError(t, err)
   567  	assert.Equal(t, map[string][]int{"a": {1, 2}, "b": {3}}, cli.Set)
   568  }
   569  
   570  type embeddedFlags struct {
   571  	Embedded string
   572  }
   573  
   574  func TestEmbeddedStruct(t *testing.T) {
   575  	var cli struct {
   576  		embeddedFlags
   577  		NotEmbedded string
   578  	}
   579  
   580  	_, err := mustNew(t, &cli).Parse([]string{"--embedded=moo", "--not-embedded=foo"})
   581  	assert.NoError(t, err)
   582  	assert.Equal(t, "moo", cli.Embedded)
   583  	assert.Equal(t, "foo", cli.NotEmbedded)
   584  }
   585  
   586  func TestSliceWithDisabledSeparator(t *testing.T) {
   587  	var cli struct {
   588  		Flag []string `sep:"none"`
   589  	}
   590  	_, err := mustNew(t, &cli).Parse([]string{"--flag=a,b", "--flag=b,c"})
   591  	assert.NoError(t, err)
   592  	assert.Equal(t, []string{"a,b", "b,c"}, cli.Flag)
   593  }
   594  
   595  func TestMultilineMessage(t *testing.T) {
   596  	w := &bytes.Buffer{}
   597  	var cli struct{}
   598  	p := mustNew(t, &cli, kong.Writers(w, w))
   599  	p.Printf("hello\nworld")
   600  	assert.Equal(t, "test: hello\n      world\n", w.String())
   601  }
   602  
   603  type cmdWithRun struct {
   604  	Arg string `arg:""`
   605  }
   606  
   607  func (c *cmdWithRun) Run(key string) error {
   608  	c.Arg += key
   609  	if key == "ERROR" {
   610  		return fmt.Errorf("ERROR")
   611  	}
   612  	return nil
   613  }
   614  
   615  type parentCmdWithRun struct {
   616  	Flag       string
   617  	SubCommand struct {
   618  		Arg string `arg:""`
   619  	} `cmd:""`
   620  }
   621  
   622  func (p *parentCmdWithRun) Run(key string) error {
   623  	p.SubCommand.Arg += key
   624  	return nil
   625  }
   626  
   627  type grammarWithRun struct {
   628  	One   cmdWithRun       `cmd:""`
   629  	Two   cmdWithRun       `cmd:""`
   630  	Three parentCmdWithRun `cmd:""`
   631  }
   632  
   633  func TestRun(t *testing.T) {
   634  	cli := &grammarWithRun{}
   635  	p := mustNew(t, cli)
   636  
   637  	ctx, err := p.Parse([]string{"one", "two"})
   638  	assert.NoError(t, err)
   639  	err = ctx.Run("hello")
   640  	assert.NoError(t, err)
   641  	assert.Equal(t, "twohello", cli.One.Arg)
   642  
   643  	ctx, err = p.Parse([]string{"two", "three"})
   644  	assert.NoError(t, err)
   645  	err = ctx.Run("ERROR")
   646  	assert.Error(t, err)
   647  
   648  	ctx, err = p.Parse([]string{"three", "sub-command", "arg"})
   649  	assert.NoError(t, err)
   650  	err = ctx.Run("ping")
   651  	assert.NoError(t, err)
   652  	assert.Equal(t, "argping", cli.Three.SubCommand.Arg)
   653  }
   654  
   655  type failCmd struct{}
   656  
   657  func (f failCmd) Run() error {
   658  	return errors.New("this command failed")
   659  }
   660  
   661  func TestPassesThroughOriginalCommandError(t *testing.T) {
   662  	var cli struct {
   663  		Fail failCmd `kong:"cmd"`
   664  	}
   665  	p := mustNew(t, &cli)
   666  	ctx, _ := p.Parse([]string{"fail"})
   667  	err := ctx.Run()
   668  	assert.Error(t, err)
   669  	assert.Equal(t, err.Error(), "this command failed")
   670  }
   671  
   672  func TestInterpolationIntoModel(t *testing.T) {
   673  	var cli struct {
   674  		Flag    string `default:"${default_value}" help:"Help, I need ${somebody}" enum:"${enum}"`
   675  		EnumRef string `enum:"a,b" required:"" help:"One of ${enum}"`
   676  		EnvRef  string `env:"${env}" help:"God ${env}"`
   677  	}
   678  	_, err := kong.New(&cli)
   679  	assert.Error(t, err)
   680  	p, err := kong.New(&cli, kong.Vars{
   681  		"default_value": "Some default value.",
   682  		"somebody":      "chickens!",
   683  		"enum":          "a,b,c,d",
   684  		"env":           "SAVE_THE_QUEEN",
   685  	})
   686  	assert.NoError(t, err)
   687  	assert.Equal(t, 4, len(p.Model.Flags))
   688  	flag := p.Model.Flags[1]
   689  	flag2 := p.Model.Flags[2]
   690  	flag3 := p.Model.Flags[3]
   691  	assert.Equal(t, "Some default value.", flag.Default)
   692  	assert.Equal(t, "Help, I need chickens!", flag.Help)
   693  	assert.Equal(t, map[string]bool{"a": true, "b": true, "c": true, "d": true}, flag.EnumMap())
   694  	assert.Equal(t, []string{"a", "b", "c", "d"}, flag.EnumSlice())
   695  	assert.Equal(t, "One of a,b", flag2.Help)
   696  	assert.Equal(t, []string{"SAVE_THE_QUEEN"}, flag3.Envs)
   697  	assert.Equal(t, "God SAVE_THE_QUEEN", flag3.Help)
   698  }
   699  
   700  func TestIssue244(t *testing.T) {
   701  	type Config struct {
   702  		Project string `short:"p" env:"CI_PROJECT_ID" help:"Environment variable: ${env}"`
   703  	}
   704  	w := &strings.Builder{}
   705  	k := mustNew(t, &Config{}, kong.Exit(func(int) {}), kong.Writers(w, w))
   706  	_, err := k.Parse([]string{"--help"})
   707  	assert.NoError(t, err)
   708  	assert.Contains(t, w.String(), `Environment variable: CI_PROJECT_ID`)
   709  }
   710  
   711  func TestErrorMissingArgs(t *testing.T) {
   712  	var cli struct {
   713  		One string `arg:""`
   714  		Two string `arg:""`
   715  	}
   716  
   717  	p := mustNew(t, &cli)
   718  	_, err := p.Parse(nil)
   719  	assert.Error(t, err)
   720  	assert.Equal(t, "expected \"<one> <two>\"", err.Error())
   721  }
   722  
   723  func TestBoolOverride(t *testing.T) {
   724  	var cli struct {
   725  		Flag bool `default:"true"`
   726  	}
   727  	p := mustNew(t, &cli)
   728  	_, err := p.Parse([]string{"--flag=false"})
   729  	assert.NoError(t, err)
   730  	_, err = p.Parse([]string{"--flag", "false"})
   731  	assert.Error(t, err)
   732  }
   733  
   734  func TestAnonymousPrefix(t *testing.T) {
   735  	type Anonymous struct {
   736  		Flag string
   737  	}
   738  	var cli struct {
   739  		Anonymous `prefix:"anon-"`
   740  	}
   741  	p := mustNew(t, &cli)
   742  	_, err := p.Parse([]string{"--anon-flag=moo"})
   743  	assert.NoError(t, err)
   744  	assert.Equal(t, "moo", cli.Flag)
   745  }
   746  
   747  type TestInterface interface {
   748  	SomeMethod()
   749  }
   750  
   751  type TestImpl struct {
   752  	Flag string
   753  }
   754  
   755  func (t *TestImpl) SomeMethod() {}
   756  
   757  func TestEmbedInterface(t *testing.T) {
   758  	type CLI struct {
   759  		SomeFlag string
   760  		TestInterface
   761  	}
   762  	cli := &CLI{TestInterface: &TestImpl{}}
   763  	p := mustNew(t, cli)
   764  	_, err := p.Parse([]string{"--some-flag=foo", "--flag=yes"})
   765  	assert.NoError(t, err)
   766  	assert.Equal(t, "foo", cli.SomeFlag)
   767  	assert.Equal(t, "yes", cli.TestInterface.(*TestImpl).Flag) //nolint
   768  }
   769  
   770  func TestExcludedField(t *testing.T) {
   771  	var cli struct {
   772  		Flag     string
   773  		Excluded string `kong:"-"`
   774  	}
   775  
   776  	p := mustNew(t, &cli)
   777  	_, err := p.Parse([]string{"--flag=foo"})
   778  	assert.NoError(t, err)
   779  	_, err = p.Parse([]string{"--excluded=foo"})
   780  	assert.Error(t, err)
   781  }
   782  
   783  func TestUnnamedFieldEmbeds(t *testing.T) {
   784  	type Embed struct {
   785  		Flag string
   786  	}
   787  	var cli struct {
   788  		One Embed `prefix:"one-" embed:""`
   789  		Two Embed `prefix:"two-" embed:""`
   790  	}
   791  	buf := &strings.Builder{}
   792  	p := mustNew(t, &cli, kong.Writers(buf, buf), kong.Exit(func(int) {}))
   793  	_, err := p.Parse([]string{"--help"})
   794  	assert.NoError(t, err)
   795  	assert.Contains(t, buf.String(), `--one-flag=STRING`)
   796  	assert.Contains(t, buf.String(), `--two-flag=STRING`)
   797  }
   798  
   799  func TestHooksCalledForDefault(t *testing.T) {
   800  	var cli struct {
   801  		Flag hookValue `default:"default"`
   802  	}
   803  
   804  	ctx := &hookContext{}
   805  	_, err := mustNew(t, &cli, kong.Bind(ctx)).Parse(nil)
   806  	assert.NoError(t, err)
   807  	assert.Equal(t, "default", string(cli.Flag))
   808  	assert.Equal(t, []string{"before:default", "after:default"}, ctx.values)
   809  }
   810  
   811  func TestEnum(t *testing.T) {
   812  	var cli struct {
   813  		Flag string `enum:"a,b,c" required:""`
   814  	}
   815  	_, err := mustNew(t, &cli).Parse([]string{"--flag", "d"})
   816  	assert.EqualError(t, err, "--flag must be one of \"a\",\"b\",\"c\" but got \"d\"")
   817  }
   818  
   819  func TestEnumMeaningfulOrder(t *testing.T) {
   820  	var cli struct {
   821  		Flag string `enum:"first,second,third,fourth,fifth" required:""`
   822  	}
   823  	_, err := mustNew(t, &cli).Parse([]string{"--flag", "sixth"})
   824  	assert.EqualError(t, err, "--flag must be one of \"first\",\"second\",\"third\",\"fourth\",\"fifth\" but got \"sixth\"")
   825  }
   826  
   827  type commandWithHook struct {
   828  	value string
   829  }
   830  
   831  func (c *commandWithHook) AfterApply(cli *cliWithHook) error {
   832  	c.value = cli.Flag
   833  	return nil
   834  }
   835  
   836  type cliWithHook struct {
   837  	Flag    string
   838  	Command commandWithHook `cmd:""`
   839  }
   840  
   841  func (c *cliWithHook) AfterApply(ctx *kong.Context) error {
   842  	ctx.Bind(c)
   843  	return nil
   844  }
   845  
   846  func TestParentBindings(t *testing.T) {
   847  	cli := &cliWithHook{}
   848  	_, err := mustNew(t, cli).Parse([]string{"command", "--flag=foo"})
   849  	assert.NoError(t, err)
   850  	assert.Equal(t, "foo", cli.Command.value)
   851  }
   852  
   853  func TestNumericParamErrors(t *testing.T) {
   854  	var cli struct {
   855  		Name string
   856  	}
   857  	parser := mustNew(t, &cli)
   858  	_, err := parser.Parse([]string{"--name", "-10"})
   859  	assert.EqualError(t, err, `--name: expected string value but got "-10" (short flag); perhaps try --name="-10"?`)
   860  }
   861  
   862  func TestDefaultValueIsHyphen(t *testing.T) {
   863  	var cli struct {
   864  		Flag string `default:"-"`
   865  	}
   866  	p := mustNew(t, &cli)
   867  	_, err := p.Parse(nil)
   868  	assert.NoError(t, err)
   869  	assert.Equal(t, "-", cli.Flag)
   870  }
   871  
   872  func TestDefaultEnumValidated(t *testing.T) {
   873  	var cli struct {
   874  		Flag string `default:"invalid" enum:"valid"`
   875  	}
   876  	p := mustNew(t, &cli)
   877  	_, err := p.Parse(nil)
   878  	assert.EqualError(t, err, "--flag must be one of \"valid\" but got \"invalid\"")
   879  }
   880  
   881  func TestEnvarEnumValidated(t *testing.T) {
   882  	restore := tempEnv(map[string]string{
   883  		"FLAG": "invalid",
   884  	})
   885  	defer restore()
   886  	var cli struct {
   887  		Flag string `env:"FLAG" required:"" enum:"valid"`
   888  	}
   889  	p := mustNew(t, &cli)
   890  	_, err := p.Parse(nil)
   891  	assert.EqualError(t, err, "--flag must be one of \"valid\" but got \"invalid\"")
   892  }
   893  
   894  func TestXor(t *testing.T) {
   895  	var cli struct {
   896  		Hello bool   `xor:"another"`
   897  		One   bool   `xor:"group"`
   898  		Two   string `xor:"group"`
   899  	}
   900  	p := mustNew(t, &cli)
   901  	_, err := p.Parse([]string{"--hello", "--one", "--two=hi"})
   902  	assert.EqualError(t, err, "--one and --two can't be used together")
   903  
   904  	p = mustNew(t, &cli)
   905  	_, err = p.Parse([]string{"--one", "--hello"})
   906  	assert.NoError(t, err)
   907  }
   908  
   909  func TestXorChild(t *testing.T) {
   910  	var cli struct {
   911  		One bool `xor:"group"`
   912  		Cmd struct {
   913  			Two   string `xor:"group"`
   914  			Three string `xor:"group"`
   915  		} `cmd`
   916  	}
   917  	p := mustNew(t, &cli)
   918  	_, err := p.Parse([]string{"--one", "cmd", "--two=hi"})
   919  	assert.NoError(t, err)
   920  
   921  	p = mustNew(t, &cli)
   922  	_, err = p.Parse([]string{"--two=hi", "cmd", "--three"})
   923  	assert.Error(t, err, "--two and --three can't be used together")
   924  }
   925  
   926  func TestMultiXor(t *testing.T) {
   927  	var cli struct {
   928  		Hello bool   `xor:"one,two"`
   929  		One   bool   `xor:"one"`
   930  		Two   string `xor:"two"`
   931  	}
   932  
   933  	p := mustNew(t, &cli)
   934  	_, err := p.Parse([]string{"--hello", "--one"})
   935  	assert.EqualError(t, err, "--hello and --one can't be used together")
   936  
   937  	p = mustNew(t, &cli)
   938  	_, err = p.Parse([]string{"--hello", "--two=foo"})
   939  	assert.EqualError(t, err, "--hello and --two can't be used together")
   940  }
   941  
   942  func TestXorRequired(t *testing.T) {
   943  	var cli struct {
   944  		One   bool `xor:"one,two" required:""`
   945  		Two   bool `xor:"one" required:""`
   946  		Three bool `xor:"two" required:""`
   947  		Four  bool `required:""`
   948  	}
   949  	p := mustNew(t, &cli)
   950  	_, err := p.Parse([]string{"--one"})
   951  	assert.EqualError(t, err, "missing flags: --four")
   952  
   953  	p = mustNew(t, &cli)
   954  	_, err = p.Parse([]string{"--two"})
   955  	assert.EqualError(t, err, "missing flags: --four, --one or --three")
   956  
   957  	p = mustNew(t, &cli)
   958  	_, err = p.Parse([]string{})
   959  	assert.EqualError(t, err, "missing flags: --four, --one or --three, --one or --two")
   960  }
   961  
   962  func TestXorRequiredMany(t *testing.T) {
   963  	var cli struct {
   964  		One   bool `xor:"one" required:""`
   965  		Two   bool `xor:"one" required:""`
   966  		Three bool `xor:"one" required:""`
   967  	}
   968  	p := mustNew(t, &cli)
   969  	_, err := p.Parse([]string{"--one"})
   970  	assert.NoError(t, err)
   971  
   972  	p = mustNew(t, &cli)
   973  	_, err = p.Parse([]string{"--three"})
   974  	assert.NoError(t, err)
   975  
   976  	p = mustNew(t, &cli)
   977  	_, err = p.Parse([]string{})
   978  	assert.EqualError(t, err, "missing flags: --one or --two or --three")
   979  }
   980  
   981  func TestEnumSequence(t *testing.T) {
   982  	var cli struct {
   983  		State []string `enum:"a,b,c" default:"a"`
   984  	}
   985  	p := mustNew(t, &cli)
   986  	_, err := p.Parse(nil)
   987  	assert.NoError(t, err)
   988  	assert.Equal(t, []string{"a"}, cli.State)
   989  }
   990  
   991  func TestIssue40EnumAcrossCommands(t *testing.T) {
   992  	var cli struct {
   993  		One struct {
   994  			OneArg string `arg:"" required:""`
   995  		} `cmd:""`
   996  		Two struct {
   997  			TwoArg string `arg:"" enum:"a,b,c" required:"" env:"FOO"`
   998  		} `cmd:""`
   999  		Three struct {
  1000  			ThreeArg string `arg:"" optional:"" default:"a" enum:"a,b,c"`
  1001  		} `cmd:""`
  1002  	}
  1003  
  1004  	p := mustNew(t, &cli)
  1005  	_, err := p.Parse([]string{"one", "two"})
  1006  	assert.NoError(t, err)
  1007  	_, err = p.Parse([]string{"two", "d"})
  1008  	assert.Error(t, err)
  1009  	_, err = p.Parse([]string{"three", "d"})
  1010  	assert.Error(t, err)
  1011  	_, err = p.Parse([]string{"three", "c"})
  1012  	assert.NoError(t, err)
  1013  }
  1014  
  1015  func TestIssue179(t *testing.T) {
  1016  	type A struct {
  1017  		Enum string `required:"" enum:"1,2"`
  1018  	}
  1019  
  1020  	type B struct{}
  1021  
  1022  	var root struct {
  1023  		A A `cmd`
  1024  		B B `cmd`
  1025  	}
  1026  
  1027  	p := mustNew(t, &root)
  1028  	_, err := p.Parse([]string{"b"})
  1029  	assert.NoError(t, err)
  1030  }
  1031  
  1032  func TestIssue153(t *testing.T) {
  1033  	type LsCmd struct {
  1034  		Paths []string `arg required name:"path" help:"Paths to list." env:"CMD_PATHS"`
  1035  	}
  1036  
  1037  	var cli struct {
  1038  		Debug bool `help:"Enable debug mode."`
  1039  
  1040  		Ls LsCmd `cmd help:"List paths."`
  1041  	}
  1042  
  1043  	p, revert := newEnvParser(t, &cli, envMap{
  1044  		"CMD_PATHS": "hello",
  1045  	})
  1046  	defer revert()
  1047  	_, err := p.Parse([]string{"ls"})
  1048  	assert.NoError(t, err)
  1049  	assert.Equal(t, []string{"hello"}, cli.Ls.Paths)
  1050  }
  1051  
  1052  func TestEnumArg(t *testing.T) {
  1053  	var cli struct {
  1054  		Nested struct {
  1055  			One string `arg:"" enum:"a,b,c" required:""`
  1056  			Two string `arg:""`
  1057  		} `cmd:""`
  1058  	}
  1059  	p := mustNew(t, &cli)
  1060  	_, err := p.Parse([]string{"nested", "a", "b"})
  1061  	assert.NoError(t, err)
  1062  	assert.Equal(t, "a", cli.Nested.One)
  1063  	assert.Equal(t, "b", cli.Nested.Two)
  1064  }
  1065  
  1066  func TestDefaultCommand(t *testing.T) {
  1067  	var cli struct {
  1068  		One struct{} `cmd:"" default:"1"`
  1069  		Two struct{} `cmd:""`
  1070  	}
  1071  	p := mustNew(t, &cli)
  1072  	ctx, err := p.Parse([]string{})
  1073  	assert.NoError(t, err)
  1074  	assert.Equal(t, "one", ctx.Command())
  1075  }
  1076  
  1077  func TestMultipleDefaultCommands(t *testing.T) {
  1078  	var cli struct {
  1079  		One struct{} `cmd:"" default:"1"`
  1080  		Two struct{} `cmd:"" default:"1"`
  1081  	}
  1082  	_, err := kong.New(&cli)
  1083  	assert.EqualError(t, err, "<anonymous struct>.Two: can't have more than one default command under  <command>")
  1084  }
  1085  
  1086  func TestDefaultCommandWithSubCommand(t *testing.T) {
  1087  	var cli struct {
  1088  		One struct {
  1089  			Two struct{} `cmd:""`
  1090  		} `cmd:"" default:"1"`
  1091  	}
  1092  	_, err := kong.New(&cli)
  1093  	assert.EqualError(t, err, "<anonymous struct>.One: default command one <command> must not have subcommands or arguments")
  1094  }
  1095  
  1096  func TestDefaultCommandWithAllowedSubCommand(t *testing.T) {
  1097  	var cli struct {
  1098  		One struct {
  1099  			Two struct{} `cmd:""`
  1100  		} `cmd:"" default:"withargs"`
  1101  	}
  1102  	p := mustNew(t, &cli)
  1103  	ctx, err := p.Parse([]string{"two"})
  1104  	assert.NoError(t, err)
  1105  	assert.Equal(t, "one two", ctx.Command())
  1106  }
  1107  
  1108  func TestDefaultCommandWithArgument(t *testing.T) {
  1109  	var cli struct {
  1110  		One struct {
  1111  			Arg string `arg:""`
  1112  		} `cmd:"" default:"1"`
  1113  	}
  1114  	_, err := kong.New(&cli)
  1115  	assert.EqualError(t, err, "<anonymous struct>.One: default command one <arg> must not have subcommands or arguments")
  1116  }
  1117  
  1118  func TestDefaultCommandWithAllowedArgument(t *testing.T) {
  1119  	var cli struct {
  1120  		One struct {
  1121  			Arg  string `arg:""`
  1122  			Flag string
  1123  		} `cmd:"" default:"withargs"`
  1124  	}
  1125  	p := mustNew(t, &cli)
  1126  	_, err := p.Parse([]string{"arg", "--flag=value"})
  1127  	assert.NoError(t, err)
  1128  	assert.Equal(t, "arg", cli.One.Arg)
  1129  	assert.Equal(t, "value", cli.One.Flag)
  1130  }
  1131  
  1132  func TestDefaultCommandWithBranchingArgument(t *testing.T) {
  1133  	var cli struct {
  1134  		One struct {
  1135  			Two struct {
  1136  				Two string `arg:""`
  1137  			} `arg:""`
  1138  		} `cmd:"" default:"1"`
  1139  	}
  1140  	_, err := kong.New(&cli)
  1141  	assert.EqualError(t, err, "<anonymous struct>.One: default command one <command> must not have subcommands or arguments")
  1142  }
  1143  
  1144  func TestDefaultCommandWithAllowedBranchingArgument(t *testing.T) {
  1145  	var cli struct {
  1146  		One struct {
  1147  			Two struct {
  1148  				Two  string `arg:""`
  1149  				Flag string
  1150  			} `arg:""`
  1151  		} `cmd:"" default:"withargs"`
  1152  	}
  1153  	p := mustNew(t, &cli)
  1154  	_, err := p.Parse([]string{"arg", "--flag=value"})
  1155  	assert.NoError(t, err)
  1156  	assert.Equal(t, "arg", cli.One.Two.Two)
  1157  	assert.Equal(t, "value", cli.One.Two.Flag)
  1158  }
  1159  
  1160  func TestDefaultCommandPrecedence(t *testing.T) {
  1161  	var cli struct {
  1162  		Two struct {
  1163  			Arg  string `arg:""`
  1164  			Flag bool
  1165  		} `cmd:"" default:"withargs"`
  1166  		One struct{} `cmd:""`
  1167  	}
  1168  	p := mustNew(t, &cli)
  1169  
  1170  	// A named command should take precedence over a default command with arg
  1171  	ctx, err := p.Parse([]string{"one"})
  1172  	assert.NoError(t, err)
  1173  	assert.Equal(t, "one", ctx.Command())
  1174  
  1175  	// An explicitly named command with arg should parse, even if labeled default:"witharg"
  1176  	ctx, err = p.Parse([]string{"two", "arg"})
  1177  	assert.NoError(t, err)
  1178  	assert.Equal(t, "two <arg>", ctx.Command())
  1179  
  1180  	// An arg to a default command that does not match another command should select the default
  1181  	ctx, err = p.Parse([]string{"arg"})
  1182  	assert.NoError(t, err)
  1183  	assert.Equal(t, "two <arg>", ctx.Command())
  1184  
  1185  	// A flag on a default command should not be valid on a sibling command
  1186  	_, err = p.Parse([]string{"one", "--flag"})
  1187  	assert.EqualError(t, err, "unknown flag --flag")
  1188  }
  1189  
  1190  func TestLoneHpyhen(t *testing.T) {
  1191  	var cli struct {
  1192  		Flag string
  1193  		Arg  string `arg:"" optional:""`
  1194  	}
  1195  	p := mustNew(t, &cli)
  1196  
  1197  	_, err := p.Parse([]string{"-"})
  1198  	assert.NoError(t, err)
  1199  	assert.Equal(t, "-", cli.Arg)
  1200  
  1201  	_, err = p.Parse([]string{"--flag", "-"})
  1202  	assert.NoError(t, err)
  1203  	assert.Equal(t, "-", cli.Flag)
  1204  }
  1205  
  1206  func TestPlugins(t *testing.T) {
  1207  	var pluginOne struct {
  1208  		One string
  1209  	}
  1210  	var pluginTwo struct {
  1211  		Two string
  1212  	}
  1213  	var cli struct {
  1214  		Base string
  1215  		kong.Plugins
  1216  	}
  1217  	cli.Plugins = kong.Plugins{&pluginOne, &pluginTwo}
  1218  
  1219  	p := mustNew(t, &cli)
  1220  	_, err := p.Parse([]string{"--base=base", "--one=one", "--two=two"})
  1221  	assert.NoError(t, err)
  1222  	assert.Equal(t, "base", cli.Base)
  1223  	assert.Equal(t, "one", pluginOne.One)
  1224  	assert.Equal(t, "two", pluginTwo.Two)
  1225  }
  1226  
  1227  type validateCmd struct{}
  1228  
  1229  func (v *validateCmd) Validate() error { return errors.New("cmd error") }
  1230  
  1231  type validateCli struct {
  1232  	Cmd validateCmd `cmd:""`
  1233  }
  1234  
  1235  func (v *validateCli) Validate() error { return errors.New("app error") }
  1236  
  1237  type validateFlag string
  1238  
  1239  func (v *validateFlag) Validate() error { return errors.New("flag error") }
  1240  
  1241  func TestValidateApp(t *testing.T) {
  1242  	cli := validateCli{}
  1243  	p := mustNew(t, &cli)
  1244  	_, err := p.Parse([]string{})
  1245  	assert.EqualError(t, err, "app error")
  1246  }
  1247  
  1248  func TestValidateCmd(t *testing.T) {
  1249  	cli := struct {
  1250  		Cmd validateCmd `cmd:""`
  1251  	}{}
  1252  	p := mustNew(t, &cli)
  1253  	_, err := p.Parse([]string{"cmd"})
  1254  	assert.EqualError(t, err, "cmd: cmd error")
  1255  }
  1256  
  1257  func TestValidateFlag(t *testing.T) {
  1258  	cli := struct {
  1259  		Flag validateFlag
  1260  	}{}
  1261  	p := mustNew(t, &cli)
  1262  	_, err := p.Parse([]string{"--flag=one"})
  1263  	assert.EqualError(t, err, "--flag: flag error")
  1264  }
  1265  
  1266  func TestValidateArg(t *testing.T) {
  1267  	cli := struct {
  1268  		Arg validateFlag `arg:""`
  1269  	}{}
  1270  	p := mustNew(t, &cli)
  1271  	_, err := p.Parse([]string{"one"})
  1272  	assert.EqualError(t, err, "<arg>: flag error")
  1273  }
  1274  
  1275  func TestPointers(t *testing.T) {
  1276  	cli := struct {
  1277  		Mapped *mappedValue
  1278  		JSON   *jsonUnmarshalerValue
  1279  	}{}
  1280  	p := mustNew(t, &cli)
  1281  	_, err := p.Parse([]string{"--mapped=mapped", "--json=\"foo\""})
  1282  	assert.NoError(t, err)
  1283  	assert.NotZero(t, cli.Mapped)
  1284  	assert.Equal(t, "mapped", cli.Mapped.decoded)
  1285  	assert.NotZero(t, cli.JSON)
  1286  	assert.Equal(t, "FOO", string(*cli.JSON))
  1287  }
  1288  
  1289  type dynamicCommand struct {
  1290  	Flag string
  1291  
  1292  	ran bool
  1293  }
  1294  
  1295  func (d *dynamicCommand) Run() error {
  1296  	d.ran = true
  1297  	return nil
  1298  }
  1299  
  1300  func TestDynamicCommands(t *testing.T) {
  1301  	cli := struct {
  1302  		One struct{} `cmd:"one"`
  1303  	}{}
  1304  	help := &strings.Builder{}
  1305  	two := &dynamicCommand{}
  1306  	three := &dynamicCommand{}
  1307  	p := mustNew(t, &cli,
  1308  		kong.DynamicCommand("two", "", "", &two),
  1309  		kong.DynamicCommand("three", "", "", three, "hidden"),
  1310  		kong.Writers(help, help),
  1311  		kong.Exit(func(int) {}))
  1312  	kctx, err := p.Parse([]string{"two", "--flag=flag"})
  1313  	assert.NoError(t, err)
  1314  	assert.Equal(t, "flag", two.Flag)
  1315  	assert.False(t, two.ran)
  1316  	err = kctx.Run()
  1317  	assert.NoError(t, err)
  1318  	assert.True(t, two.ran)
  1319  
  1320  	_, err = p.Parse([]string{"--help"})
  1321  	assert.EqualError(t, err, `expected one of "one",  "two"`)
  1322  	assert.NotContains(t, help.String(), "three", help.String())
  1323  }
  1324  
  1325  func TestDuplicateShortflags(t *testing.T) {
  1326  	cli := struct {
  1327  		Flag1 bool `short:"t"`
  1328  		Flag2 bool `short:"t"`
  1329  	}{}
  1330  	_, err := kong.New(&cli)
  1331  	assert.EqualError(t, err, "<anonymous struct>.Flag2: duplicate short flag -t")
  1332  }
  1333  
  1334  func TestDuplicateAliases(t *testing.T) {
  1335  	cli1 := struct {
  1336  		Flag1 string `aliases:"flag"`
  1337  		Flag2 string `aliases:"flag"`
  1338  	}{}
  1339  	_, err := kong.New(&cli1)
  1340  	assert.EqualError(t, err, "<anonymous struct>.Flag2: duplicate flag --flag")
  1341  }
  1342  
  1343  func TestSubCommandAliases(t *testing.T) {
  1344  	type SubC struct {
  1345  		Flag1 string `aliases:"flag"`
  1346  	}
  1347  
  1348  	cli1 := struct {
  1349  		Sub1 SubC `cmd:"sub1"`
  1350  		Sub2 SubC `cmd:"sub2"`
  1351  	}{}
  1352  
  1353  	_, err := kong.New(&cli1)
  1354  	assert.NoError(t, err, "dupe aliases shouldn't error if they're in separate sub commands")
  1355  }
  1356  
  1357  func TestDuplicateAliasLong(t *testing.T) {
  1358  	cli2 := struct {
  1359  		Flag  string ``
  1360  		Flag2 string `aliases:"flag"` // duplicates Flag
  1361  	}{}
  1362  	_, err := kong.New(&cli2)
  1363  	assert.EqualError(t, err, "<anonymous struct>.Flag2: duplicate flag --flag")
  1364  }
  1365  
  1366  func TestDuplicateNestedShortFlags(t *testing.T) {
  1367  	cli := struct {
  1368  		Flag1 bool `short:"t"`
  1369  		Cmd   struct {
  1370  			Flag2 bool `short:"t"`
  1371  		} `cmd:""`
  1372  	}{}
  1373  	_, err := kong.New(&cli)
  1374  	assert.EqualError(t, err, "<anonymous struct>.Flag2: duplicate short flag -t")
  1375  }
  1376  
  1377  func TestHydratePointerCommandsAndEmbeds(t *testing.T) {
  1378  	type cmd struct {
  1379  		Flag bool
  1380  	}
  1381  
  1382  	type embed struct {
  1383  		Embed bool
  1384  	}
  1385  
  1386  	var cli struct {
  1387  		Cmd   *cmd   `cmd:""`
  1388  		Embed *embed `embed:""`
  1389  	}
  1390  
  1391  	k := mustNew(t, &cli)
  1392  	_, err := k.Parse([]string{"--embed", "cmd", "--flag"})
  1393  	assert.NoError(t, err)
  1394  	assert.Equal(t, &cmd{Flag: true}, cli.Cmd)
  1395  	assert.Equal(t, &embed{Embed: true}, cli.Embed)
  1396  }
  1397  
  1398  //nolint:revive
  1399  type testIgnoreFields struct {
  1400  	Foo struct {
  1401  		Bar bool
  1402  		Sub struct {
  1403  			SubFlag1     bool `kong:"name=subflag1"`
  1404  			XXX_SubFlag2 bool `kong:"name=subflag2"` //nolint:stylecheck
  1405  		} `kong:"cmd"`
  1406  	} `kong:"cmd"`
  1407  	XXX_Baz struct { //nolint:stylecheck
  1408  		Boo bool
  1409  	} `kong:"cmd,name=baz"`
  1410  }
  1411  
  1412  func TestIgnoreRegex(t *testing.T) {
  1413  	cli := testIgnoreFields{}
  1414  
  1415  	k, err := kong.New(&cli, kong.IgnoreFields(`.*\.XXX_.+`))
  1416  	assert.NoError(t, err)
  1417  
  1418  	_, err = k.Parse([]string{"foo", "sub"})
  1419  	assert.NoError(t, err)
  1420  
  1421  	_, err = k.Parse([]string{"foo", "sub", "--subflag1"})
  1422  	assert.NoError(t, err)
  1423  
  1424  	_, err = k.Parse([]string{"foo", "sub", "--subflag2"})
  1425  	assert.Error(t, err)
  1426  	assert.Contains(t, err.Error(), "unknown flag --subflag2")
  1427  
  1428  	_, err = k.Parse([]string{"baz"})
  1429  	assert.Error(t, err)
  1430  	assert.Contains(t, err.Error(), "unexpected argument baz")
  1431  }
  1432  
  1433  // Verify that passing a nil regex will work
  1434  func TestIgnoreRegexEmpty(t *testing.T) {
  1435  	cli := testIgnoreFields{}
  1436  
  1437  	_, err := kong.New(&cli, kong.IgnoreFields(""))
  1438  	assert.Error(t, err)
  1439  	assert.Contains(t, "regex input cannot be empty", err.Error())
  1440  }
  1441  
  1442  type optionWithErr struct{}
  1443  
  1444  func (o *optionWithErr) Apply(k *kong.Kong) error {
  1445  	return errors.New("option returned err")
  1446  }
  1447  
  1448  func TestOptionReturnsErr(t *testing.T) {
  1449  	cli := struct {
  1450  		Test bool
  1451  	}{}
  1452  
  1453  	optWithError := &optionWithErr{}
  1454  
  1455  	_, err := kong.New(cli, optWithError)
  1456  	assert.Error(t, err)
  1457  	assert.Equal(t, "option returned err", err.Error())
  1458  }
  1459  
  1460  func TestEnumValidation(t *testing.T) {
  1461  	tests := []struct {
  1462  		name string
  1463  		cli  interface{}
  1464  		fail bool
  1465  	}{
  1466  		{
  1467  			"Arg",
  1468  			&struct {
  1469  				Enum string `arg:"" enum:"one,two"`
  1470  			}{},
  1471  			false,
  1472  		},
  1473  		{
  1474  			"RequiredArg",
  1475  			&struct {
  1476  				Enum string `required:"" arg:"" enum:"one,two"`
  1477  			}{},
  1478  			false,
  1479  		},
  1480  		{
  1481  			"OptionalArg",
  1482  			&struct {
  1483  				Enum string `optional:"" arg:"" enum:"one,two"`
  1484  			}{},
  1485  			true,
  1486  		},
  1487  		{
  1488  			"RepeatedArgs",
  1489  			&struct {
  1490  				Enum []string `arg:"" enum:"one,two"`
  1491  			}{},
  1492  			false,
  1493  		},
  1494  		{
  1495  			"RequiredRepeatedArgs",
  1496  			&struct {
  1497  				Enum []string `required:"" arg:"" enum:"one,two"`
  1498  			}{},
  1499  			false,
  1500  		},
  1501  		{
  1502  			"OptionalRepeatedArgs",
  1503  			&struct {
  1504  				Enum []string `optional:"" arg:"" enum:"one,two"`
  1505  			}{},
  1506  			false,
  1507  		},
  1508  		{
  1509  			"EnumWithEmptyDefault",
  1510  			&struct {
  1511  				Flag string `enum:"one,two," default:""`
  1512  			}{},
  1513  			false,
  1514  		},
  1515  	}
  1516  	for _, test := range tests {
  1517  		test := test
  1518  		t.Run(test.name, func(t *testing.T) {
  1519  			_, err := kong.New(test.cli)
  1520  			if test.fail {
  1521  				assert.Error(t, err, repr.String(test.cli))
  1522  			} else {
  1523  				assert.NoError(t, err, repr.String(test.cli))
  1524  			}
  1525  		})
  1526  	}
  1527  }
  1528  
  1529  func TestPassthroughCmd(t *testing.T) {
  1530  	tests := []struct {
  1531  		name    string
  1532  		args    []string
  1533  		flag    string
  1534  		cmdArgs []string
  1535  	}{
  1536  		{
  1537  			"Simple",
  1538  			[]string{"--flag", "foobar", "command", "something"},
  1539  			"foobar",
  1540  			[]string{"something"},
  1541  		},
  1542  		{
  1543  			"DashDash",
  1544  			[]string{"--flag", "foobar", "command", "--", "something"},
  1545  			"foobar",
  1546  			[]string{"--", "something"},
  1547  		},
  1548  		{
  1549  			"Flag",
  1550  			[]string{"command", "--flag", "foobar"},
  1551  			"",
  1552  			[]string{"--flag", "foobar"},
  1553  		},
  1554  		{
  1555  			"FlagAndFlag",
  1556  			[]string{"--flag", "foobar", "command", "--flag", "foobar"},
  1557  			"foobar",
  1558  			[]string{"--flag", "foobar"},
  1559  		},
  1560  		{
  1561  			"NoArgs",
  1562  			[]string{"--flag", "foobar", "command"},
  1563  			"foobar",
  1564  			[]string(nil),
  1565  		},
  1566  	}
  1567  	for _, test := range tests {
  1568  		test := test
  1569  		t.Run(test.name, func(t *testing.T) {
  1570  			var cli struct {
  1571  				Flag    string
  1572  				Command struct {
  1573  					Args []string `arg:"" optional:""`
  1574  				} `cmd:"" passthrough:""`
  1575  			}
  1576  			p := mustNew(t, &cli)
  1577  			_, err := p.Parse(test.args)
  1578  			assert.NoError(t, err)
  1579  			assert.Equal(t, test.flag, cli.Flag)
  1580  			assert.Equal(t, test.cmdArgs, cli.Command.Args)
  1581  		})
  1582  	}
  1583  }
  1584  
  1585  func TestPassthroughCmdOnlyArgs(t *testing.T) {
  1586  	var cli struct {
  1587  		Command struct {
  1588  			Flag string
  1589  			Args []string `arg:"" optional:""`
  1590  		} `cmd:"" passthrough:""`
  1591  	}
  1592  	_, err := kong.New(&cli)
  1593  	assert.EqualError(t, err, "<anonymous struct>.Command: passthrough command command [<args> ...] [flags] must not have subcommands or flags")
  1594  }
  1595  
  1596  func TestPassthroughCmdOnlyStringArgs(t *testing.T) {
  1597  	var cli struct {
  1598  		Command struct {
  1599  			Args []int `arg:"" optional:""`
  1600  		} `cmd:"" passthrough:""`
  1601  	}
  1602  	_, err := kong.New(&cli)
  1603  	assert.EqualError(t, err, "<anonymous struct>.Command: passthrough command command [<args> ...] must contain exactly one positional argument of []string type")
  1604  }
  1605  
  1606  func TestHelpShouldStillWork(t *testing.T) {
  1607  	type CLI struct {
  1608  		Dir  string `type:"existingdir" default:"missing-dir"`
  1609  		File string `type:"existingfile" default:"testdata/missing.txt"`
  1610  	}
  1611  	var cli CLI
  1612  	w := &strings.Builder{}
  1613  	k := mustNew(t, &cli, kong.Writers(w, w))
  1614  	rc := -1 // init nonzero to help assert help hook was called
  1615  	k.Exit = func(i int) {
  1616  		rc = i
  1617  	}
  1618  	_, err := k.Parse([]string{"--help"})
  1619  	t.Log(w.String())
  1620  	// checking return code validates the help hook was called
  1621  	assert.Zero(t, rc)
  1622  	// allow for error propagation from other validation (only for the
  1623  	// sake of this test, due to the exit function not actually exiting the
  1624  	// program; errors will not propagate in the real world).
  1625  	assert.Error(t, err)
  1626  }
  1627  
  1628  func TestVersionFlagShouldStillWork(t *testing.T) {
  1629  	type CLI struct {
  1630  		Dir     string `type:"existingdir" default:"missing-dir"`
  1631  		File    string `type:"existingfile" default:"testdata/missing.txt"`
  1632  		Version kong.VersionFlag
  1633  	}
  1634  	var cli CLI
  1635  	w := &strings.Builder{}
  1636  	k := mustNew(t, &cli, kong.Writers(w, w))
  1637  	rc := -1 // init nonzero to help assert help hook was called
  1638  	k.Exit = func(i int) {
  1639  		rc = i
  1640  	}
  1641  	_, err := k.Parse([]string{"--version"})
  1642  	t.Log(w.String())
  1643  	// checking return code validates the help hook was called
  1644  	assert.Zero(t, rc)
  1645  	// allow for error propagation from other validation (only for the
  1646  	// sake of this test, due to the exit function not actually exiting the
  1647  	// program; errors will not propagate in the real world).
  1648  	assert.Error(t, err)
  1649  }
  1650  
  1651  func TestSliceDecoderHelpfulErrorMsg(t *testing.T) {
  1652  	tests := []struct {
  1653  		name string
  1654  		cli  interface{}
  1655  		args []string
  1656  		err  string
  1657  	}{
  1658  		{
  1659  			"DefaultRune",
  1660  			&struct {
  1661  				Stuff []string
  1662  			}{},
  1663  			[]string{"--stuff"},
  1664  			`--stuff: missing value, expecting "<arg>,..."`,
  1665  		},
  1666  		{
  1667  			"SpecifiedRune",
  1668  			&struct {
  1669  				Stuff []string `sep:","`
  1670  			}{},
  1671  			[]string{"--stuff"},
  1672  			`--stuff: missing value, expecting "<arg>,..."`,
  1673  		},
  1674  		{
  1675  			"SpaceRune",
  1676  			&struct {
  1677  				Stuff []string `sep:" "`
  1678  			}{},
  1679  			[]string{"--stuff"},
  1680  			`--stuff: missing value, expecting "<arg> ..."`,
  1681  		},
  1682  		{
  1683  			"OtherRune",
  1684  			&struct {
  1685  				Stuff []string `sep:"_"`
  1686  			}{},
  1687  			[]string{"--stuff"},
  1688  			`--stuff: missing value, expecting "<arg>_..."`,
  1689  		},
  1690  	}
  1691  	for _, test := range tests {
  1692  		test := test
  1693  		t.Run(test.name, func(t *testing.T) {
  1694  			p := mustNew(t, test.cli)
  1695  			_, err := p.Parse(test.args)
  1696  			assert.EqualError(t, err, test.err)
  1697  		})
  1698  	}
  1699  }
  1700  
  1701  func TestMapDecoderHelpfulErrorMsg(t *testing.T) {
  1702  	tests := []struct {
  1703  		name     string
  1704  		cli      interface{}
  1705  		args     []string
  1706  		expected string
  1707  	}{
  1708  		{
  1709  			"DefaultRune",
  1710  			&struct {
  1711  				Stuff map[string]int
  1712  			}{},
  1713  			[]string{"--stuff"},
  1714  			`--stuff: missing value, expecting "<key>=<value>;..."`,
  1715  		},
  1716  		{
  1717  			"SpecifiedRune",
  1718  			&struct {
  1719  				Stuff map[string]int `mapsep:";"`
  1720  			}{},
  1721  			[]string{"--stuff"},
  1722  			`--stuff: missing value, expecting "<key>=<value>;..."`,
  1723  		},
  1724  		{
  1725  			"SpaceRune",
  1726  			&struct {
  1727  				Stuff map[string]int `mapsep:" "`
  1728  			}{},
  1729  			[]string{"--stuff"},
  1730  			`--stuff: missing value, expecting "<key>=<value> ..."`,
  1731  		},
  1732  		{
  1733  			"OtherRune",
  1734  			&struct {
  1735  				Stuff map[string]int `mapsep:","`
  1736  			}{},
  1737  			[]string{"--stuff"},
  1738  			`--stuff: missing value, expecting "<key>=<value>,..."`,
  1739  		},
  1740  	}
  1741  	for _, test := range tests {
  1742  		test := test
  1743  		t.Run(test.name, func(t *testing.T) {
  1744  			p := mustNew(t, test.cli)
  1745  			_, err := p.Parse(test.args)
  1746  			assert.EqualError(t, err, test.expected)
  1747  		})
  1748  	}
  1749  }
  1750  
  1751  func TestDuplicateName(t *testing.T) {
  1752  	var cli struct {
  1753  		DupA struct{} `cmd:"" name:"duplicate"`
  1754  		DupB struct{} `cmd:"" name:"duplicate"`
  1755  	}
  1756  	_, err := kong.New(&cli)
  1757  	assert.Error(t, err)
  1758  }
  1759  
  1760  func TestDuplicateChildName(t *testing.T) {
  1761  	var cli struct {
  1762  		A struct {
  1763  			DupA struct{} `cmd:"" name:"duplicate"`
  1764  			DupB struct{} `cmd:"" name:"duplicate"`
  1765  		} `cmd:""`
  1766  		B struct{} `cmd:""`
  1767  	}
  1768  	_, err := kong.New(&cli)
  1769  	assert.Error(t, err)
  1770  }
  1771  
  1772  func TestChildNameCanBeDuplicated(t *testing.T) {
  1773  	var cli struct {
  1774  		A struct {
  1775  			A struct{} `cmd:"" name:"duplicateA"`
  1776  			B struct{} `cmd:"" name:"duplicateB"`
  1777  		} `cmd:"" name:"duplicateA"`
  1778  		B struct{} `cmd:"" name:"duplicateB"`
  1779  	}
  1780  	mustNew(t, &cli)
  1781  }
  1782  
  1783  func TestCumulativeArgumentLast(t *testing.T) {
  1784  	var cli struct {
  1785  		Arg1 string   `arg:""`
  1786  		Arg2 []string `arg:""`
  1787  	}
  1788  	_, err := kong.New(&cli)
  1789  	assert.NoError(t, err)
  1790  }
  1791  
  1792  func TestCumulativeArgumentNotLast(t *testing.T) {
  1793  	var cli struct {
  1794  		Arg2 []string `arg:""`
  1795  		Arg1 string   `arg:""`
  1796  	}
  1797  	_, err := kong.New(&cli)
  1798  	assert.Error(t, err)
  1799  }
  1800  
  1801  func TestStringPointer(t *testing.T) {
  1802  	var cli struct {
  1803  		Foo *string
  1804  	}
  1805  	k, err := kong.New(&cli)
  1806  	assert.NoError(t, err)
  1807  	assert.NotZero(t, k)
  1808  	ctx, err := k.Parse([]string{"--foo", "wtf"})
  1809  	assert.NoError(t, err)
  1810  	assert.NotZero(t, ctx)
  1811  	assert.NotZero(t, cli.Foo)
  1812  	assert.Equal(t, "wtf", *cli.Foo)
  1813  }
  1814  
  1815  func TestStringPointerNoValue(t *testing.T) {
  1816  	var cli struct {
  1817  		Foo *string
  1818  	}
  1819  	k, err := kong.New(&cli)
  1820  	assert.NoError(t, err)
  1821  	assert.NotZero(t, k)
  1822  	ctx, err := k.Parse([]string{})
  1823  	assert.NoError(t, err)
  1824  	assert.NotZero(t, ctx)
  1825  	assert.Zero(t, cli.Foo)
  1826  }
  1827  
  1828  func TestStringPointerDefault(t *testing.T) {
  1829  	var cli struct {
  1830  		Foo *string `default:"stuff"`
  1831  	}
  1832  	k, err := kong.New(&cli)
  1833  	assert.NoError(t, err)
  1834  	assert.NotZero(t, k)
  1835  	ctx, err := k.Parse([]string{})
  1836  	assert.NoError(t, err)
  1837  	assert.NotZero(t, ctx)
  1838  	assert.NotZero(t, cli.Foo)
  1839  	assert.Equal(t, "stuff", *cli.Foo)
  1840  }
  1841  
  1842  func TestStringPointerAliasNoValue(t *testing.T) {
  1843  	type Foo string
  1844  	var cli struct {
  1845  		F *Foo
  1846  	}
  1847  	k, err := kong.New(&cli)
  1848  	assert.NoError(t, err)
  1849  	assert.NotZero(t, k)
  1850  	ctx, err := k.Parse([]string{})
  1851  	assert.NoError(t, err)
  1852  	assert.NotZero(t, ctx)
  1853  	assert.Zero(t, cli.F)
  1854  }
  1855  
  1856  func TestStringPointerAlias(t *testing.T) {
  1857  	type Foo string
  1858  	var cli struct {
  1859  		F *Foo
  1860  	}
  1861  	k, err := kong.New(&cli)
  1862  	assert.NoError(t, err)
  1863  	assert.NotZero(t, k)
  1864  	ctx, err := k.Parse([]string{"--f=value"})
  1865  	assert.NoError(t, err)
  1866  	assert.NotZero(t, ctx)
  1867  	assert.NotZero(t, cli.F)
  1868  	assert.Equal(t, Foo("value"), *cli.F)
  1869  }
  1870  
  1871  func TestStringPointerEmptyValue(t *testing.T) {
  1872  	var cli struct {
  1873  		F *string
  1874  		G *string
  1875  	}
  1876  	k, err := kong.New(&cli)
  1877  	assert.NoError(t, err)
  1878  	assert.NotZero(t, k)
  1879  	ctx, err := k.Parse([]string{"--f", "", "--g="})
  1880  	assert.NoError(t, err)
  1881  	assert.NotZero(t, ctx)
  1882  	assert.NotZero(t, cli.F)
  1883  	assert.NotZero(t, cli.G)
  1884  	assert.Equal(t, "", *cli.F)
  1885  	assert.Equal(t, "", *cli.G)
  1886  }
  1887  
  1888  func TestIntPtr(t *testing.T) {
  1889  	var cli struct {
  1890  		F *int
  1891  		G *int
  1892  	}
  1893  	k, err := kong.New(&cli)
  1894  	assert.NoError(t, err)
  1895  	assert.NotZero(t, k)
  1896  	ctx, err := k.Parse([]string{"--f=6"})
  1897  	assert.NoError(t, err)
  1898  	assert.NotZero(t, ctx)
  1899  	assert.NotZero(t, cli.F)
  1900  	assert.Zero(t, cli.G)
  1901  	assert.Equal(t, 6, *cli.F)
  1902  }
  1903  
  1904  func TestBoolPtr(t *testing.T) {
  1905  	var cli struct {
  1906  		X *bool
  1907  	}
  1908  	k, err := kong.New(&cli)
  1909  	assert.NoError(t, err)
  1910  	assert.NotZero(t, k)
  1911  	ctx, err := k.Parse([]string{"--x"})
  1912  	assert.NoError(t, err)
  1913  	assert.NotZero(t, ctx)
  1914  	assert.NotZero(t, cli.X)
  1915  	assert.Equal(t, true, *cli.X)
  1916  }
  1917  
  1918  func TestBoolPtrFalse(t *testing.T) {
  1919  	var cli struct {
  1920  		X *bool
  1921  	}
  1922  	k, err := kong.New(&cli)
  1923  	assert.NoError(t, err)
  1924  	assert.NotZero(t, k)
  1925  	ctx, err := k.Parse([]string{"--x=false"})
  1926  	assert.NoError(t, err)
  1927  	assert.NotZero(t, ctx)
  1928  	assert.NotZero(t, cli.X)
  1929  	assert.Equal(t, false, *cli.X)
  1930  }
  1931  
  1932  func TestBoolPtrNegated(t *testing.T) {
  1933  	var cli struct {
  1934  		X *bool `negatable:""`
  1935  	}
  1936  	k, err := kong.New(&cli)
  1937  	assert.NoError(t, err)
  1938  	assert.NotZero(t, k)
  1939  	ctx, err := k.Parse([]string{"--no-x"})
  1940  	assert.NoError(t, err)
  1941  	assert.NotZero(t, ctx)
  1942  	assert.NotZero(t, cli.X)
  1943  	assert.Equal(t, false, *cli.X)
  1944  }
  1945  
  1946  func TestNilNegatableBoolPtr(t *testing.T) {
  1947  	var cli struct {
  1948  		X *bool `negatable:""`
  1949  	}
  1950  	k, err := kong.New(&cli)
  1951  	assert.NoError(t, err)
  1952  	assert.NotZero(t, k)
  1953  	ctx, err := k.Parse([]string{})
  1954  	assert.NoError(t, err)
  1955  	assert.NotZero(t, ctx)
  1956  	assert.Zero(t, cli.X)
  1957  }
  1958  
  1959  func TestBoolPtrNil(t *testing.T) {
  1960  	var cli struct {
  1961  		X *bool
  1962  	}
  1963  	k, err := kong.New(&cli)
  1964  	assert.NoError(t, err)
  1965  	assert.NotZero(t, k)
  1966  	ctx, err := k.Parse([]string{})
  1967  	assert.NoError(t, err)
  1968  	assert.NotZero(t, ctx)
  1969  	assert.Zero(t, cli.X)
  1970  }
  1971  
  1972  func TestUnsupportedPtr(t *testing.T) {
  1973  	type Foo struct {
  1974  		x int //nolint
  1975  		y int //nolint
  1976  	}
  1977  
  1978  	var cli struct {
  1979  		F *Foo
  1980  	}
  1981  	k, err := kong.New(&cli)
  1982  	assert.NoError(t, err)
  1983  	assert.NotZero(t, k)
  1984  	ctx, err := k.Parse([]string{"--f=whatever"})
  1985  	assert.Zero(t, ctx)
  1986  	assert.Error(t, err)
  1987  	assert.Equal(t, "--f: cannot find mapper for kong_test.Foo", err.Error())
  1988  }
  1989  
  1990  func TestEnumPtr(t *testing.T) {
  1991  	var cli struct {
  1992  		X *string `enum:"A,B,C" default:"C"`
  1993  	}
  1994  	k, err := kong.New(&cli)
  1995  	assert.NoError(t, err)
  1996  	assert.NotZero(t, k)
  1997  	ctx, err := k.Parse([]string{"--x=A"})
  1998  	assert.NoError(t, err)
  1999  	assert.NotZero(t, ctx)
  2000  	assert.NotZero(t, cli.X)
  2001  	assert.Equal(t, "A", *cli.X)
  2002  }
  2003  
  2004  func TestEnumPtrOmitted(t *testing.T) {
  2005  	var cli struct {
  2006  		X *string `enum:"A,B,C" default:"C"`
  2007  	}
  2008  	k, err := kong.New(&cli)
  2009  	assert.NoError(t, err)
  2010  	assert.NotZero(t, k)
  2011  	ctx, err := k.Parse([]string{})
  2012  	assert.NoError(t, err)
  2013  	assert.NotZero(t, ctx)
  2014  	assert.NotZero(t, cli.X)
  2015  	assert.Equal(t, "C", *cli.X)
  2016  }
  2017  
  2018  func TestEnumPtrOmittedNoDefault(t *testing.T) {
  2019  	var cli struct {
  2020  		X *string `enum:"A,B,C"`
  2021  	}
  2022  	k, err := kong.New(&cli)
  2023  	assert.NoError(t, err)
  2024  	assert.NotZero(t, k)
  2025  	ctx, err := k.Parse([]string{})
  2026  	assert.NoError(t, err)
  2027  	assert.NotZero(t, ctx)
  2028  	assert.Zero(t, cli.X)
  2029  }