github.com/btwiuse/jiri@v0.0.0-20191125065820-53353bcfef54/cmdline/cmdline_test.go (about)

     1  // Copyright 2015 The Vanadium Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cmdline
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"flag"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"reflect"
    17  	"regexp"
    18  	"strconv"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/btwiuse/jiri/envvar"
    23  )
    24  
    25  var (
    26  	errEchoStr        = "echo error"
    27  	flagExtra         bool
    28  	optNoNewline      bool
    29  	flagTopLevelExtra bool
    30  
    31  	errUsageStr = fmt.Sprint(ErrUsage)
    32  )
    33  
    34  // runEcho is used to implement commands for our tests.
    35  func runEcho(env *Env, args []string) error {
    36  	if len(args) == 1 {
    37  		if args[0] == "error" {
    38  			return errors.New(errEchoStr)
    39  		} else if args[0] == "bad_arg" {
    40  			return env.UsageErrorf("Invalid argument %v", args[0])
    41  		}
    42  	}
    43  	if flagExtra {
    44  		args = append(args, "extra")
    45  	}
    46  	if flagTopLevelExtra {
    47  		args = append(args, "tlextra")
    48  	}
    49  	if optNoNewline {
    50  		fmt.Fprint(env.Stdout, args)
    51  	} else {
    52  		fmt.Fprintln(env.Stdout, args)
    53  	}
    54  	return nil
    55  }
    56  
    57  // runHello is another function for test commands.
    58  func runHello(env *Env, args []string) error {
    59  	if flagTopLevelExtra {
    60  		args = append(args, "tlextra")
    61  	}
    62  	fmt.Fprintln(env.Stdout, strings.Join(append([]string{"Hello"}, args...), " "))
    63  	return nil
    64  }
    65  
    66  func runDumpEnv(env *Env, args []string) error {
    67  	fmt.Fprintln(env.Stdout, envvar.MapToSlice(env.Vars))
    68  	return nil
    69  }
    70  
    71  type testCase struct {
    72  	Args        []string
    73  	Vars        map[string]string
    74  	Err         string
    75  	Stdout      string
    76  	Stderr      string
    77  	GlobalFlag1 string
    78  	GlobalFlag2 int64
    79  }
    80  
    81  func stripTestFlags(got string) string {
    82  	// The global flags include the flags from the testing package, so strip them
    83  	// out before the comparison.
    84  	re := regexp.MustCompile(" -test[^\n]+\n(?:   [^\n]+\n)+")
    85  	return re.ReplaceAllLiteralString(got, "")
    86  }
    87  
    88  func errString(err error) string {
    89  	if err == nil {
    90  		return ""
    91  	}
    92  	return fmt.Sprint(err)
    93  }
    94  
    95  var baseVars = map[string]string{
    96  	"CMDLINE_WIDTH": "80", // make sure formatting stays the same.
    97  }
    98  
    99  func runTestCases(t *testing.T, cmd *Command, tests []testCase) {
   100  	for _, test := range tests {
   101  		// Reset global variables before running each test case.
   102  		var stdout, stderr bytes.Buffer
   103  		flagExtra, flagTopLevelExtra, optNoNewline = false, false, false
   104  
   105  		// Start with a fresh flag.CommandLine for each run.
   106  		flag.CommandLine = flag.NewFlagSet("test", flag.ContinueOnError)
   107  		var globalFlag1 string
   108  		flag.StringVar(&globalFlag1, "global1", "", "global test flag 1")
   109  		globalFlag2 := flag.Int64("global2", 0, "global test flag 2")
   110  		if flag.CommandLine.Parsed() {
   111  			t.Errorf("flag.CommandLine should not be parsed yet")
   112  		}
   113  
   114  		// Parse and run the command and check against expected results.
   115  		parseOK := false
   116  		env := &Env{
   117  			Stdout: &stdout,
   118  			Stderr: &stderr,
   119  			Vars:   envvar.MergeMaps(baseVars, test.Vars),
   120  		}
   121  		runner, args, err := Parse(cmd, env, test.Args)
   122  		if err == nil {
   123  			err = runner.Run(env, args)
   124  			parseOK = true
   125  		}
   126  		if got, want := errString(err), test.Err; got != want {
   127  			t.Errorf("Ran with args %q vars %q\n GOT error:\n%q\nWANT error:\n%q", test.Args, test.Vars, got, want)
   128  		}
   129  		if got, want := stripTestFlags(stdout.String()), test.Stdout; got != want {
   130  			t.Errorf("Ran with args %q vars %q\n GOT stdout:\n%q\nWANT stdout:\n%q", test.Args, test.Vars, got, want)
   131  		}
   132  		if got, want := stripTestFlags(stderr.String()), test.Stderr; got != want {
   133  			t.Errorf("Ran with args %q vars %q\n GOT stderr:\n%q\nWANT stderr:\n%q", test.Args, test.Vars, got, want)
   134  		}
   135  		if got, want := globalFlag1, test.GlobalFlag1; got != want {
   136  			t.Errorf("global1 flag got %q, want %q", got, want)
   137  		}
   138  		if got, want := *globalFlag2, test.GlobalFlag2; got != want {
   139  			t.Errorf("global2 flag got %q, want %q", got, want)
   140  		}
   141  
   142  		if parseOK && !flag.CommandLine.Parsed() {
   143  			t.Errorf("flag.CommandLine should be parsed by now")
   144  		}
   145  	}
   146  }
   147  
   148  func TestEmptyRootName(t *testing.T) {
   149  	noname := &Command{
   150  		Name:   "",
   151  		Short:  "Empty root name",
   152  		Long:   "Empty root name.",
   153  		Runner: RunnerFunc(runHello),
   154  	}
   155  	wantErr := `CODE INVARIANT BROKEN; FIX YOUR CODE
   156  
   157  Root command name cannot be empty.`
   158  	tests := []testCase{
   159  		{Args: []string{}, Err: wantErr},
   160  		{Args: []string{"foo"}, Err: wantErr},
   161  	}
   162  	runTestCases(t, noname, tests)
   163  }
   164  
   165  func TestEmptyTopicName(t *testing.T) {
   166  	topic := Topic{
   167  		Name:  "",
   168  		Short: "noname",
   169  		Long:  "noname",
   170  	}
   171  	parent := &Command{
   172  		Name:   "parent",
   173  		Short:  "parent",
   174  		Long:   "parent",
   175  		Topics: []Topic{topic},
   176  	}
   177  	wantErr := `parent: CODE INVARIANT BROKEN; FIX YOUR CODE
   178  
   179  Command and topic names cannot be empty.`
   180  	tests := []testCase{
   181  		{Args: []string{}, Err: wantErr},
   182  		{Args: []string{"foo"}, Err: wantErr},
   183  	}
   184  	runTestCases(t, parent, tests)
   185  	grandparent := &Command{
   186  		Name:     "grandparent",
   187  		Short:    "grandparent",
   188  		Long:     "grandparent",
   189  		Children: []*Command{parent},
   190  	}
   191  	wantErr = "grandparent " + wantErr
   192  	tests = []testCase{
   193  		{Args: []string{}, Err: wantErr},
   194  		{Args: []string{"foo"}, Err: wantErr},
   195  	}
   196  	runTestCases(t, grandparent, tests)
   197  }
   198  
   199  func TestEmptyChildName(t *testing.T) {
   200  	child := &Command{
   201  		Name:   "",
   202  		Short:  "Empty child name",
   203  		Long:   "Empty child name.",
   204  		Runner: RunnerFunc(runHello),
   205  	}
   206  	parent := &Command{
   207  		Name:     "parent",
   208  		Short:    "parent",
   209  		Long:     "parent",
   210  		Children: []*Command{child},
   211  	}
   212  	wantErr := `parent: CODE INVARIANT BROKEN; FIX YOUR CODE
   213  
   214  Command and topic names cannot be empty.`
   215  	tests := []testCase{
   216  		{Args: []string{}, Err: wantErr},
   217  		{Args: []string{"foo"}, Err: wantErr},
   218  	}
   219  	runTestCases(t, parent, tests)
   220  	grandparent := &Command{
   221  		Name:     "grandparent",
   222  		Short:    "grandparent",
   223  		Long:     "grandparent",
   224  		Children: []*Command{parent},
   225  	}
   226  	wantErr = "grandparent " + wantErr
   227  	tests = []testCase{
   228  		{Args: []string{}, Err: wantErr},
   229  		{Args: []string{"foo"}, Err: wantErr},
   230  	}
   231  	runTestCases(t, grandparent, tests)
   232  }
   233  
   234  func TestDuplicateNames(t *testing.T) {
   235  	child := &Command{
   236  		Name:   "duplicate",
   237  		Short:  "Dup command name",
   238  		Long:   "Dup command name.",
   239  		Runner: RunnerFunc(runHello),
   240  	}
   241  	topic := Topic{
   242  		Name:  "duplicate",
   243  		Short: "Dup topic name",
   244  		Long:  "Dup topic name.",
   245  	}
   246  	parent := &Command{
   247  		Name:     "parent",
   248  		Short:    "parent",
   249  		Long:     "parent",
   250  		Children: []*Command{child},
   251  		Topics:   []Topic{topic},
   252  	}
   253  	wantErr := `parent: CODE INVARIANT BROKEN; FIX YOUR CODE
   254  
   255  Each command must have unique children and topic names.
   256  Saw "duplicate" multiple times.`
   257  	tests := []testCase{
   258  		{Args: []string{}, Err: wantErr},
   259  		{Args: []string{"foo"}, Err: wantErr},
   260  	}
   261  	runTestCases(t, parent, tests)
   262  	grandparent := &Command{
   263  		Name:     "grandparent",
   264  		Short:    "grandparent",
   265  		Long:     "grandparent",
   266  		Children: []*Command{parent},
   267  	}
   268  	wantErr = "grandparent " + wantErr
   269  	tests = []testCase{
   270  		{Args: []string{}, Err: wantErr},
   271  		{Args: []string{"foo"}, Err: wantErr},
   272  	}
   273  	runTestCases(t, grandparent, tests)
   274  }
   275  
   276  func TestNoChildrenOrRunner(t *testing.T) {
   277  	neither := &Command{
   278  		Name:  "neither",
   279  		Short: "Neither is invalid.",
   280  		Long:  "Neither has no commands and no runner.",
   281  	}
   282  	wantErr := `neither: CODE INVARIANT BROKEN; FIX YOUR CODE
   283  
   284  At least one of Children or Runner must be specified.`
   285  	tests := []testCase{
   286  		{Args: []string{}, Err: wantErr},
   287  		{Args: []string{"foo"}, Err: wantErr},
   288  	}
   289  	runTestCases(t, neither, tests)
   290  	parent := &Command{
   291  		Name:     "parent",
   292  		Short:    "parent",
   293  		Long:     "parent",
   294  		Children: []*Command{neither},
   295  	}
   296  	wantErr = "parent " + wantErr
   297  	tests = []testCase{
   298  		{Args: []string{}, Err: wantErr},
   299  		{Args: []string{"foo"}, Err: wantErr},
   300  	}
   301  	runTestCases(t, parent, tests)
   302  }
   303  
   304  func TestBothChildrenAndRunnerWithArgs(t *testing.T) {
   305  	child := &Command{
   306  		Name:   "child",
   307  		Short:  "Child command.",
   308  		Long:   "Child command.",
   309  		Runner: RunnerFunc(runEcho),
   310  	}
   311  	both := &Command{
   312  		Name:     "both",
   313  		Short:    "Both is invalid.",
   314  		Long:     "Both has both commands and a runner with args.",
   315  		ArgsName: "[strings]",
   316  		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
   317  		Children: []*Command{child},
   318  		Runner:   RunnerFunc(runEcho),
   319  	}
   320  	wantErr := `both: CODE INVARIANT BROKEN; FIX YOUR CODE
   321  
   322  Since both Children and Runner are specified, the Runner cannot take args.
   323  Otherwise a conflict between child names and runner args is possible.`
   324  	tests := []testCase{
   325  		{Args: []string{}, Err: wantErr},
   326  		{Args: []string{"foo"}, Err: wantErr},
   327  	}
   328  	runTestCases(t, both, tests)
   329  	parent := &Command{
   330  		Name:     "parent",
   331  		Short:    "parent",
   332  		Long:     "parent",
   333  		Children: []*Command{both},
   334  	}
   335  	wantErr = "parent " + wantErr
   336  	tests = []testCase{
   337  		{Args: []string{}, Err: wantErr},
   338  		{Args: []string{"foo"}, Err: wantErr},
   339  	}
   340  	runTestCases(t, parent, tests)
   341  }
   342  
   343  func TestBothChildrenAndRunnerNoArgs(t *testing.T) {
   344  	cmdEcho := &Command{
   345  		Name:     "echo",
   346  		Short:    "Print strings on stdout",
   347  		Long:     "Echo prints any strings passed in to stdout.",
   348  		Runner:   RunnerFunc(runEcho),
   349  		ArgsName: "[strings]",
   350  		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
   351  	}
   352  	prog := &Command{
   353  		Name:     "cmdrun",
   354  		Short:    "Cmdrun program.",
   355  		Long:     "Cmdrun has the echo command and a Run function with no args.",
   356  		Children: []*Command{cmdEcho},
   357  		Runner:   RunnerFunc(runHello),
   358  	}
   359  	var tests = []testCase{
   360  		{
   361  			Args:   []string{},
   362  			Stdout: "Hello\n",
   363  		},
   364  		{
   365  			Args: []string{"foo"},
   366  			Err:  errUsageStr,
   367  			Stderr: `ERROR: cmdrun: unknown command "foo"
   368  
   369  Cmdrun has the echo command and a Run function with no args.
   370  
   371  Usage:
   372     cmdrun [flags]
   373     cmdrun [flags] <command>
   374  
   375  The cmdrun commands are:
   376     echo        Print strings on stdout
   377     help        Display help for commands or topics
   378  Run "cmdrun help [command]" for command usage.
   379  
   380  The global flags are:
   381   -global1=
   382     global test flag 1
   383   -global2=0
   384     global test flag 2
   385  `,
   386  		},
   387  		{
   388  			Args: []string{"help"},
   389  			Stdout: `Cmdrun has the echo command and a Run function with no args.
   390  
   391  Usage:
   392     cmdrun [flags]
   393     cmdrun [flags] <command>
   394  
   395  The cmdrun commands are:
   396     echo        Print strings on stdout
   397     help        Display help for commands or topics
   398  Run "cmdrun help [command]" for command usage.
   399  
   400  The global flags are:
   401   -global1=
   402     global test flag 1
   403   -global2=0
   404     global test flag 2
   405  `,
   406  		},
   407  		{
   408  			Args: []string{"help", "echo"},
   409  			Stdout: `Echo prints any strings passed in to stdout.
   410  
   411  Usage:
   412     cmdrun echo [flags] [strings]
   413  
   414  [strings] are arbitrary strings that will be echoed.
   415  
   416  The global flags are:
   417   -global1=
   418     global test flag 1
   419   -global2=0
   420     global test flag 2
   421  `,
   422  		},
   423  		{
   424  			Args: []string{"help", "..."},
   425  			Stdout: `Cmdrun has the echo command and a Run function with no args.
   426  
   427  Usage:
   428     cmdrun [flags]
   429     cmdrun [flags] <command>
   430  
   431  The cmdrun commands are:
   432     echo        Print strings on stdout
   433     help        Display help for commands or topics
   434  Run "cmdrun help [command]" for command usage.
   435  
   436  The global flags are:
   437   -global1=
   438     global test flag 1
   439   -global2=0
   440     global test flag 2
   441  ================================================================================
   442  Cmdrun echo - Print strings on stdout
   443  
   444  Echo prints any strings passed in to stdout.
   445  
   446  Usage:
   447     cmdrun echo [flags] [strings]
   448  
   449  [strings] are arbitrary strings that will be echoed.
   450  ================================================================================
   451  Cmdrun help - Display help for commands or topics
   452  
   453  Help with no args displays the usage of the parent command.
   454  
   455  Help with args displays the usage of the specified sub-command or help topic.
   456  
   457  "help ..." recursively displays help for all commands and topics.
   458  
   459  Usage:
   460     cmdrun help [flags] [command/topic ...]
   461  
   462  [command/topic ...] optionally identifies a specific sub-command or help topic.
   463  
   464  The cmdrun help flags are:
   465   -style=compact
   466     The formatting style for help output:
   467        compact   - Good for compact cmdline output.
   468        full      - Good for cmdline output, shows all global flags.
   469        godoc     - Good for godoc processing.
   470        shortonly - Only output short description.
   471     Override the default by setting the CMDLINE_STYLE environment variable.
   472   -width=80
   473     Format output to this target width in runes, or unlimited if width < 0.
   474     Defaults to the terminal width if available.  Override the default by setting
   475     the CMDLINE_WIDTH environment variable.
   476  `,
   477  		},
   478  		{
   479  			Args: []string{"help", "foo"},
   480  			Err:  errUsageStr,
   481  			Stderr: `ERROR: cmdrun: unknown command or topic "foo"
   482  
   483  Cmdrun has the echo command and a Run function with no args.
   484  
   485  Usage:
   486     cmdrun [flags]
   487     cmdrun [flags] <command>
   488  
   489  The cmdrun commands are:
   490     echo        Print strings on stdout
   491     help        Display help for commands or topics
   492  Run "cmdrun help [command]" for command usage.
   493  
   494  The global flags are:
   495   -global1=
   496     global test flag 1
   497   -global2=0
   498     global test flag 2
   499  `,
   500  		},
   501  		{
   502  			Args:   []string{"echo", "foo", "bar"},
   503  			Stdout: "[foo bar]\n",
   504  		},
   505  		{
   506  			Args: []string{"echo", "error"},
   507  			Err:  errEchoStr,
   508  		},
   509  		{
   510  			Args: []string{"echo", "bad_arg"},
   511  			Err:  errUsageStr,
   512  			Stderr: `ERROR: Invalid argument bad_arg
   513  
   514  Echo prints any strings passed in to stdout.
   515  
   516  Usage:
   517     cmdrun echo [flags] [strings]
   518  
   519  [strings] are arbitrary strings that will be echoed.
   520  
   521  The global flags are:
   522   -global1=
   523     global test flag 1
   524   -global2=0
   525     global test flag 2
   526  `,
   527  		},
   528  	}
   529  	runTestCases(t, prog, tests)
   530  }
   531  
   532  func TestOneCommand(t *testing.T) {
   533  	cmdEcho := &Command{
   534  		Name:  "echo",
   535  		Short: "Print strings on stdout",
   536  		Long: `
   537  Echo prints any strings passed in to stdout.
   538  `,
   539  		Runner:   RunnerFunc(runEcho),
   540  		ArgsName: "[strings]",
   541  		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
   542  	}
   543  	prog := &Command{
   544  		Name:     "onecmd",
   545  		Short:    "Onecmd program.",
   546  		Long:     "Onecmd only has the echo command.",
   547  		Children: []*Command{cmdEcho},
   548  	}
   549  
   550  	tests := []testCase{
   551  		{
   552  			Args: []string{},
   553  			Err:  errUsageStr,
   554  			Stderr: `ERROR: onecmd: no command specified
   555  
   556  Onecmd only has the echo command.
   557  
   558  Usage:
   559     onecmd [flags] <command>
   560  
   561  The onecmd commands are:
   562     echo        Print strings on stdout
   563     help        Display help for commands or topics
   564  Run "onecmd help [command]" for command usage.
   565  
   566  The global flags are:
   567   -global1=
   568     global test flag 1
   569   -global2=0
   570     global test flag 2
   571  `,
   572  		},
   573  		{
   574  			Args: []string{"foo"},
   575  			Err:  errUsageStr,
   576  			Stderr: `ERROR: onecmd: unknown command "foo"
   577  
   578  Onecmd only has the echo command.
   579  
   580  Usage:
   581     onecmd [flags] <command>
   582  
   583  The onecmd commands are:
   584     echo        Print strings on stdout
   585     help        Display help for commands or topics
   586  Run "onecmd help [command]" for command usage.
   587  
   588  The global flags are:
   589   -global1=
   590     global test flag 1
   591   -global2=0
   592     global test flag 2
   593  `,
   594  		},
   595  		{
   596  			Args: []string{"help"},
   597  			Stdout: `Onecmd only has the echo command.
   598  
   599  Usage:
   600     onecmd [flags] <command>
   601  
   602  The onecmd commands are:
   603     echo        Print strings on stdout
   604     help        Display help for commands or topics
   605  Run "onecmd help [command]" for command usage.
   606  
   607  The global flags are:
   608   -global1=
   609     global test flag 1
   610   -global2=0
   611     global test flag 2
   612  `,
   613  		},
   614  		{
   615  			Args: []string{"help", "echo"},
   616  			Stdout: `Echo prints any strings passed in to stdout.
   617  
   618  Usage:
   619     onecmd echo [flags] [strings]
   620  
   621  [strings] are arbitrary strings that will be echoed.
   622  
   623  The global flags are:
   624   -global1=
   625     global test flag 1
   626   -global2=0
   627     global test flag 2
   628  `,
   629  		},
   630  		{
   631  			Args: []string{"help", "help"},
   632  			Stdout: `Help with no args displays the usage of the parent command.
   633  
   634  Help with args displays the usage of the specified sub-command or help topic.
   635  
   636  "help ..." recursively displays help for all commands and topics.
   637  
   638  Usage:
   639     onecmd help [flags] [command/topic ...]
   640  
   641  [command/topic ...] optionally identifies a specific sub-command or help topic.
   642  
   643  The onecmd help flags are:
   644   -style=compact
   645     The formatting style for help output:
   646        compact   - Good for compact cmdline output.
   647        full      - Good for cmdline output, shows all global flags.
   648        godoc     - Good for godoc processing.
   649        shortonly - Only output short description.
   650     Override the default by setting the CMDLINE_STYLE environment variable.
   651   -width=80
   652     Format output to this target width in runes, or unlimited if width < 0.
   653     Defaults to the terminal width if available.  Override the default by setting
   654     the CMDLINE_WIDTH environment variable.
   655  
   656  The global flags are:
   657   -global1=
   658     global test flag 1
   659   -global2=0
   660     global test flag 2
   661  `,
   662  		},
   663  		{
   664  			Args: []string{"help", "..."},
   665  			Stdout: `Onecmd only has the echo command.
   666  
   667  Usage:
   668     onecmd [flags] <command>
   669  
   670  The onecmd commands are:
   671     echo        Print strings on stdout
   672     help        Display help for commands or topics
   673  Run "onecmd help [command]" for command usage.
   674  
   675  The global flags are:
   676   -global1=
   677     global test flag 1
   678   -global2=0
   679     global test flag 2
   680  ================================================================================
   681  Onecmd echo - Print strings on stdout
   682  
   683  Echo prints any strings passed in to stdout.
   684  
   685  Usage:
   686     onecmd echo [flags] [strings]
   687  
   688  [strings] are arbitrary strings that will be echoed.
   689  ================================================================================
   690  Onecmd help - Display help for commands or topics
   691  
   692  Help with no args displays the usage of the parent command.
   693  
   694  Help with args displays the usage of the specified sub-command or help topic.
   695  
   696  "help ..." recursively displays help for all commands and topics.
   697  
   698  Usage:
   699     onecmd help [flags] [command/topic ...]
   700  
   701  [command/topic ...] optionally identifies a specific sub-command or help topic.
   702  
   703  The onecmd help flags are:
   704   -style=compact
   705     The formatting style for help output:
   706        compact   - Good for compact cmdline output.
   707        full      - Good for cmdline output, shows all global flags.
   708        godoc     - Good for godoc processing.
   709        shortonly - Only output short description.
   710     Override the default by setting the CMDLINE_STYLE environment variable.
   711   -width=80
   712     Format output to this target width in runes, or unlimited if width < 0.
   713     Defaults to the terminal width if available.  Override the default by setting
   714     the CMDLINE_WIDTH environment variable.
   715  `,
   716  		},
   717  		{
   718  			Args: []string{"help", "foo"},
   719  			Err:  errUsageStr,
   720  			Stderr: `ERROR: onecmd: unknown command or topic "foo"
   721  
   722  Onecmd only has the echo command.
   723  
   724  Usage:
   725     onecmd [flags] <command>
   726  
   727  The onecmd commands are:
   728     echo        Print strings on stdout
   729     help        Display help for commands or topics
   730  Run "onecmd help [command]" for command usage.
   731  
   732  The global flags are:
   733   -global1=
   734     global test flag 1
   735   -global2=0
   736     global test flag 2
   737  `,
   738  		},
   739  		{
   740  			Args:   []string{"echo", "foo", "bar"},
   741  			Stdout: "[foo bar]\n",
   742  		},
   743  		{
   744  			Args: []string{"echo", "error"},
   745  			Err:  errEchoStr,
   746  		},
   747  		{
   748  			Args: []string{"echo", "bad_arg"},
   749  			Err:  errUsageStr,
   750  			Stderr: `ERROR: Invalid argument bad_arg
   751  
   752  Echo prints any strings passed in to stdout.
   753  
   754  Usage:
   755     onecmd echo [flags] [strings]
   756  
   757  [strings] are arbitrary strings that will be echoed.
   758  
   759  The global flags are:
   760   -global1=
   761     global test flag 1
   762   -global2=0
   763     global test flag 2
   764  `,
   765  		},
   766  	}
   767  	runTestCases(t, prog, tests)
   768  }
   769  
   770  func TestMultiCommands(t *testing.T) {
   771  	cmdEcho := &Command{
   772  		Runner: RunnerFunc(runEcho),
   773  		Name:   "echo",
   774  		Short:  "Print strings on stdout",
   775  		Long: `
   776  Echo prints any strings passed in to stdout.
   777  `,
   778  		ArgsName: "[strings]",
   779  		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
   780  	}
   781  	var cmdEchoOpt = &Command{
   782  		Runner: RunnerFunc(runEcho),
   783  		Name:   "echoopt",
   784  		Short:  "Print strings on stdout with opts",
   785  		// Try varying number of header/trailer newlines around the long description.
   786  		Long: `Echoopt prints any args passed in to stdout.
   787  
   788  
   789  `,
   790  		ArgsName: "[args]",
   791  		ArgsLong: "[args] are arbitrary strings that will be echoed.",
   792  	}
   793  	cmdEchoOpt.Flags.BoolVar(&optNoNewline, "n", false, "Do not output trailing newline")
   794  
   795  	prog := &Command{
   796  		Name:     "multi",
   797  		Short:    "Multi test command",
   798  		Long:     "Multi has two variants of echo.",
   799  		Children: []*Command{cmdEcho, cmdEchoOpt},
   800  	}
   801  	prog.Flags.BoolVar(&flagExtra, "extra", false, "Print an extra arg")
   802  
   803  	var tests = []testCase{
   804  		{
   805  			Args: []string{},
   806  			Err:  errUsageStr,
   807  			Stderr: `ERROR: multi: no command specified
   808  
   809  Multi has two variants of echo.
   810  
   811  Usage:
   812     multi [flags] <command>
   813  
   814  The multi commands are:
   815     echo        Print strings on stdout
   816     echoopt     Print strings on stdout with opts
   817     help        Display help for commands or topics
   818  Run "multi help [command]" for command usage.
   819  
   820  The multi flags are:
   821   -extra=false
   822     Print an extra arg
   823  
   824  The global flags are:
   825   -global1=
   826     global test flag 1
   827   -global2=0
   828     global test flag 2
   829  `,
   830  		},
   831  		{
   832  			Args: []string{"help"},
   833  			Stdout: `Multi has two variants of echo.
   834  
   835  Usage:
   836     multi [flags] <command>
   837  
   838  The multi commands are:
   839     echo        Print strings on stdout
   840     echoopt     Print strings on stdout with opts
   841     help        Display help for commands or topics
   842  Run "multi help [command]" for command usage.
   843  
   844  The multi flags are:
   845   -extra=false
   846     Print an extra arg
   847  
   848  The global flags are:
   849   -global1=
   850     global test flag 1
   851   -global2=0
   852     global test flag 2
   853  `,
   854  		},
   855  		{
   856  			Args: []string{"help", "..."},
   857  			Stdout: `Multi has two variants of echo.
   858  
   859  Usage:
   860     multi [flags] <command>
   861  
   862  The multi commands are:
   863     echo        Print strings on stdout
   864     echoopt     Print strings on stdout with opts
   865     help        Display help for commands or topics
   866  Run "multi help [command]" for command usage.
   867  
   868  The multi flags are:
   869   -extra=false
   870     Print an extra arg
   871  
   872  The global flags are:
   873   -global1=
   874     global test flag 1
   875   -global2=0
   876     global test flag 2
   877  ================================================================================
   878  Multi echo - Print strings on stdout
   879  
   880  Echo prints any strings passed in to stdout.
   881  
   882  Usage:
   883     multi echo [flags] [strings]
   884  
   885  [strings] are arbitrary strings that will be echoed.
   886  
   887  Run "multi help -style=full echo" to show all flags.
   888  ================================================================================
   889  Multi echoopt - Print strings on stdout with opts
   890  
   891  Echoopt prints any args passed in to stdout.
   892  
   893  Usage:
   894     multi echoopt [flags] [args]
   895  
   896  [args] are arbitrary strings that will be echoed.
   897  
   898  The multi echoopt flags are:
   899   -n=false
   900     Do not output trailing newline
   901  
   902  Run "multi help -style=full echoopt" to show all flags.
   903  ================================================================================
   904  Multi help - Display help for commands or topics
   905  
   906  Help with no args displays the usage of the parent command.
   907  
   908  Help with args displays the usage of the specified sub-command or help topic.
   909  
   910  "help ..." recursively displays help for all commands and topics.
   911  
   912  Usage:
   913     multi help [flags] [command/topic ...]
   914  
   915  [command/topic ...] optionally identifies a specific sub-command or help topic.
   916  
   917  The multi help flags are:
   918   -style=compact
   919     The formatting style for help output:
   920        compact   - Good for compact cmdline output.
   921        full      - Good for cmdline output, shows all global flags.
   922        godoc     - Good for godoc processing.
   923        shortonly - Only output short description.
   924     Override the default by setting the CMDLINE_STYLE environment variable.
   925   -width=80
   926     Format output to this target width in runes, or unlimited if width < 0.
   927     Defaults to the terminal width if available.  Override the default by setting
   928     the CMDLINE_WIDTH environment variable.
   929  `,
   930  		},
   931  		{
   932  			Args: []string{"help", "echo"},
   933  			Stdout: `Echo prints any strings passed in to stdout.
   934  
   935  Usage:
   936     multi echo [flags] [strings]
   937  
   938  [strings] are arbitrary strings that will be echoed.
   939  
   940  The global flags are:
   941   -global1=
   942     global test flag 1
   943   -global2=0
   944     global test flag 2
   945  
   946  Run "multi help -style=full echo" to show all flags.
   947  `,
   948  		},
   949  		{
   950  			Args: []string{"help", "echoopt"},
   951  			Stdout: `Echoopt prints any args passed in to stdout.
   952  
   953  Usage:
   954     multi echoopt [flags] [args]
   955  
   956  [args] are arbitrary strings that will be echoed.
   957  
   958  The multi echoopt flags are:
   959   -n=false
   960     Do not output trailing newline
   961  
   962  The global flags are:
   963   -global1=
   964     global test flag 1
   965   -global2=0
   966     global test flag 2
   967  
   968  Run "multi help -style=full echoopt" to show all flags.
   969  `,
   970  		},
   971  		{
   972  			Args: []string{"help", "foo"},
   973  			Err:  errUsageStr,
   974  			Stderr: `ERROR: multi: unknown command or topic "foo"
   975  
   976  Multi has two variants of echo.
   977  
   978  Usage:
   979     multi [flags] <command>
   980  
   981  The multi commands are:
   982     echo        Print strings on stdout
   983     echoopt     Print strings on stdout with opts
   984     help        Display help for commands or topics
   985  Run "multi help [command]" for command usage.
   986  
   987  The multi flags are:
   988   -extra=false
   989     Print an extra arg
   990  
   991  The global flags are:
   992   -global1=
   993     global test flag 1
   994   -global2=0
   995     global test flag 2
   996  `,
   997  		},
   998  		{
   999  			Args:   []string{"echo", "foo", "bar"},
  1000  			Stdout: "[foo bar]\n",
  1001  		},
  1002  		{
  1003  			Args:   []string{"-extra", "echo", "foo", "bar"},
  1004  			Stdout: "[foo bar extra]\n",
  1005  		},
  1006  		{
  1007  			Args: []string{"echo", "error"},
  1008  			Err:  errEchoStr,
  1009  		},
  1010  		{
  1011  			Args:   []string{"echoopt", "foo", "bar"},
  1012  			Stdout: "[foo bar]\n",
  1013  		},
  1014  		{
  1015  			Args:   []string{"-extra", "echoopt", "foo", "bar"},
  1016  			Stdout: "[foo bar extra]\n",
  1017  		},
  1018  		{
  1019  			Args:   []string{"echoopt", "-n", "foo", "bar"},
  1020  			Stdout: "[foo bar]",
  1021  		},
  1022  		{
  1023  			Args:   []string{"-extra", "echoopt", "-n", "foo", "bar"},
  1024  			Stdout: "[foo bar extra]",
  1025  		},
  1026  		{
  1027  			Args:        []string{"-global1=globalStringValue", "-extra", "echoopt", "-n", "foo", "bar"},
  1028  			Stdout:      "[foo bar extra]",
  1029  			GlobalFlag1: "globalStringValue",
  1030  		},
  1031  		{
  1032  			Args:        []string{"-global2=42", "echoopt", "-n", "foo", "bar"},
  1033  			Stdout:      "[foo bar]",
  1034  			GlobalFlag2: 42,
  1035  		},
  1036  		{
  1037  			Args:        []string{"-global1=globalStringOtherValue", "-global2=43", "-extra", "echoopt", "-n", "foo", "bar"},
  1038  			Stdout:      "[foo bar extra]",
  1039  			GlobalFlag1: "globalStringOtherValue",
  1040  			GlobalFlag2: 43,
  1041  		},
  1042  		{
  1043  			Args: []string{"echoopt", "error"},
  1044  			Err:  errEchoStr,
  1045  		},
  1046  		{
  1047  			Args: []string{"echo", "-n", "foo", "bar"},
  1048  			Err:  errUsageStr,
  1049  			Stderr: `ERROR: multi echo: flag provided but not defined: -n
  1050  
  1051  Echo prints any strings passed in to stdout.
  1052  
  1053  Usage:
  1054     multi echo [flags] [strings]
  1055  
  1056  [strings] are arbitrary strings that will be echoed.
  1057  
  1058  The global flags are:
  1059   -global1=
  1060     global test flag 1
  1061   -global2=0
  1062     global test flag 2
  1063  
  1064  Run "multi help -style=full echo" to show all flags.
  1065  `,
  1066  		},
  1067  		{
  1068  			Args: []string{"-nosuchflag", "echo", "foo", "bar"},
  1069  			Err:  errUsageStr,
  1070  			Stderr: `ERROR: multi: flag provided but not defined: -nosuchflag
  1071  
  1072  Multi has two variants of echo.
  1073  
  1074  Usage:
  1075     multi [flags] <command>
  1076  
  1077  The multi commands are:
  1078     echo        Print strings on stdout
  1079     echoopt     Print strings on stdout with opts
  1080     help        Display help for commands or topics
  1081  Run "multi help [command]" for command usage.
  1082  
  1083  The multi flags are:
  1084   -extra=false
  1085     Print an extra arg
  1086  
  1087  The global flags are:
  1088   -global1=
  1089     global test flag 1
  1090   -global2=0
  1091     global test flag 2
  1092  `,
  1093  		},
  1094  	}
  1095  	runTestCases(t, prog, tests)
  1096  }
  1097  
  1098  func TestMultiLevelCommands(t *testing.T) {
  1099  	cmdEcho := &Command{
  1100  		Runner: RunnerFunc(runEcho),
  1101  		Name:   "echo",
  1102  		Short:  "Print strings on stdout",
  1103  		Long: `
  1104  Echo prints any strings passed in to stdout.
  1105  `,
  1106  		ArgsName: "[strings]",
  1107  		ArgsLong: "[strings] are arbitrary strings that will be echoed.",
  1108  	}
  1109  	cmdEchoOpt := &Command{
  1110  		Runner: RunnerFunc(runEcho),
  1111  		Name:   "echoopt",
  1112  		Short:  "Print strings on stdout with opts",
  1113  		// Try varying number of header/trailer newlines around the long description.
  1114  		Long: `Echoopt prints any args passed in to stdout.
  1115  
  1116  
  1117  `,
  1118  		ArgsName: "[args]",
  1119  		ArgsLong: "[args] are arbitrary strings that will be echoed.",
  1120  	}
  1121  	cmdEchoOpt.Flags.BoolVar(&optNoNewline, "n", false, "Do not output trailing newline")
  1122  	cmdHello := &Command{
  1123  		Runner: RunnerFunc(runHello),
  1124  		Name:   "hello",
  1125  		Short:  "Print strings on stdout preceded by Hello",
  1126  		Long: `
  1127  Hello prints any strings passed in to stdout preceded by "Hello".
  1128  `,
  1129  		ArgsName: "[strings]",
  1130  		ArgsLong: "[strings] are arbitrary strings that will be printed.",
  1131  	}
  1132  	echoProg := &Command{
  1133  		Name:     "echoprog",
  1134  		Short:    "Set of echo commands",
  1135  		Long:     "Echoprog has two variants of echo.",
  1136  		Children: []*Command{cmdEcho, cmdEchoOpt},
  1137  		Topics: []Topic{
  1138  			{Name: "topic3", Short: "Help topic 3 short", Long: "Help topic 3 long."},
  1139  		},
  1140  	}
  1141  	echoProg.Flags.BoolVar(&flagExtra, "extra", false, "Print an extra arg")
  1142  	prog := &Command{
  1143  		Name:     "toplevelprog",
  1144  		Short:    "Top level prog",
  1145  		Long:     "Toplevelprog has the echo subprogram and the hello command.",
  1146  		Children: []*Command{echoProg, cmdHello},
  1147  		Topics: []Topic{
  1148  			{Name: "topic1", Short: "Help topic 1 short", Long: "Help topic 1 long."},
  1149  			{Name: "topic2", Short: "Help topic 2 short", Long: "Help topic 2 long."},
  1150  		},
  1151  	}
  1152  	prog.Flags.BoolVar(&flagTopLevelExtra, "tlextra", false, "Print an extra arg for all commands")
  1153  
  1154  	var tests = []testCase{
  1155  		{
  1156  			Args: []string{},
  1157  			Err:  errUsageStr,
  1158  			Stderr: `ERROR: toplevelprog: no command specified
  1159  
  1160  Toplevelprog has the echo subprogram and the hello command.
  1161  
  1162  Usage:
  1163     toplevelprog [flags] <command>
  1164  
  1165  The toplevelprog commands are:
  1166     echoprog    Set of echo commands
  1167     hello       Print strings on stdout preceded by Hello
  1168     help        Display help for commands or topics
  1169  Run "toplevelprog help [command]" for command usage.
  1170  
  1171  The toplevelprog additional help topics are:
  1172     topic1      Help topic 1 short
  1173     topic2      Help topic 2 short
  1174  Run "toplevelprog help [topic]" for topic details.
  1175  
  1176  The toplevelprog flags are:
  1177   -tlextra=false
  1178     Print an extra arg for all commands
  1179  
  1180  The global flags are:
  1181   -global1=
  1182     global test flag 1
  1183   -global2=0
  1184     global test flag 2
  1185  `,
  1186  		},
  1187  		{
  1188  			Args: []string{"help"},
  1189  			Stdout: `Toplevelprog has the echo subprogram and the hello command.
  1190  
  1191  Usage:
  1192     toplevelprog [flags] <command>
  1193  
  1194  The toplevelprog commands are:
  1195     echoprog    Set of echo commands
  1196     hello       Print strings on stdout preceded by Hello
  1197     help        Display help for commands or topics
  1198  Run "toplevelprog help [command]" for command usage.
  1199  
  1200  The toplevelprog additional help topics are:
  1201     topic1      Help topic 1 short
  1202     topic2      Help topic 2 short
  1203  Run "toplevelprog help [topic]" for topic details.
  1204  
  1205  The toplevelprog flags are:
  1206   -tlextra=false
  1207     Print an extra arg for all commands
  1208  
  1209  The global flags are:
  1210   -global1=
  1211     global test flag 1
  1212   -global2=0
  1213     global test flag 2
  1214  `,
  1215  		},
  1216  		{
  1217  			Args: []string{"help", "..."},
  1218  			Stdout: `Toplevelprog has the echo subprogram and the hello command.
  1219  
  1220  Usage:
  1221     toplevelprog [flags] <command>
  1222  
  1223  The toplevelprog commands are:
  1224     echoprog    Set of echo commands
  1225     hello       Print strings on stdout preceded by Hello
  1226     help        Display help for commands or topics
  1227  Run "toplevelprog help [command]" for command usage.
  1228  
  1229  The toplevelprog additional help topics are:
  1230     topic1      Help topic 1 short
  1231     topic2      Help topic 2 short
  1232  Run "toplevelprog help [topic]" for topic details.
  1233  
  1234  The toplevelprog flags are:
  1235   -tlextra=false
  1236     Print an extra arg for all commands
  1237  
  1238  The global flags are:
  1239   -global1=
  1240     global test flag 1
  1241   -global2=0
  1242     global test flag 2
  1243  ================================================================================
  1244  Toplevelprog echoprog - Set of echo commands
  1245  
  1246  Echoprog has two variants of echo.
  1247  
  1248  Usage:
  1249     toplevelprog echoprog [flags] <command>
  1250  
  1251  The toplevelprog echoprog commands are:
  1252     echo        Print strings on stdout
  1253     echoopt     Print strings on stdout with opts
  1254  
  1255  The toplevelprog echoprog additional help topics are:
  1256     topic3      Help topic 3 short
  1257  
  1258  The toplevelprog echoprog flags are:
  1259   -extra=false
  1260     Print an extra arg
  1261  
  1262  Run "toplevelprog echoprog help -style=full" to show all flags.
  1263  ================================================================================
  1264  Toplevelprog echoprog echo - Print strings on stdout
  1265  
  1266  Echo prints any strings passed in to stdout.
  1267  
  1268  Usage:
  1269     toplevelprog echoprog echo [flags] [strings]
  1270  
  1271  [strings] are arbitrary strings that will be echoed.
  1272  
  1273  Run "toplevelprog echoprog help -style=full echo" to show all flags.
  1274  ================================================================================
  1275  Toplevelprog echoprog echoopt - Print strings on stdout with opts
  1276  
  1277  Echoopt prints any args passed in to stdout.
  1278  
  1279  Usage:
  1280     toplevelprog echoprog echoopt [flags] [args]
  1281  
  1282  [args] are arbitrary strings that will be echoed.
  1283  
  1284  The toplevelprog echoprog echoopt flags are:
  1285   -n=false
  1286     Do not output trailing newline
  1287  
  1288  Run "toplevelprog echoprog help -style=full echoopt" to show all flags.
  1289  ================================================================================
  1290  Toplevelprog echoprog topic3 - Help topic 3 short
  1291  
  1292  Help topic 3 long.
  1293  ================================================================================
  1294  Toplevelprog hello - Print strings on stdout preceded by Hello
  1295  
  1296  Hello prints any strings passed in to stdout preceded by "Hello".
  1297  
  1298  Usage:
  1299     toplevelprog hello [flags] [strings]
  1300  
  1301  [strings] are arbitrary strings that will be printed.
  1302  
  1303  Run "toplevelprog help -style=full hello" to show all flags.
  1304  ================================================================================
  1305  Toplevelprog help - Display help for commands or topics
  1306  
  1307  Help with no args displays the usage of the parent command.
  1308  
  1309  Help with args displays the usage of the specified sub-command or help topic.
  1310  
  1311  "help ..." recursively displays help for all commands and topics.
  1312  
  1313  Usage:
  1314     toplevelprog help [flags] [command/topic ...]
  1315  
  1316  [command/topic ...] optionally identifies a specific sub-command or help topic.
  1317  
  1318  The toplevelprog help flags are:
  1319   -style=compact
  1320     The formatting style for help output:
  1321        compact   - Good for compact cmdline output.
  1322        full      - Good for cmdline output, shows all global flags.
  1323        godoc     - Good for godoc processing.
  1324        shortonly - Only output short description.
  1325     Override the default by setting the CMDLINE_STYLE environment variable.
  1326   -width=80
  1327     Format output to this target width in runes, or unlimited if width < 0.
  1328     Defaults to the terminal width if available.  Override the default by setting
  1329     the CMDLINE_WIDTH environment variable.
  1330  ================================================================================
  1331  Toplevelprog topic1 - Help topic 1 short
  1332  
  1333  Help topic 1 long.
  1334  ================================================================================
  1335  Toplevelprog topic2 - Help topic 2 short
  1336  
  1337  Help topic 2 long.
  1338  `,
  1339  		},
  1340  		{
  1341  			Args: []string{"help", "echoprog"},
  1342  			Stdout: `Echoprog has two variants of echo.
  1343  
  1344  Usage:
  1345     toplevelprog echoprog [flags] <command>
  1346  
  1347  The toplevelprog echoprog commands are:
  1348     echo        Print strings on stdout
  1349     echoopt     Print strings on stdout with opts
  1350     help        Display help for commands or topics
  1351  Run "toplevelprog echoprog help [command]" for command usage.
  1352  
  1353  The toplevelprog echoprog additional help topics are:
  1354     topic3      Help topic 3 short
  1355  Run "toplevelprog echoprog help [topic]" for topic details.
  1356  
  1357  The toplevelprog echoprog flags are:
  1358   -extra=false
  1359     Print an extra arg
  1360  
  1361  The global flags are:
  1362   -global1=
  1363     global test flag 1
  1364   -global2=0
  1365     global test flag 2
  1366  
  1367  Run "toplevelprog echoprog help -style=full" to show all flags.
  1368  `,
  1369  		},
  1370  		{
  1371  			Args: []string{"help", "topic1"},
  1372  			Stdout: `Help topic 1 long.
  1373  `,
  1374  		},
  1375  		{
  1376  			Args: []string{"help", "topic2"},
  1377  			Stdout: `Help topic 2 long.
  1378  `,
  1379  		},
  1380  		{
  1381  			Args: []string{"echoprog", "help", "..."},
  1382  			Stdout: `Echoprog has two variants of echo.
  1383  
  1384  Usage:
  1385     toplevelprog echoprog [flags] <command>
  1386  
  1387  The toplevelprog echoprog commands are:
  1388     echo        Print strings on stdout
  1389     echoopt     Print strings on stdout with opts
  1390     help        Display help for commands or topics
  1391  Run "toplevelprog echoprog help [command]" for command usage.
  1392  
  1393  The toplevelprog echoprog additional help topics are:
  1394     topic3      Help topic 3 short
  1395  Run "toplevelprog echoprog help [topic]" for topic details.
  1396  
  1397  The toplevelprog echoprog flags are:
  1398   -extra=false
  1399     Print an extra arg
  1400  
  1401  The global flags are:
  1402   -global1=
  1403     global test flag 1
  1404   -global2=0
  1405     global test flag 2
  1406  
  1407  Run "toplevelprog echoprog help -style=full" to show all flags.
  1408  ================================================================================
  1409  Toplevelprog echoprog echo - Print strings on stdout
  1410  
  1411  Echo prints any strings passed in to stdout.
  1412  
  1413  Usage:
  1414     toplevelprog echoprog echo [flags] [strings]
  1415  
  1416  [strings] are arbitrary strings that will be echoed.
  1417  
  1418  Run "toplevelprog echoprog help -style=full echo" to show all flags.
  1419  ================================================================================
  1420  Toplevelprog echoprog echoopt - Print strings on stdout with opts
  1421  
  1422  Echoopt prints any args passed in to stdout.
  1423  
  1424  Usage:
  1425     toplevelprog echoprog echoopt [flags] [args]
  1426  
  1427  [args] are arbitrary strings that will be echoed.
  1428  
  1429  The toplevelprog echoprog echoopt flags are:
  1430   -n=false
  1431     Do not output trailing newline
  1432  
  1433  Run "toplevelprog echoprog help -style=full echoopt" to show all flags.
  1434  ================================================================================
  1435  Toplevelprog echoprog help - Display help for commands or topics
  1436  
  1437  Help with no args displays the usage of the parent command.
  1438  
  1439  Help with args displays the usage of the specified sub-command or help topic.
  1440  
  1441  "help ..." recursively displays help for all commands and topics.
  1442  
  1443  Usage:
  1444     toplevelprog echoprog help [flags] [command/topic ...]
  1445  
  1446  [command/topic ...] optionally identifies a specific sub-command or help topic.
  1447  
  1448  The toplevelprog echoprog help flags are:
  1449   -style=compact
  1450     The formatting style for help output:
  1451        compact   - Good for compact cmdline output.
  1452        full      - Good for cmdline output, shows all global flags.
  1453        godoc     - Good for godoc processing.
  1454        shortonly - Only output short description.
  1455     Override the default by setting the CMDLINE_STYLE environment variable.
  1456   -width=80
  1457     Format output to this target width in runes, or unlimited if width < 0.
  1458     Defaults to the terminal width if available.  Override the default by setting
  1459     the CMDLINE_WIDTH environment variable.
  1460  ================================================================================
  1461  Toplevelprog echoprog topic3 - Help topic 3 short
  1462  
  1463  Help topic 3 long.
  1464  `,
  1465  		},
  1466  		{
  1467  			Args: []string{"echoprog", "help", "echoopt"},
  1468  			Stdout: `Echoopt prints any args passed in to stdout.
  1469  
  1470  Usage:
  1471     toplevelprog echoprog echoopt [flags] [args]
  1472  
  1473  [args] are arbitrary strings that will be echoed.
  1474  
  1475  The toplevelprog echoprog echoopt flags are:
  1476   -n=false
  1477     Do not output trailing newline
  1478  
  1479  The global flags are:
  1480   -global1=
  1481     global test flag 1
  1482   -global2=0
  1483     global test flag 2
  1484  
  1485  Run "toplevelprog echoprog help -style=full echoopt" to show all flags.
  1486  `,
  1487  		},
  1488  		{
  1489  			Args: []string{"help", "echoprog", "topic3"},
  1490  			Stdout: `Help topic 3 long.
  1491  `,
  1492  		},
  1493  		{
  1494  			Args: []string{"echoprog", "help", "topic3"},
  1495  			Stdout: `Help topic 3 long.
  1496  `,
  1497  		},
  1498  		{
  1499  			Args: []string{"help", "hello"},
  1500  			Stdout: `Hello prints any strings passed in to stdout preceded by "Hello".
  1501  
  1502  Usage:
  1503     toplevelprog hello [flags] [strings]
  1504  
  1505  [strings] are arbitrary strings that will be printed.
  1506  
  1507  The global flags are:
  1508   -global1=
  1509     global test flag 1
  1510   -global2=0
  1511     global test flag 2
  1512  
  1513  Run "toplevelprog help -style=full hello" to show all flags.
  1514  `,
  1515  		},
  1516  		{
  1517  			Args: []string{"help", "foo"},
  1518  			Err:  errUsageStr,
  1519  			Stderr: `ERROR: toplevelprog: unknown command or topic "foo"
  1520  
  1521  Toplevelprog has the echo subprogram and the hello command.
  1522  
  1523  Usage:
  1524     toplevelprog [flags] <command>
  1525  
  1526  The toplevelprog commands are:
  1527     echoprog    Set of echo commands
  1528     hello       Print strings on stdout preceded by Hello
  1529     help        Display help for commands or topics
  1530  Run "toplevelprog help [command]" for command usage.
  1531  
  1532  The toplevelprog additional help topics are:
  1533     topic1      Help topic 1 short
  1534     topic2      Help topic 2 short
  1535  Run "toplevelprog help [topic]" for topic details.
  1536  
  1537  The toplevelprog flags are:
  1538   -tlextra=false
  1539     Print an extra arg for all commands
  1540  
  1541  The global flags are:
  1542   -global1=
  1543     global test flag 1
  1544   -global2=0
  1545     global test flag 2
  1546  `,
  1547  		},
  1548  		{
  1549  			Args:   []string{"echoprog", "echo", "foo", "bar"},
  1550  			Stdout: "[foo bar]\n",
  1551  		},
  1552  		{
  1553  			Args:   []string{"echoprog", "-extra", "echo", "foo", "bar"},
  1554  			Stdout: "[foo bar extra]\n",
  1555  		},
  1556  		{
  1557  			Args: []string{"echoprog", "echo", "error"},
  1558  			Err:  errEchoStr,
  1559  		},
  1560  		{
  1561  			Args:   []string{"echoprog", "echoopt", "foo", "bar"},
  1562  			Stdout: "[foo bar]\n",
  1563  		},
  1564  		{
  1565  			Args:   []string{"echoprog", "-extra", "echoopt", "foo", "bar"},
  1566  			Stdout: "[foo bar extra]\n",
  1567  		},
  1568  		{
  1569  			Args:   []string{"echoprog", "echoopt", "-n", "foo", "bar"},
  1570  			Stdout: "[foo bar]",
  1571  		},
  1572  		{
  1573  			Args:   []string{"echoprog", "-extra", "echoopt", "-n", "foo", "bar"},
  1574  			Stdout: "[foo bar extra]",
  1575  		},
  1576  		{
  1577  			Args: []string{"echoprog", "echoopt", "error"},
  1578  			Err:  errEchoStr,
  1579  		},
  1580  		{
  1581  			Args:   []string{"--tlextra", "echoprog", "-extra", "echoopt", "foo", "bar"},
  1582  			Stdout: "[foo bar extra tlextra]\n",
  1583  		},
  1584  		{
  1585  			Args:   []string{"hello", "foo", "bar"},
  1586  			Stdout: "Hello foo bar\n",
  1587  		},
  1588  		{
  1589  			Args:   []string{"--tlextra", "hello", "foo", "bar"},
  1590  			Stdout: "Hello foo bar tlextra\n",
  1591  		},
  1592  		{
  1593  			Args: []string{"hello", "--extra", "foo", "bar"},
  1594  			Err:  errUsageStr,
  1595  			Stderr: `ERROR: toplevelprog hello: flag provided but not defined: -extra
  1596  
  1597  Hello prints any strings passed in to stdout preceded by "Hello".
  1598  
  1599  Usage:
  1600     toplevelprog hello [flags] [strings]
  1601  
  1602  [strings] are arbitrary strings that will be printed.
  1603  
  1604  The global flags are:
  1605   -global1=
  1606     global test flag 1
  1607   -global2=0
  1608     global test flag 2
  1609  
  1610  Run "toplevelprog help -style=full hello" to show all flags.
  1611  `,
  1612  		},
  1613  		{
  1614  			Args: []string{"-extra", "echoprog", "echoopt", "foo", "bar"},
  1615  			Err:  errUsageStr,
  1616  			Stderr: `ERROR: toplevelprog: flag provided but not defined: -extra
  1617  
  1618  Toplevelprog has the echo subprogram and the hello command.
  1619  
  1620  Usage:
  1621     toplevelprog [flags] <command>
  1622  
  1623  The toplevelprog commands are:
  1624     echoprog    Set of echo commands
  1625     hello       Print strings on stdout preceded by Hello
  1626     help        Display help for commands or topics
  1627  Run "toplevelprog help [command]" for command usage.
  1628  
  1629  The toplevelprog additional help topics are:
  1630     topic1      Help topic 1 short
  1631     topic2      Help topic 2 short
  1632  Run "toplevelprog help [topic]" for topic details.
  1633  
  1634  The toplevelprog flags are:
  1635   -tlextra=false
  1636     Print an extra arg for all commands
  1637  
  1638  The global flags are:
  1639   -global1=
  1640     global test flag 1
  1641   -global2=0
  1642     global test flag 2
  1643  `,
  1644  		},
  1645  	}
  1646  	runTestCases(t, prog, tests)
  1647  }
  1648  
  1649  func TestMultiLevelCommandsOrdering(t *testing.T) {
  1650  	cmdHello11 := &Command{
  1651  		Name:  "hello11",
  1652  		Short: "Print strings on stdout preceded by Hello",
  1653  		Long: `
  1654  Hello prints any strings passed in to stdout preceded by "Hello".
  1655  `,
  1656  		ArgsName: "[strings]",
  1657  		ArgsLong: "[strings] are arbitrary strings that will be printed.",
  1658  		Runner:   RunnerFunc(runHello),
  1659  	}
  1660  	cmdHello12 := &Command{
  1661  		Name:  "hello12",
  1662  		Short: "Print strings on stdout preceded by Hello",
  1663  		Long: `
  1664  Hello prints any strings passed in to stdout preceded by "Hello".
  1665  `,
  1666  		ArgsName: "[strings]",
  1667  		ArgsLong: "[strings] are arbitrary strings that will be printed.",
  1668  		Runner:   RunnerFunc(runHello),
  1669  	}
  1670  	cmdHello21 := &Command{
  1671  		Name:  "hello21",
  1672  		Short: "Print strings on stdout preceded by Hello",
  1673  		Long: `
  1674  Hello prints any strings passed in to stdout preceded by "Hello".
  1675  `,
  1676  		ArgsName: "[strings]",
  1677  		ArgsLong: "[strings] are arbitrary strings that will be printed.",
  1678  		Runner:   RunnerFunc(runHello),
  1679  	}
  1680  	cmdHello22 := &Command{
  1681  		Name:  "hello22",
  1682  		Short: "Print strings on stdout preceded by Hello",
  1683  		Long: `
  1684  Hello prints any strings passed in to stdout preceded by "Hello".
  1685  `,
  1686  		ArgsName: "[strings]",
  1687  		ArgsLong: "[strings] are arbitrary strings that will be printed.",
  1688  		Runner:   RunnerFunc(runHello),
  1689  	}
  1690  	cmdHello31 := &Command{
  1691  		Name:  "hello31",
  1692  		Short: "Print strings on stdout preceded by Hello",
  1693  		Long: `
  1694  Hello prints any strings passed in to stdout preceded by "Hello".
  1695  `,
  1696  		ArgsName: "[strings]",
  1697  		ArgsLong: "[strings] are arbitrary strings that will be printed.",
  1698  		Runner:   RunnerFunc(runHello),
  1699  	}
  1700  	cmdHello32 := &Command{
  1701  		Name:  "hello32",
  1702  		Short: "Print strings on stdout preceded by Hello",
  1703  		Long: `
  1704  Hello prints any strings passed in to stdout preceded by "Hello".
  1705  `,
  1706  		ArgsName: "[strings]",
  1707  		ArgsLong: "[strings] are arbitrary strings that will be printed.",
  1708  		Runner:   RunnerFunc(runHello),
  1709  	}
  1710  	progHello3 := &Command{
  1711  		Name:     "prog3",
  1712  		Short:    "Set of hello commands",
  1713  		Long:     "Prog3 has two variants of hello.",
  1714  		Children: []*Command{cmdHello31, cmdHello32},
  1715  	}
  1716  	progHello2 := &Command{
  1717  		Name:     "prog2",
  1718  		Short:    "Set of hello commands",
  1719  		Long:     "Prog2 has two variants of hello and a subprogram prog3.",
  1720  		Children: []*Command{cmdHello21, progHello3, cmdHello22},
  1721  	}
  1722  	progHello1 := &Command{
  1723  		Name:     "prog1",
  1724  		Short:    "Set of hello commands",
  1725  		Long:     "Prog1 has two variants of hello and a subprogram prog2.",
  1726  		Children: []*Command{cmdHello11, cmdHello12, progHello2},
  1727  	}
  1728  
  1729  	var tests = []testCase{
  1730  		{
  1731  			Args: []string{},
  1732  			Err:  errUsageStr,
  1733  			Stderr: `ERROR: prog1: no command specified
  1734  
  1735  Prog1 has two variants of hello and a subprogram prog2.
  1736  
  1737  Usage:
  1738     prog1 [flags] <command>
  1739  
  1740  The prog1 commands are:
  1741     hello11     Print strings on stdout preceded by Hello
  1742     hello12     Print strings on stdout preceded by Hello
  1743     prog2       Set of hello commands
  1744     help        Display help for commands or topics
  1745  Run "prog1 help [command]" for command usage.
  1746  
  1747  The global flags are:
  1748   -global1=
  1749     global test flag 1
  1750   -global2=0
  1751     global test flag 2
  1752  `,
  1753  		},
  1754  		{
  1755  			Args: []string{"help"},
  1756  			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
  1757  
  1758  Usage:
  1759     prog1 [flags] <command>
  1760  
  1761  The prog1 commands are:
  1762     hello11     Print strings on stdout preceded by Hello
  1763     hello12     Print strings on stdout preceded by Hello
  1764     prog2       Set of hello commands
  1765     help        Display help for commands or topics
  1766  Run "prog1 help [command]" for command usage.
  1767  
  1768  The global flags are:
  1769   -global1=
  1770     global test flag 1
  1771   -global2=0
  1772     global test flag 2
  1773  `,
  1774  		},
  1775  		{
  1776  			Args: []string{"help", "..."},
  1777  			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
  1778  
  1779  Usage:
  1780     prog1 [flags] <command>
  1781  
  1782  The prog1 commands are:
  1783     hello11     Print strings on stdout preceded by Hello
  1784     hello12     Print strings on stdout preceded by Hello
  1785     prog2       Set of hello commands
  1786     help        Display help for commands or topics
  1787  Run "prog1 help [command]" for command usage.
  1788  
  1789  The global flags are:
  1790   -global1=
  1791     global test flag 1
  1792   -global2=0
  1793     global test flag 2
  1794  ================================================================================
  1795  Prog1 hello11 - Print strings on stdout preceded by Hello
  1796  
  1797  Hello prints any strings passed in to stdout preceded by "Hello".
  1798  
  1799  Usage:
  1800     prog1 hello11 [flags] [strings]
  1801  
  1802  [strings] are arbitrary strings that will be printed.
  1803  ================================================================================
  1804  Prog1 hello12 - Print strings on stdout preceded by Hello
  1805  
  1806  Hello prints any strings passed in to stdout preceded by "Hello".
  1807  
  1808  Usage:
  1809     prog1 hello12 [flags] [strings]
  1810  
  1811  [strings] are arbitrary strings that will be printed.
  1812  ================================================================================
  1813  Prog1 prog2 - Set of hello commands
  1814  
  1815  Prog2 has two variants of hello and a subprogram prog3.
  1816  
  1817  Usage:
  1818     prog1 prog2 [flags] <command>
  1819  
  1820  The prog1 prog2 commands are:
  1821     hello21     Print strings on stdout preceded by Hello
  1822     prog3       Set of hello commands
  1823     hello22     Print strings on stdout preceded by Hello
  1824  ================================================================================
  1825  Prog1 prog2 hello21 - Print strings on stdout preceded by Hello
  1826  
  1827  Hello prints any strings passed in to stdout preceded by "Hello".
  1828  
  1829  Usage:
  1830     prog1 prog2 hello21 [flags] [strings]
  1831  
  1832  [strings] are arbitrary strings that will be printed.
  1833  ================================================================================
  1834  Prog1 prog2 prog3 - Set of hello commands
  1835  
  1836  Prog3 has two variants of hello.
  1837  
  1838  Usage:
  1839     prog1 prog2 prog3 [flags] <command>
  1840  
  1841  The prog1 prog2 prog3 commands are:
  1842     hello31     Print strings on stdout preceded by Hello
  1843     hello32     Print strings on stdout preceded by Hello
  1844  ================================================================================
  1845  Prog1 prog2 prog3 hello31 - Print strings on stdout preceded by Hello
  1846  
  1847  Hello prints any strings passed in to stdout preceded by "Hello".
  1848  
  1849  Usage:
  1850     prog1 prog2 prog3 hello31 [flags] [strings]
  1851  
  1852  [strings] are arbitrary strings that will be printed.
  1853  ================================================================================
  1854  Prog1 prog2 prog3 hello32 - Print strings on stdout preceded by Hello
  1855  
  1856  Hello prints any strings passed in to stdout preceded by "Hello".
  1857  
  1858  Usage:
  1859     prog1 prog2 prog3 hello32 [flags] [strings]
  1860  
  1861  [strings] are arbitrary strings that will be printed.
  1862  ================================================================================
  1863  Prog1 prog2 hello22 - Print strings on stdout preceded by Hello
  1864  
  1865  Hello prints any strings passed in to stdout preceded by "Hello".
  1866  
  1867  Usage:
  1868     prog1 prog2 hello22 [flags] [strings]
  1869  
  1870  [strings] are arbitrary strings that will be printed.
  1871  ================================================================================
  1872  Prog1 help - Display help for commands or topics
  1873  
  1874  Help with no args displays the usage of the parent command.
  1875  
  1876  Help with args displays the usage of the specified sub-command or help topic.
  1877  
  1878  "help ..." recursively displays help for all commands and topics.
  1879  
  1880  Usage:
  1881     prog1 help [flags] [command/topic ...]
  1882  
  1883  [command/topic ...] optionally identifies a specific sub-command or help topic.
  1884  
  1885  The prog1 help flags are:
  1886   -style=compact
  1887     The formatting style for help output:
  1888        compact   - Good for compact cmdline output.
  1889        full      - Good for cmdline output, shows all global flags.
  1890        godoc     - Good for godoc processing.
  1891        shortonly - Only output short description.
  1892     Override the default by setting the CMDLINE_STYLE environment variable.
  1893   -width=80
  1894     Format output to this target width in runes, or unlimited if width < 0.
  1895     Defaults to the terminal width if available.  Override the default by setting
  1896     the CMDLINE_WIDTH environment variable.
  1897  `,
  1898  		},
  1899  		{
  1900  			Args: []string{"prog2", "help", "..."},
  1901  			Stdout: `Prog2 has two variants of hello and a subprogram prog3.
  1902  
  1903  Usage:
  1904     prog1 prog2 [flags] <command>
  1905  
  1906  The prog1 prog2 commands are:
  1907     hello21     Print strings on stdout preceded by Hello
  1908     prog3       Set of hello commands
  1909     hello22     Print strings on stdout preceded by Hello
  1910     help        Display help for commands or topics
  1911  Run "prog1 prog2 help [command]" for command usage.
  1912  
  1913  The global flags are:
  1914   -global1=
  1915     global test flag 1
  1916   -global2=0
  1917     global test flag 2
  1918  ================================================================================
  1919  Prog1 prog2 hello21 - Print strings on stdout preceded by Hello
  1920  
  1921  Hello prints any strings passed in to stdout preceded by "Hello".
  1922  
  1923  Usage:
  1924     prog1 prog2 hello21 [flags] [strings]
  1925  
  1926  [strings] are arbitrary strings that will be printed.
  1927  ================================================================================
  1928  Prog1 prog2 prog3 - Set of hello commands
  1929  
  1930  Prog3 has two variants of hello.
  1931  
  1932  Usage:
  1933     prog1 prog2 prog3 [flags] <command>
  1934  
  1935  The prog1 prog2 prog3 commands are:
  1936     hello31     Print strings on stdout preceded by Hello
  1937     hello32     Print strings on stdout preceded by Hello
  1938  ================================================================================
  1939  Prog1 prog2 prog3 hello31 - Print strings on stdout preceded by Hello
  1940  
  1941  Hello prints any strings passed in to stdout preceded by "Hello".
  1942  
  1943  Usage:
  1944     prog1 prog2 prog3 hello31 [flags] [strings]
  1945  
  1946  [strings] are arbitrary strings that will be printed.
  1947  ================================================================================
  1948  Prog1 prog2 prog3 hello32 - Print strings on stdout preceded by Hello
  1949  
  1950  Hello prints any strings passed in to stdout preceded by "Hello".
  1951  
  1952  Usage:
  1953     prog1 prog2 prog3 hello32 [flags] [strings]
  1954  
  1955  [strings] are arbitrary strings that will be printed.
  1956  ================================================================================
  1957  Prog1 prog2 hello22 - Print strings on stdout preceded by Hello
  1958  
  1959  Hello prints any strings passed in to stdout preceded by "Hello".
  1960  
  1961  Usage:
  1962     prog1 prog2 hello22 [flags] [strings]
  1963  
  1964  [strings] are arbitrary strings that will be printed.
  1965  ================================================================================
  1966  Prog1 prog2 help - Display help for commands or topics
  1967  
  1968  Help with no args displays the usage of the parent command.
  1969  
  1970  Help with args displays the usage of the specified sub-command or help topic.
  1971  
  1972  "help ..." recursively displays help for all commands and topics.
  1973  
  1974  Usage:
  1975     prog1 prog2 help [flags] [command/topic ...]
  1976  
  1977  [command/topic ...] optionally identifies a specific sub-command or help topic.
  1978  
  1979  The prog1 prog2 help flags are:
  1980   -style=compact
  1981     The formatting style for help output:
  1982        compact   - Good for compact cmdline output.
  1983        full      - Good for cmdline output, shows all global flags.
  1984        godoc     - Good for godoc processing.
  1985        shortonly - Only output short description.
  1986     Override the default by setting the CMDLINE_STYLE environment variable.
  1987   -width=80
  1988     Format output to this target width in runes, or unlimited if width < 0.
  1989     Defaults to the terminal width if available.  Override the default by setting
  1990     the CMDLINE_WIDTH environment variable.
  1991  `,
  1992  		},
  1993  		{
  1994  			Args: []string{"prog2", "prog3", "help", "..."},
  1995  			Stdout: `Prog3 has two variants of hello.
  1996  
  1997  Usage:
  1998     prog1 prog2 prog3 [flags] <command>
  1999  
  2000  The prog1 prog2 prog3 commands are:
  2001     hello31     Print strings on stdout preceded by Hello
  2002     hello32     Print strings on stdout preceded by Hello
  2003     help        Display help for commands or topics
  2004  Run "prog1 prog2 prog3 help [command]" for command usage.
  2005  
  2006  The global flags are:
  2007   -global1=
  2008     global test flag 1
  2009   -global2=0
  2010     global test flag 2
  2011  ================================================================================
  2012  Prog1 prog2 prog3 hello31 - Print strings on stdout preceded by Hello
  2013  
  2014  Hello prints any strings passed in to stdout preceded by "Hello".
  2015  
  2016  Usage:
  2017     prog1 prog2 prog3 hello31 [flags] [strings]
  2018  
  2019  [strings] are arbitrary strings that will be printed.
  2020  ================================================================================
  2021  Prog1 prog2 prog3 hello32 - Print strings on stdout preceded by Hello
  2022  
  2023  Hello prints any strings passed in to stdout preceded by "Hello".
  2024  
  2025  Usage:
  2026     prog1 prog2 prog3 hello32 [flags] [strings]
  2027  
  2028  [strings] are arbitrary strings that will be printed.
  2029  ================================================================================
  2030  Prog1 prog2 prog3 help - Display help for commands or topics
  2031  
  2032  Help with no args displays the usage of the parent command.
  2033  
  2034  Help with args displays the usage of the specified sub-command or help topic.
  2035  
  2036  "help ..." recursively displays help for all commands and topics.
  2037  
  2038  Usage:
  2039     prog1 prog2 prog3 help [flags] [command/topic ...]
  2040  
  2041  [command/topic ...] optionally identifies a specific sub-command or help topic.
  2042  
  2043  The prog1 prog2 prog3 help flags are:
  2044   -style=compact
  2045     The formatting style for help output:
  2046        compact   - Good for compact cmdline output.
  2047        full      - Good for cmdline output, shows all global flags.
  2048        godoc     - Good for godoc processing.
  2049        shortonly - Only output short description.
  2050     Override the default by setting the CMDLINE_STYLE environment variable.
  2051   -width=80
  2052     Format output to this target width in runes, or unlimited if width < 0.
  2053     Defaults to the terminal width if available.  Override the default by setting
  2054     the CMDLINE_WIDTH environment variable.
  2055  `,
  2056  		},
  2057  		{
  2058  			Args: []string{"help", "prog2", "prog3", "..."},
  2059  			Stdout: `Prog3 has two variants of hello.
  2060  
  2061  Usage:
  2062     prog1 prog2 prog3 [flags] <command>
  2063  
  2064  The prog1 prog2 prog3 commands are:
  2065     hello31     Print strings on stdout preceded by Hello
  2066     hello32     Print strings on stdout preceded by Hello
  2067     help        Display help for commands or topics
  2068  Run "prog1 prog2 prog3 help [command]" for command usage.
  2069  
  2070  The global flags are:
  2071   -global1=
  2072     global test flag 1
  2073   -global2=0
  2074     global test flag 2
  2075  ================================================================================
  2076  Prog1 prog2 prog3 hello31 - Print strings on stdout preceded by Hello
  2077  
  2078  Hello prints any strings passed in to stdout preceded by "Hello".
  2079  
  2080  Usage:
  2081     prog1 prog2 prog3 hello31 [flags] [strings]
  2082  
  2083  [strings] are arbitrary strings that will be printed.
  2084  ================================================================================
  2085  Prog1 prog2 prog3 hello32 - Print strings on stdout preceded by Hello
  2086  
  2087  Hello prints any strings passed in to stdout preceded by "Hello".
  2088  
  2089  Usage:
  2090     prog1 prog2 prog3 hello32 [flags] [strings]
  2091  
  2092  [strings] are arbitrary strings that will be printed.
  2093  ================================================================================
  2094  Prog1 prog2 prog3 help - Display help for commands or topics
  2095  
  2096  Help with no args displays the usage of the parent command.
  2097  
  2098  Help with args displays the usage of the specified sub-command or help topic.
  2099  
  2100  "help ..." recursively displays help for all commands and topics.
  2101  
  2102  Usage:
  2103     prog1 prog2 prog3 help [flags] [command/topic ...]
  2104  
  2105  [command/topic ...] optionally identifies a specific sub-command or help topic.
  2106  
  2107  The prog1 prog2 prog3 help flags are:
  2108   -style=compact
  2109     The formatting style for help output:
  2110        compact   - Good for compact cmdline output.
  2111        full      - Good for cmdline output, shows all global flags.
  2112        godoc     - Good for godoc processing.
  2113        shortonly - Only output short description.
  2114     Override the default by setting the CMDLINE_STYLE environment variable.
  2115   -width=80
  2116     Format output to this target width in runes, or unlimited if width < 0.
  2117     Defaults to the terminal width if available.  Override the default by setting
  2118     the CMDLINE_WIDTH environment variable.
  2119  `,
  2120  		},
  2121  		{
  2122  			Args: []string{"help", "-style=godoc", "..."},
  2123  			Stdout: `Prog1 has two variants of hello and a subprogram prog2.
  2124  
  2125  Usage:
  2126     prog1 [flags] <command>
  2127  
  2128  The prog1 commands are:
  2129     hello11     Print strings on stdout preceded by Hello
  2130     hello12     Print strings on stdout preceded by Hello
  2131     prog2       Set of hello commands
  2132     help        Display help for commands or topics
  2133  
  2134  The global flags are:
  2135   -global1=
  2136     global test flag 1
  2137   -global2=0
  2138     global test flag 2
  2139  
  2140  Prog1 hello11 - Print strings on stdout preceded by Hello
  2141  
  2142  Hello prints any strings passed in to stdout preceded by "Hello".
  2143  
  2144  Usage:
  2145     prog1 hello11 [flags] [strings]
  2146  
  2147  [strings] are arbitrary strings that will be printed.
  2148  
  2149  Prog1 hello12 - Print strings on stdout preceded by Hello
  2150  
  2151  Hello prints any strings passed in to stdout preceded by "Hello".
  2152  
  2153  Usage:
  2154     prog1 hello12 [flags] [strings]
  2155  
  2156  [strings] are arbitrary strings that will be printed.
  2157  
  2158  Prog1 prog2 - Set of hello commands
  2159  
  2160  Prog2 has two variants of hello and a subprogram prog3.
  2161  
  2162  Usage:
  2163     prog1 prog2 [flags] <command>
  2164  
  2165  The prog1 prog2 commands are:
  2166     hello21     Print strings on stdout preceded by Hello
  2167     prog3       Set of hello commands
  2168     hello22     Print strings on stdout preceded by Hello
  2169  
  2170  Prog1 prog2 hello21 - Print strings on stdout preceded by Hello
  2171  
  2172  Hello prints any strings passed in to stdout preceded by "Hello".
  2173  
  2174  Usage:
  2175     prog1 prog2 hello21 [flags] [strings]
  2176  
  2177  [strings] are arbitrary strings that will be printed.
  2178  
  2179  Prog1 prog2 prog3 - Set of hello commands
  2180  
  2181  Prog3 has two variants of hello.
  2182  
  2183  Usage:
  2184     prog1 prog2 prog3 [flags] <command>
  2185  
  2186  The prog1 prog2 prog3 commands are:
  2187     hello31     Print strings on stdout preceded by Hello
  2188     hello32     Print strings on stdout preceded by Hello
  2189  
  2190  Prog1 prog2 prog3 hello31 - Print strings on stdout preceded by Hello
  2191  
  2192  Hello prints any strings passed in to stdout preceded by "Hello".
  2193  
  2194  Usage:
  2195     prog1 prog2 prog3 hello31 [flags] [strings]
  2196  
  2197  [strings] are arbitrary strings that will be printed.
  2198  
  2199  Prog1 prog2 prog3 hello32 - Print strings on stdout preceded by Hello
  2200  
  2201  Hello prints any strings passed in to stdout preceded by "Hello".
  2202  
  2203  Usage:
  2204     prog1 prog2 prog3 hello32 [flags] [strings]
  2205  
  2206  [strings] are arbitrary strings that will be printed.
  2207  
  2208  Prog1 prog2 hello22 - Print strings on stdout preceded by Hello
  2209  
  2210  Hello prints any strings passed in to stdout preceded by "Hello".
  2211  
  2212  Usage:
  2213     prog1 prog2 hello22 [flags] [strings]
  2214  
  2215  [strings] are arbitrary strings that will be printed.
  2216  
  2217  Prog1 help - Display help for commands or topics
  2218  
  2219  Help with no args displays the usage of the parent command.
  2220  
  2221  Help with args displays the usage of the specified sub-command or help topic.
  2222  
  2223  "help ..." recursively displays help for all commands and topics.
  2224  
  2225  Usage:
  2226     prog1 help [flags] [command/topic ...]
  2227  
  2228  [command/topic ...] optionally identifies a specific sub-command or help topic.
  2229  
  2230  The prog1 help flags are:
  2231   -style=compact
  2232     The formatting style for help output:
  2233        compact   - Good for compact cmdline output.
  2234        full      - Good for cmdline output, shows all global flags.
  2235        godoc     - Good for godoc processing.
  2236        shortonly - Only output short description.
  2237     Override the default by setting the CMDLINE_STYLE environment variable.
  2238   -width=<terminal width>
  2239     Format output to this target width in runes, or unlimited if width < 0.
  2240     Defaults to the terminal width if available.  Override the default by setting
  2241     the CMDLINE_WIDTH environment variable.
  2242  `,
  2243  		},
  2244  	}
  2245  	runTestCases(t, progHello1, tests)
  2246  }
  2247  
  2248  func TestLongCommands(t *testing.T) {
  2249  	cmdLong := &Command{
  2250  		Name:   "thisisaverylongcommand",
  2251  		Short:  "the short description of the very long command is very long, and will have to be wrapped",
  2252  		Long:   "The long description of the very long command is also very long, and will similarly have to be wrapped",
  2253  		Runner: RunnerFunc(runEcho),
  2254  	}
  2255  	cmdShort := &Command{
  2256  		Name:   "x",
  2257  		Short:  "description of short command.",
  2258  		Long:   "blah blah blah",
  2259  		Runner: RunnerFunc(runEcho),
  2260  	}
  2261  	prog := &Command{
  2262  		Name:     "program",
  2263  		Short:    "Test help strings when there are long commands.",
  2264  		Long:     "Test help strings when there are long commands.",
  2265  		Children: []*Command{cmdShort, cmdLong},
  2266  	}
  2267  	var tests = []testCase{
  2268  		{
  2269  			Args: []string{"help"},
  2270  			Stdout: `Test help strings when there are long commands.
  2271  
  2272  Usage:
  2273     program [flags] <command>
  2274  
  2275  The program commands are:
  2276     x                      description of short command.
  2277     thisisaverylongcommand the short description of the very long command is very
  2278                            long, and will have to be wrapped
  2279     help                   Display help for commands or topics
  2280  Run "program help [command]" for command usage.
  2281  
  2282  The global flags are:
  2283   -global1=
  2284     global test flag 1
  2285   -global2=0
  2286     global test flag 2
  2287  `,
  2288  		},
  2289  		{
  2290  			Args: []string{"help", "thisisaverylongcommand"},
  2291  			Stdout: `The long description of the very long command is also very long, and will
  2292  similarly have to be wrapped
  2293  
  2294  Usage:
  2295     program thisisaverylongcommand [flags]
  2296  
  2297  The global flags are:
  2298   -global1=
  2299     global test flag 1
  2300   -global2=0
  2301     global test flag 2
  2302  `,
  2303  		},
  2304  	}
  2305  	runTestCases(t, prog, tests)
  2306  }
  2307  
  2308  func TestHideGlobalFlags(t *testing.T) {
  2309  	HideGlobalFlags(regexp.MustCompile(`^global1$`))
  2310  	cmdChild := &Command{
  2311  		Name:   "child",
  2312  		Short:  "description of child command.",
  2313  		Long:   "blah blah blah",
  2314  		Runner: RunnerFunc(runEcho),
  2315  	}
  2316  	prog := &Command{
  2317  		Name:     "program",
  2318  		Short:    "Test hiding global flags.",
  2319  		Long:     "Test hiding global flags.",
  2320  		Children: []*Command{cmdChild},
  2321  	}
  2322  	var tests = []testCase{
  2323  		{
  2324  			Args: []string{"help"},
  2325  			Stdout: `Test hiding global flags.
  2326  
  2327  Usage:
  2328     program [flags] <command>
  2329  
  2330  The program commands are:
  2331     child       description of child command.
  2332     help        Display help for commands or topics
  2333  Run "program help [command]" for command usage.
  2334  
  2335  The global flags are:
  2336   -global2=0
  2337     global test flag 2
  2338  
  2339  Run "program help -style=full" to show all flags.
  2340  `,
  2341  		},
  2342  		{
  2343  			Args: []string{"help", "child"},
  2344  			Stdout: `blah blah blah
  2345  
  2346  Usage:
  2347     program child [flags]
  2348  
  2349  The global flags are:
  2350   -global2=0
  2351     global test flag 2
  2352  
  2353  Run "program help -style=full child" to show all flags.
  2354  `,
  2355  		},
  2356  		{
  2357  			Args: []string{"help", "-style=full"},
  2358  			Stdout: `Test hiding global flags.
  2359  
  2360  Usage:
  2361     program [flags] <command>
  2362  
  2363  The program commands are:
  2364     child       description of child command.
  2365     help        Display help for commands or topics
  2366  Run "program help [command]" for command usage.
  2367  
  2368  The global flags are:
  2369   -global2=0
  2370     global test flag 2
  2371  
  2372   -global1=
  2373     global test flag 1
  2374  `,
  2375  		},
  2376  		{
  2377  			Args: []string{"help", "-style=full", "child"},
  2378  			Stdout: `blah blah blah
  2379  
  2380  Usage:
  2381     program child [flags]
  2382  
  2383  The global flags are:
  2384   -global2=0
  2385     global test flag 2
  2386  
  2387   -global1=
  2388     global test flag 1
  2389  `,
  2390  		},
  2391  	}
  2392  	runTestCases(t, prog, tests)
  2393  	hiddenGlobalFlags = nil
  2394  }
  2395  
  2396  func TestHideGlobalFlagsRootNoChildren(t *testing.T) {
  2397  	HideGlobalFlags(regexp.MustCompile(`^global1$`))
  2398  	prog := &Command{
  2399  		Name:   "program",
  2400  		Short:  "Test hiding global flags, root no children.",
  2401  		Long:   "Test hiding global flags, root no children.",
  2402  		Runner: RunnerFunc(runEcho),
  2403  	}
  2404  	var tests = []testCase{
  2405  		{
  2406  			Args: []string{"-help"},
  2407  			Stdout: `Test hiding global flags, root no children.
  2408  
  2409  Usage:
  2410     program [flags]
  2411  
  2412  The global flags are:
  2413   -global2=0
  2414     global test flag 2
  2415  
  2416  Run "CMDLINE_STYLE=full program -help" to show all flags.
  2417  `,
  2418  		},
  2419  		{
  2420  			Args: []string{"-help"},
  2421  			Vars: map[string]string{"CMDLINE_STYLE": "full"},
  2422  			Stdout: `Test hiding global flags, root no children.
  2423  
  2424  Usage:
  2425     program [flags]
  2426  
  2427  The global flags are:
  2428   -global2=0
  2429     global test flag 2
  2430  
  2431   -global1=
  2432     global test flag 1
  2433  `,
  2434  		},
  2435  	}
  2436  	runTestCases(t, prog, tests)
  2437  	hiddenGlobalFlags = nil
  2438  }
  2439  
  2440  func TestRootCommandFlags(t *testing.T) {
  2441  	root := &Command{
  2442  		Name:   "root",
  2443  		Short:  "Test root command flags.",
  2444  		Long:   "Test root command flags.",
  2445  		Runner: RunnerFunc(runHello),
  2446  	}
  2447  	rb := root.Flags.Bool("rbool", false, "rbool desc")
  2448  	rs := root.Flags.String("rstring", "abc", "rstring desc")
  2449  	origFlags := flag.CommandLine
  2450  	// Parse and make sure the flags get set appropriately.
  2451  	_, _, err := Parse(root, EnvFromOS(), []string{"-rbool=true", "-rstring=XYZ"})
  2452  	if err != nil {
  2453  		t.Fatalf("Parse failed: %v", err)
  2454  	}
  2455  	if got, want := *rb, true; got != want {
  2456  		t.Errorf("rbool got %v want %v", got, want)
  2457  	}
  2458  	if got, want := *rs, "XYZ"; got != want {
  2459  		t.Errorf("rstring got %v want %v", got, want)
  2460  	}
  2461  	// Make sure we haven't changed the flag.CommandLine pointer, and that it's
  2462  	// parsed, and it contains our root command flags.  These properties are
  2463  	// important to ensure so that users can check whether the flags are already
  2464  	// parsed to avoid double-parsing.  Even if they do call flag.Parse it'll
  2465  	// succeed, as long as cmdline.Parse succeeded.
  2466  	if got, want := flag.CommandLine, origFlags; got != want {
  2467  		t.Errorf("flag.CommandLine pointer changed, got %p want %p", got, want)
  2468  	}
  2469  	if got, want := flag.CommandLine.Parsed(), true; got != want {
  2470  		t.Errorf("flag.CommandLine.Parsed() got %v, want %v", got, want)
  2471  	}
  2472  	if name := "rbool"; flag.CommandLine.Lookup(name) == nil {
  2473  		t.Errorf("flag.CommandLine.Lookup(%q) failed", name)
  2474  	}
  2475  	if name := "rstring"; flag.CommandLine.Lookup(name) == nil {
  2476  		t.Errorf("flag.CommandLine.Lookup(%q) failed", name)
  2477  	}
  2478  	// Actually try double-parsing flag.CommandLine.
  2479  	if err := flag.CommandLine.Parse([]string{"-rbool=false", "-rstring=123"}); err != nil {
  2480  		t.Errorf("flag.CommandLine.Parse() failed: %v", err)
  2481  	}
  2482  	if got, want := *rb, false; got != want {
  2483  		t.Errorf("rbool got %v want %v", got, want)
  2484  	}
  2485  	if got, want := *rs, "123"; got != want {
  2486  		t.Errorf("rstring got %v want %v", got, want)
  2487  	}
  2488  }
  2489  
  2490  func TestExternalSubcommand(t *testing.T) {
  2491  	// Create a temporary directory for the external subcommands.
  2492  	tmpDir, err := ioutil.TempDir("", "cmdline-test")
  2493  	if err != nil {
  2494  		t.Fatalf("%v", err)
  2495  	}
  2496  	defer os.RemoveAll(tmpDir)
  2497  
  2498  	// Add the temporary directory to PATH.  We add it twice to ensure dups are
  2499  	// filtered in the resulting output.
  2500  	oldPath := os.Getenv("PATH")
  2501  	defer os.Setenv("PATH", oldPath)
  2502  	tokens := strings.Split(oldPath, string(os.PathListSeparator))
  2503  	tokens = append([]string{tmpDir, tmpDir}, tokens...)
  2504  	os.Setenv("PATH", strings.Join(tokens, string(os.PathListSeparator)))
  2505  
  2506  	// Build the external subcommands.
  2507  	for _, subCmd := range []string{"exitcode", "flags", "flat", "foreign", "nested", "repeated"} {
  2508  		cmd := exec.Command("go", "build", "-o", filepath.Join(tmpDir, "unlikely-"+subCmd), filepath.Join(".", "testdata", subCmd+".go"))
  2509  		if out, err := cmd.CombinedOutput(); err != nil {
  2510  			t.Fatalf("%v, %v", string(out), err)
  2511  		}
  2512  	}
  2513  
  2514  	// Create a command that uses these.
  2515  	cmd := &Command{
  2516  		Name:     "unlikely",
  2517  		Short:    "Short description of command unlikely",
  2518  		Long:     "Long description of command unlikely.",
  2519  		LookPath: true,
  2520  		Children: []*Command{
  2521  			&Command{
  2522  				Runner: RunnerFunc(runDumpEnv),
  2523  				Name:   "dumpenv",
  2524  				Short:  "Short description of command dumpenv",
  2525  				Long:   "Long description of command dumpenv.",
  2526  			},
  2527  			&Command{
  2528  				Runner: RunnerFunc(runHello),
  2529  				Name:   "repeated",
  2530  				Short:  "Repeated appears as both a child and as a binary",
  2531  				Long:   "Long description of command repeated.",
  2532  			},
  2533  		},
  2534  	}
  2535  	cmd.Flags.StringVar(new(string), "shared", "", "description of shared")
  2536  
  2537  	var tests = []testCase{
  2538  		{
  2539  			Args: []string{"-help"},
  2540  			Vars: map[string]string{
  2541  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2542  			},
  2543  			Stdout: `Long description of command unlikely.
  2544  
  2545  Usage:
  2546     unlikely [flags] <command>
  2547  
  2548  The unlikely commands are:
  2549     dumpenv     Short description of command dumpenv
  2550     repeated    Repeated appears as both a child and as a binary
  2551     help        Display help for commands or topics
  2552  The unlikely external commands are:
  2553     exitcode    Short description of command exitcode
  2554     flags       Short description of command flags
  2555     flat        Short description of command flat
  2556     foreign     No description available
  2557     nested      Short description of command nested
  2558  Run "unlikely help [command]" for command usage.
  2559  
  2560  The unlikely flags are:
  2561   -shared=
  2562     description of shared
  2563  
  2564  The global flags are:
  2565   -global1=
  2566     global test flag 1
  2567   -global2=0
  2568     global test flag 2
  2569  `,
  2570  		},
  2571  		{
  2572  			Args: []string{"help"},
  2573  			Vars: map[string]string{
  2574  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2575  			},
  2576  			Stdout: `Long description of command unlikely.
  2577  
  2578  Usage:
  2579     unlikely [flags] <command>
  2580  
  2581  The unlikely commands are:
  2582     dumpenv     Short description of command dumpenv
  2583     repeated    Repeated appears as both a child and as a binary
  2584     help        Display help for commands or topics
  2585  The unlikely external commands are:
  2586     exitcode    Short description of command exitcode
  2587     flags       Short description of command flags
  2588     flat        Short description of command flat
  2589     foreign     No description available
  2590     nested      Short description of command nested
  2591  Run "unlikely help [command]" for command usage.
  2592  
  2593  The unlikely flags are:
  2594   -shared=
  2595     description of shared
  2596  
  2597  The global flags are:
  2598   -global1=
  2599     global test flag 1
  2600   -global2=0
  2601     global test flag 2
  2602  `,
  2603  		},
  2604  		{
  2605  			Args: []string{"help", "..."},
  2606  			Vars: map[string]string{
  2607  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2608  			},
  2609  			Stdout: `Long description of command unlikely.
  2610  
  2611  Usage:
  2612     unlikely [flags] <command>
  2613  
  2614  The unlikely commands are:
  2615     dumpenv     Short description of command dumpenv
  2616     repeated    Repeated appears as both a child and as a binary
  2617     help        Display help for commands or topics
  2618  The unlikely external commands are:
  2619     exitcode    Short description of command exitcode
  2620     flags       Short description of command flags
  2621     flat        Short description of command flat
  2622     foreign     No description available
  2623     nested      Short description of command nested
  2624  Run "unlikely help [command]" for command usage.
  2625  
  2626  The unlikely flags are:
  2627   -shared=
  2628     description of shared
  2629  
  2630  The global flags are:
  2631   -global1=
  2632     global test flag 1
  2633   -global2=0
  2634     global test flag 2
  2635  ================================================================================
  2636  Unlikely dumpenv - Short description of command dumpenv
  2637  
  2638  Long description of command dumpenv.
  2639  
  2640  Usage:
  2641     unlikely dumpenv [flags]
  2642  
  2643  Run "unlikely help -style=full dumpenv" to show all flags.
  2644  ================================================================================
  2645  Unlikely repeated - Repeated appears as both a child and as a binary
  2646  
  2647  Long description of command repeated.
  2648  
  2649  Usage:
  2650     unlikely repeated [flags]
  2651  
  2652  Run "unlikely help -style=full repeated" to show all flags.
  2653  ================================================================================
  2654  Unlikely help - Display help for commands or topics
  2655  
  2656  Help with no args displays the usage of the parent command.
  2657  
  2658  Help with args displays the usage of the specified sub-command or help topic.
  2659  
  2660  "help ..." recursively displays help for all commands and topics.
  2661  
  2662  Usage:
  2663     unlikely help [flags] [command/topic ...]
  2664  
  2665  [command/topic ...] optionally identifies a specific sub-command or help topic.
  2666  
  2667  The unlikely help flags are:
  2668   -style=compact
  2669     The formatting style for help output:
  2670        compact   - Good for compact cmdline output.
  2671        full      - Good for cmdline output, shows all global flags.
  2672        godoc     - Good for godoc processing.
  2673        shortonly - Only output short description.
  2674     Override the default by setting the CMDLINE_STYLE environment variable.
  2675   -width=80
  2676     Format output to this target width in runes, or unlimited if width < 0.
  2677     Defaults to the terminal width if available.  Override the default by setting
  2678     the CMDLINE_WIDTH environment variable.
  2679  ================================================================================
  2680  Unlikely exitcode - Short description of command exitcode
  2681  
  2682  Long description of command exitcode.
  2683  
  2684  Usage:
  2685     unlikely exitcode [flags] [args]
  2686  
  2687  [args] are ignored
  2688  ================================================================================
  2689  Unlikely flags - Short description of command flags
  2690  
  2691  Long description of command flags.
  2692  
  2693  Usage:
  2694     unlikely flags [flags] [args]
  2695  
  2696  [args] are ignored
  2697  
  2698  The unlikely flags flags are:
  2699   -global1=
  2700     description of global1
  2701   -local=
  2702     description of local
  2703   -shared=
  2704     description of shared
  2705  ================================================================================
  2706  Unlikely flat - Short description of command flat
  2707  
  2708  Long description of command flat.
  2709  
  2710  Usage:
  2711     unlikely flat [flags] [args]
  2712  
  2713  [args] are ignored
  2714  ================================================================================
  2715  Unlikely foreign - No description available
  2716  ================================================================================
  2717  Unlikely nested - Short description of command nested
  2718  
  2719  Long description of command nested.
  2720  
  2721  Usage:
  2722     unlikely nested [flags] <command>
  2723  
  2724  The unlikely nested commands are:
  2725     child       Short description of command child
  2726  ================================================================================
  2727  Unlikely nested child - Short description of command child
  2728  
  2729  Long description of command child.
  2730  
  2731  Usage:
  2732     unlikely nested child [flags]
  2733  `,
  2734  		},
  2735  		{
  2736  			Args: []string{"help", "-style=godoc", "..."},
  2737  			Vars: map[string]string{
  2738  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2739  			},
  2740  			Stdout: `Long description of command unlikely.
  2741  
  2742  Usage:
  2743     unlikely [flags] <command>
  2744  
  2745  The unlikely commands are:
  2746     dumpenv     Short description of command dumpenv
  2747     repeated    Repeated appears as both a child and as a binary
  2748     help        Display help for commands or topics
  2749  The unlikely external commands are:
  2750     exitcode    Short description of command exitcode
  2751     flags       Short description of command flags
  2752     flat        Short description of command flat
  2753     foreign     No description available
  2754     nested      Short description of command nested
  2755  
  2756  The unlikely flags are:
  2757   -shared=
  2758     description of shared
  2759  
  2760  The global flags are:
  2761   -global1=
  2762     global test flag 1
  2763   -global2=0
  2764     global test flag 2
  2765  
  2766  Unlikely dumpenv - Short description of command dumpenv
  2767  
  2768  Long description of command dumpenv.
  2769  
  2770  Usage:
  2771     unlikely dumpenv [flags]
  2772  
  2773  The unlikely dumpenv flags are:
  2774   -shared=
  2775     description of shared
  2776  
  2777  Unlikely repeated - Repeated appears as both a child and as a binary
  2778  
  2779  Long description of command repeated.
  2780  
  2781  Usage:
  2782     unlikely repeated [flags]
  2783  
  2784  The unlikely repeated flags are:
  2785   -shared=
  2786     description of shared
  2787  
  2788  Unlikely help - Display help for commands or topics
  2789  
  2790  Help with no args displays the usage of the parent command.
  2791  
  2792  Help with args displays the usage of the specified sub-command or help topic.
  2793  
  2794  "help ..." recursively displays help for all commands and topics.
  2795  
  2796  Usage:
  2797     unlikely help [flags] [command/topic ...]
  2798  
  2799  [command/topic ...] optionally identifies a specific sub-command or help topic.
  2800  
  2801  The unlikely help flags are:
  2802   -style=compact
  2803     The formatting style for help output:
  2804        compact   - Good for compact cmdline output.
  2805        full      - Good for cmdline output, shows all global flags.
  2806        godoc     - Good for godoc processing.
  2807        shortonly - Only output short description.
  2808     Override the default by setting the CMDLINE_STYLE environment variable.
  2809   -width=<terminal width>
  2810     Format output to this target width in runes, or unlimited if width < 0.
  2811     Defaults to the terminal width if available.  Override the default by setting
  2812     the CMDLINE_WIDTH environment variable.
  2813  
  2814  Unlikely exitcode - Short description of command exitcode
  2815  
  2816  Long description of command exitcode.
  2817  
  2818  Usage:
  2819     unlikely exitcode [flags] [args]
  2820  
  2821  [args] are ignored
  2822  
  2823  Unlikely flags - Short description of command flags
  2824  
  2825  Long description of command flags.
  2826  
  2827  Usage:
  2828     unlikely flags [flags] [args]
  2829  
  2830  [args] are ignored
  2831  
  2832  The unlikely flags flags are:
  2833   -global1=
  2834     description of global1
  2835   -local=
  2836     description of local
  2837   -shared=
  2838     description of shared
  2839  
  2840  Unlikely flat - Short description of command flat
  2841  
  2842  Long description of command flat.
  2843  
  2844  Usage:
  2845     unlikely flat [flags] [args]
  2846  
  2847  [args] are ignored
  2848  
  2849  Unlikely foreign - No description available
  2850  
  2851  Unlikely nested - Short description of command nested
  2852  
  2853  Long description of command nested.
  2854  
  2855  Usage:
  2856     unlikely nested [flags] <command>
  2857  
  2858  The unlikely nested commands are:
  2859     child       Short description of command child
  2860  
  2861  Unlikely nested child - Short description of command child
  2862  
  2863  Long description of command child.
  2864  
  2865  Usage:
  2866     unlikely nested child [flags]
  2867  `,
  2868  		},
  2869  		{
  2870  			Args: []string{"flat", "help", "..."},
  2871  			Vars: map[string]string{
  2872  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2873  			},
  2874  			Err: errUsageStr,
  2875  			Stderr: `ERROR: unlikely flat: unsupported help invocation
  2876  
  2877  Long description of command flat.
  2878  
  2879  Usage:
  2880     unlikely flat [flags] [args]
  2881  
  2882  [args] are ignored
  2883  
  2884  The global flags are:
  2885   -metadata=<just specify -metadata to activate>
  2886     Displays metadata for the program and exits.
  2887   -time=false
  2888     Dump timing information to stderr before exiting the program.
  2889  `,
  2890  		},
  2891  		{
  2892  			Args: []string{"nested", "child"},
  2893  			Vars: map[string]string{
  2894  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2895  			},
  2896  			Err: errUsageStr,
  2897  			Stderr: `ERROR: wombats!
  2898  
  2899  Long description of command child.
  2900  
  2901  Usage:
  2902     unlikely nested child [flags]
  2903  
  2904  The global flags are:
  2905   -metadata=<just specify -metadata to activate>
  2906     Displays metadata for the program and exits.
  2907   -time=false
  2908     Dump timing information to stderr before exiting the program.
  2909  `,
  2910  		},
  2911  		{
  2912  			Args:   []string{"dumpenv"},
  2913  			Vars:   map[string]string{"A": "a", "B": "b", "CMDLINE_PREFIX": "abc"},
  2914  			Stdout: "[A=a B=b]\n",
  2915  		},
  2916  		{
  2917  			Args: []string{"repeated"},
  2918  			Vars: map[string]string{
  2919  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2920  			},
  2921  			Stdout: "Hello\n",
  2922  		},
  2923  		{
  2924  			Args: []string{"exitcode"},
  2925  			Vars: map[string]string{
  2926  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2927  			},
  2928  			Err: "exit code 42",
  2929  		},
  2930  		{
  2931  			Args: []string{"flags"},
  2932  			Vars: map[string]string{
  2933  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2934  			},
  2935  			Stdout: `global1="" shared="" local="" []` + "\n",
  2936  		},
  2937  		{
  2938  			Args: []string{"-global1=A B", "-shared=C D", "flags"},
  2939  			Vars: map[string]string{
  2940  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2941  			},
  2942  			Stdout:      `global1="A B" shared="C D" local="" []` + "\n",
  2943  			GlobalFlag1: "A B",
  2944  		},
  2945  		{
  2946  			Args: []string{"flags", "-global1=A B", "-shared=C D"},
  2947  			Vars: map[string]string{
  2948  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2949  			},
  2950  			Stdout: `global1="A B" shared="C D" local="" []` + "\n",
  2951  		},
  2952  		{
  2953  			Args: []string{"flags", "-global1=A B", "-shared=C D", "-local=E F"},
  2954  			Vars: map[string]string{
  2955  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2956  			},
  2957  			Stdout: `global1="A B" shared="C D" local="E F" []` + "\n",
  2958  		},
  2959  		{
  2960  			Args: []string{"flags", "x", "y", "z"},
  2961  			Vars: map[string]string{
  2962  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2963  			},
  2964  			Stdout: `global1="" shared="" local="" ["x" "y" "z"]` + "\n",
  2965  		},
  2966  		{
  2967  			Args: []string{"flags", "-global1=A B", "-shared=C D", "-local=E F", "x", "y", "z"},
  2968  			Vars: map[string]string{
  2969  				"PATH": strings.Join(tokens, string(os.PathListSeparator)),
  2970  			},
  2971  			Stdout: `global1="A B" shared="C D" local="E F" ["x" "y" "z"]` + "\n",
  2972  		},
  2973  	}
  2974  	runTestCases(t, cmd, tests)
  2975  }
  2976  
  2977  func TestParsedFlags(t *testing.T) {
  2978  	root := &Command{
  2979  		Name:   "root",
  2980  		Short:  "short",
  2981  		Long:   "long.",
  2982  		Runner: RunnerFunc(runHello),
  2983  	}
  2984  	var v1, v2 bool
  2985  	env := EnvFromOS()
  2986  	root.Flags.BoolVar(&v1, "a", false, "bool")
  2987  	root.Flags.BoolVar(&v2, "b", false, "bool")
  2988  
  2989  	// ParsedFlags should be nil if Parse fails.
  2990  	_, _, err := Parse(root, env, []string{"-xx"})
  2991  	if err == nil {
  2992  		t.Errorf("expected an error")
  2993  	}
  2994  	var nilFlagSet *flag.FlagSet
  2995  	if got, want := root.ParsedFlags, nilFlagSet; got != want {
  2996  		t.Errorf("got %v, want %v", got, want)
  2997  	}
  2998  
  2999  	// ParsedFlags should be set and Parsed returns true if
  3000  	// the command line is successfully parsed.
  3001  	_, _, err = Parse(root, env, nil)
  3002  	if err != nil {
  3003  		t.Fatal(err)
  3004  	}
  3005  	if got, want := root.Flags.Parsed(), false; got != want {
  3006  		t.Errorf("got %v, want %v", got, want)
  3007  	}
  3008  	if got, want := root.ParsedFlags.Parsed(), true; got != want {
  3009  		t.Errorf("got %v, want %v", got, want)
  3010  	}
  3011  	if got, want := v1, false; got != want {
  3012  		t.Errorf("got %v, want %v", got, want)
  3013  	}
  3014  	if got, want := v2, false; got != want {
  3015  		t.Errorf("got %v, want %v", got, want)
  3016  	}
  3017  
  3018  	_, _, err = Parse(root, env, []string{"-a"})
  3019  	if err != nil {
  3020  		t.Fatal(err)
  3021  	}
  3022  	if got, want := root.Flags.Parsed(), false; got != want {
  3023  		t.Errorf("got %v, want %v", got, want)
  3024  	}
  3025  	if got, want := root.ParsedFlags.Parsed(), true; got != want {
  3026  		t.Errorf("got %v, want %v", got, want)
  3027  	}
  3028  	if got, want := v1, true; got != want {
  3029  		t.Errorf("got %v, want %v", got, want)
  3030  	}
  3031  	if got, want := v2, false; got != want {
  3032  		t.Errorf("got %v, want %v", got, want)
  3033  	}
  3034  }
  3035  
  3036  type fc struct {
  3037  	DontPropagateFlags bool
  3038  	DontInheritFlags   bool
  3039  }
  3040  
  3041  func TestFlagPropagation(t *testing.T) {
  3042  	var err error
  3043  	env := EnvFromOS()
  3044  
  3045  	tests := []struct {
  3046  		flagConfigs []fc
  3047  		args        []string
  3048  		want        []string
  3049  	}{
  3050  		{
  3051  			[]fc{fc{false, false}, fc{false, false}, fc{false, false}},
  3052  			[]string{"cmd1", "cmd2"},
  3053  			[]string{"flag0", "flag1", "flag2"},
  3054  		},
  3055  		{
  3056  			[]fc{fc{true, false}, fc{false, false}, fc{false, false}},
  3057  			[]string{"cmd1", "cmd2"},
  3058  			[]string{"flag1", "flag2"},
  3059  		},
  3060  		{
  3061  			[]fc{fc{false, false}, fc{true, false}, fc{false, false}},
  3062  			[]string{"cmd1", "cmd2"},
  3063  			[]string{"flag2"},
  3064  		},
  3065  		{
  3066  			[]fc{fc{false, false}, fc{false, true}, fc{false, false}},
  3067  			[]string{"cmd1", "cmd2"},
  3068  			[]string{"flag1", "flag2"},
  3069  		},
  3070  		{
  3071  			[]fc{fc{false, false}, fc{true, true}, fc{false, false}},
  3072  			[]string{"cmd1", "cmd2"},
  3073  			[]string{"flag2"},
  3074  		},
  3075  		{
  3076  			[]fc{fc{false, false}, fc{false, false}, fc{true, false}},
  3077  			[]string{"cmd1", "cmd2"},
  3078  			[]string{"flag0", "flag1", "flag2"},
  3079  		},
  3080  		{
  3081  			[]fc{fc{false, false}, fc{false, false}, fc{false, true}},
  3082  			[]string{"cmd1", "cmd2"},
  3083  			[]string{"flag2"},
  3084  		},
  3085  		{
  3086  			[]fc{fc{false, false}, fc{false, false}, fc{true, true}},
  3087  			[]string{"cmd1", "cmd2"},
  3088  			[]string{"flag2"},
  3089  		},
  3090  	}
  3091  
  3092  	for _, test := range tests {
  3093  		commands := createCommandTree(test.flagConfigs)
  3094  		root := commands[0]
  3095  		leaf := commands[len(commands)-1]
  3096  
  3097  		_, _, err = Parse(root, env, test.args)
  3098  		if err != nil {
  3099  			t.Fatal(err)
  3100  		}
  3101  
  3102  		want := map[string]bool{}
  3103  		globalFlags.VisitAll(func(f *flag.Flag) { want[f.Name] = true })
  3104  		for _, flagName := range test.want {
  3105  			want[flagName] = true
  3106  		}
  3107  
  3108  		got := map[string]bool{}
  3109  		leaf.ParsedFlags.VisitAll(func(f *flag.Flag) { got[f.Name] = true })
  3110  
  3111  		if !reflect.DeepEqual(got, want) {
  3112  			t.Fatalf("got %v, want %v", got, want)
  3113  		}
  3114  	}
  3115  }
  3116  
  3117  func createCommandTree(flagConfigs []fc) []*Command {
  3118  	size := len(flagConfigs)
  3119  	result := make([]*Command, size)
  3120  
  3121  	result[size-1] = &Command{Runner: RunnerFunc(runHello)}
  3122  	for i := size - 2; i >= 0; i-- {
  3123  		result[i] = &Command{Children: []*Command{result[i+1]}}
  3124  	}
  3125  
  3126  	for i, cmd := range result {
  3127  		cmd.Name = "cmd" + strconv.Itoa(i)
  3128  		cmd.Short = "short"
  3129  		cmd.Long = "long."
  3130  		cmd.Flags.Bool("flag"+strconv.Itoa(i), false, "bool")
  3131  		cmd.DontPropagateFlags = flagConfigs[i].DontPropagateFlags
  3132  		cmd.DontInheritFlags = flagConfigs[i].DontInheritFlags
  3133  	}
  3134  
  3135  	return result
  3136  }