github.com/bobziuchkovski/writ@v0.8.9/command_test.go (about)

     1  // Copyright (c) 2016 Bob Ziuchkovski
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package writ
    22  
    23  import (
    24  	"fmt"
    25  	"io"
    26  	"io/ioutil"
    27  	"math"
    28  	"os"
    29  	"reflect"
    30  	"strconv"
    31  	"strings"
    32  	"testing"
    33  )
    34  
    35  func CompareField(structval interface{}, field string, value interface{}) (equal bool, fieldVal interface{}) {
    36  	rval := reflect.ValueOf(structval)
    37  	for rval.Kind() == reflect.Ptr {
    38  		rval = rval.Elem()
    39  	}
    40  	f := rval.FieldByName(field)
    41  	equal = reflect.DeepEqual(f.Interface(), value)
    42  	fieldVal = f.Interface()
    43  	return
    44  }
    45  
    46  /*
    47   * Test command and option routing for multi-tier commands
    48   */
    49  
    50  type topSpec struct {
    51  	MidSpec  midSpec `command:"mid" alias:"second, 2nd" description:"a mid-level command"`
    52  	HelpFlag bool    `flag:"h, help" description:"help flag on a top-level command"`
    53  	Top      int     `option:"t, topval" description:"an option on a top-level command"`
    54  }
    55  
    56  type midSpec struct {
    57  	Mid        int        `option:"m, midval" description:"an option on a mid-level command"`
    58  	HelpFlag   bool       `flag:"h, help" description:"help flag on a mid-level command"`
    59  	BottomSpec bottomSpec `command:"bottom" alias:"third" description:"a bottom-level command"`
    60  }
    61  
    62  type bottomSpec struct {
    63  	Bottom   int  `option:"b, bottomval" description:"an option on a bottom-level command"`
    64  	HelpFlag bool `flag:"h, help" description:"help flag on a bottom-level command"`
    65  }
    66  
    67  type commandFieldTest struct {
    68  	Args       []string
    69  	Valid      bool
    70  	Err        string // TODO: rewrite all Valid: false cases with Err: message
    71  	Path       string
    72  	Positional []string
    73  	Field      string
    74  	Value      interface{}
    75  	SkipReason string
    76  }
    77  
    78  var commandFieldTests = []commandFieldTest{
    79  	// Path: top
    80  	{Args: []string{}, Valid: true, Path: "top", Positional: []string{}},
    81  	{Args: []string{"-"}, Valid: true, Path: "top", Positional: []string{"-"}},
    82  	{Args: []string{"-", "mid"}, Valid: true, Path: "top", Positional: []string{"-", "mid"}},
    83  	{Args: []string{"--"}, Valid: true, Path: "top", Positional: []string{}},
    84  	{Args: []string{"--", "mid"}, Valid: true, Path: "top", Positional: []string{"mid"}},
    85  	{Args: []string{"-t", "1"}, Valid: true, Path: "top", Positional: []string{}, Field: "Top", Value: 1},
    86  	{Args: []string{"foo", "-t", "1"}, Valid: true, Path: "top", Positional: []string{"foo"}, Field: "Top", Value: 1},
    87  	{Args: []string{"-t", "1", "foo"}, Valid: true, Path: "top", Positional: []string{"foo"}, Field: "Top", Value: 1},
    88  	{Args: []string{"foo", "bar"}, Valid: true, Path: "top", Positional: []string{"foo", "bar"}},
    89  	{Args: []string{"foo", "-t", "1", "bar"}, Valid: true, Path: "top", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
    90  	{Args: []string{"-t", "1", "foo", "bar"}, Valid: true, Path: "top", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
    91  	{Args: []string{"--", "mid"}, Valid: true, Path: "top", Positional: []string{"mid"}},
    92  	{Args: []string{"-t", "1", "--", "mid"}, Valid: true, Path: "top", Positional: []string{"mid"}, Field: "Top", Value: 1},
    93  	{Args: []string{"-", "-t", "1", "--", "mid"}, Valid: true, Path: "top", Positional: []string{"-", "mid"}, Field: "Top", Value: 1},
    94  	{Args: []string{"--", "-t", "1", "mid"}, Valid: true, Path: "top", Positional: []string{"-t", "1", "mid"}, Field: "Top", Value: 0},
    95  	{Args: []string{"--", "-t", "1", "-", "mid"}, Valid: true, Path: "top", Positional: []string{"-t", "1", "-", "mid"}, Field: "Top", Value: 0},
    96  	{Args: []string{"bottom"}, Valid: true, Path: "top", Positional: []string{"bottom"}},
    97  	{Args: []string{"third"}, Valid: true, Path: "top", Positional: []string{"third"}},
    98  	{Args: []string{"bottom", "mid"}, Valid: true, Path: "top", Positional: []string{"bottom", "mid"}},
    99  	{Args: []string{"bottom", "second"}, Valid: true, Path: "top", Positional: []string{"bottom", "second"}},
   100  	{Args: []string{"bottom", "-", "second"}, Valid: true, Path: "top", Positional: []string{"bottom", "-", "second"}},
   101  	{Args: []string{"-m", "2"}, Valid: false},
   102  	{Args: []string{"--midval", "2"}, Valid: false},
   103  	{Args: []string{"-b", "3"}, Valid: false},
   104  	{Args: []string{"--bottomval", "3"}, Valid: false},
   105  	{Args: []string{"--bogus", "4"}, Valid: false},
   106  	{Args: []string{"--foo"}, Valid: false},
   107  	{Args: []string{"--foo=bar"}, Valid: false},
   108  	{Args: []string{"-f"}, Valid: false},
   109  	{Args: []string{"-f", "bar"}, Valid: false},
   110  	{Args: []string{"-fbar"}, Valid: false},
   111  	{Args: []string{"--help", "--help"}, Valid: false, Err: `option "--help" specified too many times`},
   112  	{Args: []string{"-h", "-h"}, Valid: false, Err: `option "-h" specified too many times`},
   113  	{Args: []string{"-hh"}, Valid: false, Err: `option "-h" specified too many times`},
   114  
   115  	// Path: top mid
   116  	{Args: []string{"mid"}, Valid: true, Path: "top mid", Positional: []string{}},
   117  	{Args: []string{"mid", "-"}, Valid: true, Path: "top mid", Positional: []string{"-"}},
   118  	{Args: []string{"mid", "--"}, Valid: true, Path: "top mid", Positional: []string{}},
   119  	{Args: []string{"mid", "-", "bottom"}, Valid: true, Path: "top mid", Positional: []string{"-", "bottom"}},
   120  	{Args: []string{"mid", "--", "bottom"}, Valid: true, Path: "top mid", Positional: []string{"bottom"}},
   121  	{Args: []string{"mid", "-t", "1"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "Top", Value: 1},
   122  	{Args: []string{"mid", "-", "-t", "1"}, Valid: true, Path: "top mid", Positional: []string{"-"}, Field: "Top", Value: 1},
   123  	{Args: []string{"mid", "--", "-t", "1"}, Valid: true, Path: "top mid", Positional: []string{"-t", "1"}, Field: "Top", Value: 0},
   124  	{Args: []string{"-t", "1", "mid"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "Top", Value: 1},
   125  	{Args: []string{"mid", "foo", "-t", "1"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Top", Value: 1},
   126  	{Args: []string{"-t", "1", "mid", "foo"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Top", Value: 1},
   127  	{Args: []string{"mid", "-t", "1", "foo"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Top", Value: 1},
   128  	{Args: []string{"-t", "1", "mid", "foo"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Top", Value: 1},
   129  	{Args: []string{"mid", "foo", "-m", "2"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Mid", Value: 2},
   130  	{Args: []string{"mid", "-m", "2", "foo"}, Valid: true, Path: "top mid", Positional: []string{"foo"}, Field: "Mid", Value: 2},
   131  	{Args: []string{"mid", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}},
   132  	{Args: []string{"second", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}},
   133  	{Args: []string{"2nd", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}},
   134  	{Args: []string{"mid", "foo", "-t", "1", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   135  	{Args: []string{"mid", "-t", "1", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   136  	{Args: []string{"-t", "1", "mid", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   137  	{Args: []string{"mid", "foo", "-m", "2", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Mid", Value: 2},
   138  	{Args: []string{"-t", "1", "second", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   139  	{Args: []string{"second", "foo", "-m", "2", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Mid", Value: 2},
   140  	{Args: []string{"-t", "1", "2nd", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   141  	{Args: []string{"2nd", "foo", "-m", "2", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Mid", Value: 2},
   142  	{Args: []string{"mid", "-m", "2", "foo", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Mid", Value: 2},
   143  	{Args: []string{"mid", "-m", "2", "foo", "-t", "1", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   144  	{Args: []string{"mid", "-m", "2", "foo", "-t", "1", "bar"}, Valid: true, Field: "Mid", Value: 2},
   145  	{Args: []string{"mid", "-t", "1", "foo", "-m", "2", "bar"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   146  	{Args: []string{"mid", "-t", "1", "foo", "-m", "2", "bar"}, Valid: true, Field: "Mid", Value: 2},
   147  	{Args: []string{"-t", "1", "mid", "foo", "bar", "-m", "2"}, Valid: true, Path: "top mid", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   148  	{Args: []string{"-t", "1", "mid", "foo", "bar", "-m", "2"}, Valid: true, Field: "Mid", Value: 2},
   149  	{Args: []string{"mid", "-m", "2", "--"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "Mid", Value: 2},
   150  	{Args: []string{"mid", "--", "-m", "2"}, Valid: true, Path: "top mid", Positional: []string{"-m", "2"}, Field: "Mid", Value: 0},
   151  	{Args: []string{"mid", "--", "bottom", "-b", "3"}, Valid: true, Path: "top mid", Positional: []string{"bottom", "-b", "3"}},
   152  	{Args: []string{"mid", "--", "bottom", "-b", "3", "--"}, Valid: true, Path: "top mid", Positional: []string{"bottom", "-b", "3", "--"}},
   153  	{Args: []string{"mid", "--", "bottom", "--", "-b", "3"}, Valid: true, Path: "top mid", Positional: []string{"bottom", "--", "-b", "3"}},
   154  	{Args: []string{"-m", "2", "mid"}, Valid: false},
   155  	{Args: []string{"--midval", "2", "mid"}, Valid: false},
   156  	{Args: []string{"-m", "2", "mid", "foo"}, Valid: false},
   157  	{Args: []string{"-b", "3", "mid"}, Valid: false},
   158  	{Args: []string{"-b", "3", "mid", "foo"}, Valid: false},
   159  	{Args: []string{"mid", "-b", "3"}, Valid: false},
   160  	{Args: []string{"mid", "-b", "3", "foo"}, Valid: false},
   161  	{Args: []string{"mid", "--bogus", "4"}, Valid: false},
   162  	{Args: []string{"mid", "--foo"}, Valid: false},
   163  	{Args: []string{"mid", "--foo=bar"}, Valid: false},
   164  	{Args: []string{"mid", "-f"}, Valid: false},
   165  	{Args: []string{"mid", "-f", "bar"}, Valid: false},
   166  	{Args: []string{"mid", "-fbar"}, Valid: false},
   167  	{Args: []string{"mid", "--help", "--help"}, Valid: false, Err: `option "--help" specified too many times`},
   168  	{Args: []string{"mid", "-h", "-h"}, Valid: false, Err: `option "-h" specified too many times`},
   169  	{Args: []string{"mid", "-hh"}, Valid: false, Err: `option "-h" specified too many times`},
   170  
   171  	// Path: top mid bottom
   172  	{Args: []string{"mid", "bottom"}, Valid: true, Path: "top mid bottom", Positional: []string{}},
   173  	{Args: []string{"mid", "bottom", "-"}, Valid: true, Path: "top mid bottom", Positional: []string{"-"}},
   174  	{Args: []string{"mid", "bottom", "--"}, Valid: true, Path: "top mid bottom", Positional: []string{}},
   175  	{Args: []string{"mid", "bottom", "-t", "1"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "Top", Value: 1},
   176  	{Args: []string{"mid", "-t", "1", "bottom"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "Top", Value: 1},
   177  	{Args: []string{"-t", "1", "mid", "bottom"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "Top", Value: 1},
   178  	{Args: []string{"mid", "bottom", "foo", "-t", "1"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Top", Value: 1},
   179  	{Args: []string{"mid", "-t", "1", "bottom", "foo"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Top", Value: 1},
   180  	{Args: []string{"-t", "1", "mid", "bottom", "foo"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Top", Value: 1},
   181  	{Args: []string{"mid", "bottom", "foo", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Bottom", Value: 3},
   182  	{Args: []string{"mid", "bottom", "-b", "3", "foo"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Bottom", Value: 3},
   183  	{Args: []string{"mid", "bottom", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}},
   184  	{Args: []string{"mid", "third", "-b", "3", "foo"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo"}, Field: "Bottom", Value: 3},
   185  	{Args: []string{"2nd", "third", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}},
   186  	{Args: []string{"-t", "1", "mid", "bottom", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Top", Value: 1},
   187  	{Args: []string{"mid", "bottom", "foo", "-b", "3", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3},
   188  	{Args: []string{"mid", "bottom", "-b", "3", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3},
   189  	{Args: []string{"mid", "-t", "1", "bottom", "-b", "3", "foo", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3},
   190  	{Args: []string{"mid", "-t", "1", "bottom", "-b", "3", "foo", "bar"}, Valid: true, Field: "Top", Value: 1},
   191  	{Args: []string{"mid", "-m", "2", "bottom", "foo", "-b", "3", "bar"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3},
   192  	{Args: []string{"mid", "-m", "2", "bottom", "foo", "-b", "3", "bar"}, Valid: true, Field: "Mid", Value: 2},
   193  	{Args: []string{"-t", "1", "mid", "bottom", "foo", "bar", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3},
   194  	{Args: []string{"-t", "1", "mid", "bottom", "foo", "bar", "-b", "3"}, Valid: true, Field: "Top", Value: 1},
   195  	{Args: []string{"-t", "1", "2nd", "bottom", "foo", "bar", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3},
   196  	{Args: []string{"-t", "1", "2nd", "bottom", "foo", "bar", "-b", "3"}, Valid: true, Field: "Top", Value: 1},
   197  	{Args: []string{"-t", "1", "mid", "third", "foo", "bar", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3},
   198  	{Args: []string{"-t", "1", "mid", "third", "foo", "bar", "-b", "3"}, Valid: true, Field: "Top", Value: 1},
   199  	{Args: []string{"-t", "1", "second", "third", "foo", "bar", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"foo", "bar"}, Field: "Bottom", Value: 3},
   200  	{Args: []string{"-t", "1", "second", "third", "foo", "bar", "-b", "3"}, Valid: true, Field: "Top", Value: 1},
   201  	{Args: []string{"mid", "bottom", "-b", "3", "--"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "Bottom", Value: 3},
   202  	{Args: []string{"mid", "bottom", "-", "-b", "3", "--"}, Valid: true, Path: "top mid bottom", Positional: []string{"-"}, Field: "Bottom", Value: 3},
   203  	{Args: []string{"mid", "bottom", "--", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"-b", "3"}, Field: "Bottom", Value: 0},
   204  	{Args: []string{"mid", "bottom", "-", "--", "-b", "3"}, Valid: true, Path: "top mid bottom", Positional: []string{"-", "-b", "3"}, Field: "Bottom", Value: 0},
   205  	{Args: []string{"mid", "-b", "3", "bottom"}, Valid: false},
   206  	{Args: []string{"bottom", "-b", "3"}, Valid: false},
   207  	{Args: []string{"-b", "3", "bottom"}, Valid: false},
   208  	{Args: []string{"-b", "3", "mid", "bottom"}, Valid: false},
   209  	{Args: []string{"mid", "--help", "--help", "bottom"}, Valid: false, Err: `option "--help" specified too many times`},
   210  	{Args: []string{"mid", "-h", "-h", "bottom"}, Valid: false, Err: `option "-h" specified too many times`},
   211  	{Args: []string{"mid", "-hh", "bottom"}, Valid: false, Err: `option "-h" specified too many times`},
   212  
   213  	// Duplicate option routing (HelpFlag)
   214  	{Args: []string{"-h"}, Valid: true, Path: "top", Positional: []string{}, Field: "HelpFlagTop", Value: true},
   215  	{Args: []string{"-h"}, Valid: true, Field: "HelpFlagMid", Value: false},
   216  	{Args: []string{"-h"}, Valid: true, Field: "HelpFlagBottom", Value: false},
   217  	{Args: []string{"-h", "mid"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "HelpFlagTop", Value: true},
   218  	{Args: []string{"-h", "mid"}, Valid: true, Field: "HelpFlagMid", Value: false},
   219  	{Args: []string{"-h", "mid"}, Valid: true, Field: "HelpFlagBottom", Value: false},
   220  	{Args: []string{"mid", "-h"}, Valid: true, Path: "top mid", Positional: []string{}, Field: "HelpFlagMid", Value: true},
   221  	{Args: []string{"mid", "-h"}, Valid: true, Field: "HelpFlagTop", Value: false},
   222  	{Args: []string{"mid", "-h"}, Valid: true, Field: "HelpFlagBottom", Value: false},
   223  	{Args: []string{"mid", "-h", "bottom"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "HelpFlagMid", Value: true},
   224  	{Args: []string{"mid", "-h", "bottom"}, Valid: true, Field: "HelpFlagTop", Value: false},
   225  	{Args: []string{"mid", "-h", "bottom"}, Valid: true, Field: "HelpFlagBottom", Value: false},
   226  	{Args: []string{"mid", "bottom", "-h"}, Valid: true, Path: "top mid bottom", Positional: []string{}, Field: "HelpFlagBottom", Value: true},
   227  	{Args: []string{"mid", "bottom", "-h"}, Valid: true, Field: "HelpFlagTop", Value: false},
   228  	{Args: []string{"mid", "bottom", "-h"}, Valid: true, Field: "HelpFlagMid", Value: false},
   229  }
   230  
   231  func TestCommandFields(t *testing.T) {
   232  	for _, test := range commandFieldTests {
   233  		spec := &topSpec{}
   234  		runCommandFieldTest(t, spec, test)
   235  	}
   236  }
   237  
   238  func runCommandFieldTest(t *testing.T, spec *topSpec, test commandFieldTest) {
   239  	if test.SkipReason != "" {
   240  		t.Logf("Test skipped. Args: %q, Field: %s, Reason: %s", test.Args, test.Field, test.SkipReason)
   241  		return
   242  	}
   243  
   244  	cmd := New("top", spec)
   245  	path, positional, err := cmd.Decode(test.Args)
   246  	values := map[string]interface{}{
   247  		"Top":            spec.Top,
   248  		"Mid":            spec.MidSpec.Mid,
   249  		"Bottom":         spec.MidSpec.BottomSpec.Bottom,
   250  		"HelpFlagTop":    spec.HelpFlag,
   251  		"HelpFlagMid":    spec.MidSpec.HelpFlag,
   252  		"HelpFlagBottom": spec.MidSpec.BottomSpec.HelpFlag,
   253  	}
   254  	if !test.Valid {
   255  		if err == nil {
   256  			t.Errorf("Expected error but none received. Args: %q", test.Args)
   257  		}
   258  		if test.Err != "" && err.Error() != test.Err {
   259  			t.Errorf("Invalid error message.  Expected: %s, Received: %s", test.Err, err.Error())
   260  		}
   261  		return
   262  	}
   263  	if err != nil {
   264  		t.Errorf("Received unexpected error. Field: %s, Args: %q, Error: %s", test.Field, test.Args, err)
   265  		return
   266  	}
   267  	if test.Positional != nil && !reflect.DeepEqual(positional, test.Positional) {
   268  		t.Errorf("Positional args are incorrect. Args: %q, Expected: %s, Received: %s", test.Args, test.Positional, positional)
   269  		return
   270  	}
   271  	if test.Field != "" && !reflect.DeepEqual(values[test.Field], test.Value) {
   272  		t.Errorf("Decoded value is incorrect. Field: %s, Args: %q, Expected: %#v, Received: %#v", test.Field, test.Args, test.Value, values[test.Field])
   273  		return
   274  	}
   275  	if path.First() != cmd {
   276  		t.Errorf("Expected first command in path to be top-level command, but got %s instead.", path.First().Name)
   277  		return
   278  	}
   279  	if test.Path != "" && path.String() != test.Path {
   280  		t.Errorf("Command path is incorrect. Args: %q, Expected: %s, Received: %s", test.Args, test.Path, path)
   281  		return
   282  	}
   283  }
   284  
   285  func TestCommandString(t *testing.T) {
   286  	cmd := New("top", &topSpec{})
   287  	if cmd.String() != "top" {
   288  		t.Errorf("Invalid Command.String() value.  Expected: %q, received: %q", "top", cmd.String())
   289  	}
   290  	if cmd.Subcommand("mid").String() != "mid" {
   291  		t.Errorf("Invalid Command.String() value.  Expected: %q, received: %q", "mid", cmd.Subcommand("mid").String())
   292  	}
   293  	if cmd.Subcommand("mid").Subcommand("bottom").String() != "bottom" {
   294  		t.Errorf("Invalid Command.String() value.  Expected: %q, received: %q", "bottom", cmd.Subcommand("mid").Subcommand("bottom").String())
   295  	}
   296  }
   297  
   298  /*
   299   * Test parsing of description metadata
   300   */
   301  
   302  func TestSpecDescriptions(t *testing.T) {
   303  	type Spec struct {
   304  		Flag    bool     `flag:"flag" description:"a flag"`
   305  		Option  int      `option:"option" description:"an option"`
   306  		Command struct{} `command:"command" description:"a command"`
   307  	}
   308  	cmd := New("test", &Spec{})
   309  	if cmd.Option("flag").Description != "a flag" {
   310  		t.Errorf("Flag description is incorrect. Expected: %q, Received: %q", "a flag", cmd.Option("flag").Description)
   311  	}
   312  	if cmd.Option("option").Description != "an option" {
   313  		t.Errorf("Option description is incorrect. Expected: %q, Received: %q", "an option", cmd.Option("option").Description)
   314  	}
   315  	if cmd.Subcommand("command").Description != "a command" {
   316  		t.Errorf("Command description is incorrect. Expected: %q, Received: %q", "a command", cmd.Subcommand("command").Description)
   317  	}
   318  }
   319  
   320  /*
   321   * Test parsing of placeholder metadata
   322   */
   323  
   324  func TestSpecPlaceholders(t *testing.T) {
   325  	type Spec struct {
   326  		Option int `option:"option" description:"an option" placeholder:"VALUE"`
   327  	}
   328  	cmd := New("test", &Spec{})
   329  	if cmd.Option("option").Placeholder != "VALUE" {
   330  		t.Errorf("Option placeholder is incorrect. Expected: %q, Received: %q", "VALUE", cmd.Option("option").Placeholder)
   331  	}
   332  }
   333  
   334  /*
   335   * Test default values on fields
   336   */
   337  
   338  type defaultFieldSpec struct {
   339  	Default        int `option:"d" description:"An int field with a default" default:"42"`
   340  	EnvDefault     int `option:"e" description:"An int field with an environment default" env:"ENV_DEFAULT"`
   341  	StackedDefault int `option:"s" description:"An int field with both a default and environment default" default:"84" env:"STACKED_DEFAULT"`
   342  }
   343  
   344  type defaultFieldTest struct {
   345  	Args       []string
   346  	Valid      bool
   347  	Field      string
   348  	Value      interface{}
   349  	EnvKey     string
   350  	EnvValue   string
   351  	SkipReason string
   352  }
   353  
   354  var defaultFieldTests = []defaultFieldTest{
   355  	// Field with a default value
   356  	{Args: []string{""}, Valid: true, Field: "Default", Value: 42},
   357  	{Args: []string{"-d", "2"}, Valid: true, Field: "Default", Value: 2},
   358  	{Args: []string{"-d", "foo"}, Valid: false},
   359  
   360  	// Field with an environment default
   361  	{Args: []string{""}, Valid: true, Field: "EnvDefault", Value: 0},
   362  	{Args: []string{""}, Valid: true, EnvKey: "ENV_DEFAULT", EnvValue: "2", Field: "EnvDefault", Value: 2},
   363  	{Args: []string{""}, Valid: true, EnvKey: "ENV_DEFAULT", EnvValue: "foo", Field: "EnvDefault", Value: 0},
   364  	{Args: []string{"-e", "4"}, Valid: true, EnvKey: "ENV_DEFAULT", EnvValue: "2", Field: "EnvDefault", Value: 4},
   365  	{Args: []string{"-e", "4"}, Valid: true, EnvKey: "ENV_DEFAULT", EnvValue: "foo", Field: "EnvDefault", Value: 4},
   366  	{Args: []string{"-e", "foo"}, Valid: false, EnvKey: "ENV_DEFAULT", EnvValue: "2"},
   367  
   368  	// Field with both a default value and an environment default
   369  	{Args: []string{""}, Valid: true, Field: "StackedDefault", Value: 84},
   370  	{Args: []string{""}, Valid: true, EnvKey: "STACKED_DEFAULT", EnvValue: "2", Field: "StackedDefault", Value: 2},
   371  	{Args: []string{""}, Valid: true, EnvKey: "STACKED_DEFAULT", EnvValue: "foo", Field: "StackedDefault", Value: 84},
   372  	{Args: []string{"-s", "4"}, Valid: true, EnvKey: "STACKED_DEFAULT", EnvValue: "2", Field: "StackedDefault", Value: 4},
   373  	{Args: []string{"-s", "4"}, Valid: true, EnvKey: "STACKED_DEFAULT", EnvValue: "foo", Field: "StackedDefault", Value: 4},
   374  	{Args: []string{"-s", "foo"}, Valid: false, EnvKey: "STACKED_DEFAULT", EnvValue: "foo"},
   375  	{Args: []string{"-s", "foo"}, Valid: false},
   376  }
   377  
   378  func TestDefaultFields(t *testing.T) {
   379  	for _, test := range defaultFieldTests {
   380  		spec := &defaultFieldSpec{}
   381  		runDefaultFieldTest(t, spec, test)
   382  	}
   383  }
   384  
   385  func runDefaultFieldTest(t *testing.T, spec interface{}, test defaultFieldTest) {
   386  	if test.SkipReason != "" {
   387  		t.Logf("Test skipped. Args: %q, Field: %s, Reason: %s", test.Args, test.Field, test.SkipReason)
   388  		return
   389  	}
   390  
   391  	if test.EnvKey != "" {
   392  		realval := os.Getenv(test.EnvKey)
   393  		defer (func() { os.Setenv(test.EnvKey, realval) })()
   394  		os.Setenv(test.EnvKey, test.EnvValue)
   395  	}
   396  	cmd := New("test", spec)
   397  	_, _, err := cmd.Decode(test.Args)
   398  
   399  	if !test.Valid {
   400  		if err == nil {
   401  			t.Errorf("Expected error but none received. Args: %q", test.Args)
   402  		}
   403  		return
   404  	}
   405  	if err != nil {
   406  		t.Errorf("Received unexpected error. Field: %s, Args: %q, Error: %s", test.Field, test.Args, err)
   407  		return
   408  	}
   409  	equal, fieldval := CompareField(spec, test.Field, test.Value)
   410  	if !equal {
   411  		t.Errorf("Decoded value is incorrect. Field: %s, Args: %q, Expected: %#v, Received: %#v", test.Field, test.Args, test.Value, fieldval)
   412  		return
   413  	}
   414  }
   415  
   416  func TestBogusDefaultField(t *testing.T) {
   417  	var spec = &struct {
   418  		BogusDefault int `option:"b" description:"An int field with a bogus default" default:"bogus"`
   419  	}{}
   420  
   421  	defer func() {
   422  		r := recover()
   423  		if r != nil {
   424  			switch r.(type) {
   425  			case commandError, optionError:
   426  				// Intentional No-op
   427  			default:
   428  				panic(r)
   429  			}
   430  		}
   431  	}()
   432  
   433  	cmd := New("test", spec)
   434  	cmd.Decode([]string{})
   435  	t.Errorf("Expected decoding to panic on bogus default value, but this didn't happen.")
   436  }
   437  
   438  /*
   439   * Generic field test helpers
   440   */
   441  
   442  type fieldTest struct {
   443  	Args       []string
   444  	Valid      bool
   445  	Field      string
   446  	Value      interface{}
   447  	SkipReason string
   448  }
   449  
   450  func runFieldTest(t *testing.T, spec interface{}, test fieldTest) {
   451  	if test.SkipReason != "" {
   452  		t.Logf("Test skipped. Args: %q, Field: %s, Reason: %s", test.Args, test.Field, test.SkipReason)
   453  		return
   454  	}
   455  
   456  	cmd := New("test", spec)
   457  	_, _, err := cmd.Decode(test.Args)
   458  	if !test.Valid {
   459  		if err == nil {
   460  			t.Errorf("Expected error but none received. Args: %q", test.Args)
   461  		}
   462  		return
   463  	}
   464  	if err != nil {
   465  		t.Errorf("Received unexpected error. Field: %s, Args: %q, Error: %s", test.Field, test.Args, err)
   466  		return
   467  	}
   468  	equal, fieldval := CompareField(spec, test.Field, test.Value)
   469  	if !equal {
   470  		t.Errorf("Decoded value is incorrect. Field: %s, Args: %q, Expected: %#v, Received: %#v", test.Field, test.Args, test.Value, fieldval)
   471  		return
   472  	}
   473  }
   474  
   475  /*
   476   * Test option name variations
   477   */
   478  
   479  type optNameSpec struct {
   480  	Bool        bool    `flag:" b, bool" description:"A bool flag"`
   481  	Accumulator int     `flag:"a,acc,A, accum" description:"An accumulator field"`
   482  	Int         int     `option:"int,  I" description:"An int field"`
   483  	Float       float32 `option:" float,F, FloaT,  f " description:"A float field"`
   484  }
   485  
   486  var optNameTests = []fieldTest{
   487  	// Bool Flag
   488  	{Args: []string{"-b"}, Valid: true, Field: "Bool", Value: true},
   489  	{Args: []string{"--bool"}, Valid: true, Field: "Bool", Value: true},
   490  
   491  	// Accumulator Flag
   492  	{Args: []string{"-A", "--accum", "-a", "--acc", "-Aa", "-aA"}, Valid: true, Field: "Accumulator", Value: 8},
   493  
   494  	// Int Option
   495  	{Args: []string{"-I2"}, Valid: true, Field: "Int", Value: 2},
   496  	{Args: []string{"-I", "2"}, Valid: true, Field: "Int", Value: 2},
   497  	{Args: []string{"--int", "2"}, Valid: true, Field: "Int", Value: 2},
   498  	{Args: []string{"--int=2"}, Valid: true, Field: "Int", Value: 2},
   499  
   500  	// Float Option
   501  	{Args: []string{"-F2"}, Valid: true, Field: "Float", Value: float32(2.0)},
   502  	{Args: []string{"-F2.5"}, Valid: true, Field: "Float", Value: float32(2.5)},
   503  	{Args: []string{"-F", "2"}, Valid: true, Field: "Float", Value: float32(2.0)},
   504  	{Args: []string{"-F", "2.5"}, Valid: true, Field: "Float", Value: float32(2.5)},
   505  	{Args: []string{"-f2"}, Valid: true, Field: "Float", Value: float32(2.0)},
   506  	{Args: []string{"-f2.5"}, Valid: true, Field: "Float", Value: float32(2.5)},
   507  	{Args: []string{"-f", "2"}, Valid: true, Field: "Float", Value: float32(2.0)},
   508  	{Args: []string{"-f", "2.5"}, Valid: true, Field: "Float", Value: float32(2.5)},
   509  	{Args: []string{"--FloaT", "2"}, Valid: true, Field: "Float", Value: float32(2.0)},
   510  	{Args: []string{"--FloaT", "2.5"}, Valid: true, Field: "Float", Value: float32(2.5)},
   511  	{Args: []string{"--FloaT=2"}, Valid: true, Field: "Float", Value: float32(2.0)},
   512  	{Args: []string{"--FloaT=2.5"}, Valid: true, Field: "Float", Value: float32(2.5)},
   513  	{Args: []string{"--float", "2"}, Valid: true, Field: "Float", Value: float32(2.0)},
   514  	{Args: []string{"--float", "2.5"}, Valid: true, Field: "Float", Value: float32(2.5)},
   515  	{Args: []string{"--float=2"}, Valid: true, Field: "Float", Value: float32(2.0)},
   516  	{Args: []string{"--float=2.5"}, Valid: true, Field: "Float", Value: float32(2.5)},
   517  }
   518  
   519  func TestOptionNames(t *testing.T) {
   520  	for _, test := range optNameTests {
   521  		spec := &optNameSpec{}
   522  		runFieldTest(t, spec, test)
   523  	}
   524  }
   525  
   526  /*
   527   * Test flag fields
   528   */
   529  
   530  type flagFieldSpec struct {
   531  	Bool        bool `flag:"b, bool" description:"A bool flag"`
   532  	Accumulator int  `flag:"a, acc" description:"An accumulator flag"`
   533  }
   534  
   535  var flagTests = []fieldTest{
   536  	// Bool flag
   537  	{Args: []string{}, Valid: true, Field: "Bool", Value: false},
   538  	{Args: []string{"-b"}, Valid: true, Field: "Bool", Value: true},
   539  	{Args: []string{"--bool"}, Valid: true, Field: "Bool", Value: true},
   540  	{Args: []string{"-b", "-b"}, Valid: false},
   541  	{Args: []string{"-b2"}, Valid: false},
   542  	{Args: []string{"--bool=2"}, Valid: false},
   543  
   544  	// Accumulator flag
   545  	{Args: []string{}, Valid: true, Field: "Accumulator", Value: 0},
   546  	{Args: []string{"-a"}, Valid: true, Field: "Accumulator", Value: 1},
   547  	{Args: []string{"-a", "-a"}, Valid: true, Field: "Accumulator", Value: 2},
   548  	{Args: []string{"-aaa"}, Valid: true, Field: "Accumulator", Value: 3},
   549  	{Args: []string{"--acc", "-a"}, Valid: true, Field: "Accumulator", Value: 2},
   550  	{Args: []string{"-a", "--acc", "-aa"}, Valid: true, Field: "Accumulator", Value: 4},
   551  	{Args: []string{"-a3"}, Valid: false},
   552  	{Args: []string{"--acc=3"}, Valid: false},
   553  }
   554  
   555  func TestFlagFields(t *testing.T) {
   556  	for _, test := range flagTests {
   557  		spec := &flagFieldSpec{}
   558  		runFieldTest(t, spec, test)
   559  	}
   560  }
   561  
   562  /*
   563   * Test map and slice field types
   564   */
   565  
   566  type mapSliceFieldSpec struct {
   567  	StringSlice []string          `option:"s" description:"A string slice option" placeholder:"STRINGSLICE"`
   568  	StringMap   map[string]string `option:"m" description:"A map of strings option" placeholder:"KEY=VALUE"`
   569  }
   570  
   571  var mapSliceFieldTests = []fieldTest{
   572  	// String Slice
   573  	{Args: []string{"-s", "1", "-s", "-1", "-s", "+1"}, Valid: true, Field: "StringSlice", Value: []string{"1", "-1", "+1"}},
   574  	{Args: []string{"-s", " a b", "-s", "\n", "-s", "\t"}, Valid: true, Field: "StringSlice", Value: []string{" a b", "\n", "\t"}},
   575  	{Args: []string{"-s", "日本", "-s", "-日本", "-s", "--日本"}, Valid: true, Field: "StringSlice", Value: []string{"日本", "-日本", "--日本"}},
   576  	{Args: []string{"-s", "1"}, Valid: true, Field: "StringSlice", Value: []string{"1"}},
   577  	{Args: []string{"-s", "-1"}, Valid: true, Field: "StringSlice", Value: []string{"-1"}},
   578  	{Args: []string{"-s", "+1"}, Valid: true, Field: "StringSlice", Value: []string{"+1"}},
   579  	{Args: []string{"-s", "1.0"}, Valid: true, Field: "StringSlice", Value: []string{"1.0"}},
   580  	{Args: []string{"-s", "0x01"}, Valid: true, Field: "StringSlice", Value: []string{"0x01"}},
   581  	{Args: []string{"-s", "-"}, Valid: true, Field: "StringSlice", Value: []string{"-"}},
   582  	{Args: []string{"-s", "-a"}, Valid: true, Field: "StringSlice", Value: []string{"-a"}},
   583  	{Args: []string{"-s", "--"}, Valid: true, Field: "StringSlice", Value: []string{"--"}},
   584  	{Args: []string{"-s", "--a"}, Valid: true, Field: "StringSlice", Value: []string{"--a"}},
   585  	{Args: []string{"-s", ""}, Valid: true, Field: "StringSlice", Value: []string{""}},
   586  	{Args: []string{"-s", " "}, Valid: true, Field: "StringSlice", Value: []string{" "}},
   587  	{Args: []string{"-s", " a"}, Valid: true, Field: "StringSlice", Value: []string{" a"}},
   588  	{Args: []string{"-s", "a "}, Valid: true, Field: "StringSlice", Value: []string{"a "}},
   589  	{Args: []string{"-s", "a b "}, Valid: true, Field: "StringSlice", Value: []string{"a b "}},
   590  	{Args: []string{"-s", " a b"}, Valid: true, Field: "StringSlice", Value: []string{" a b"}},
   591  	{Args: []string{"-s", "\n"}, Valid: true, Field: "StringSlice", Value: []string{"\n"}},
   592  	{Args: []string{"-s", "\t"}, Valid: true, Field: "StringSlice", Value: []string{"\t"}},
   593  	{Args: []string{"-s", "日本"}, Valid: true, Field: "StringSlice", Value: []string{"日本"}},
   594  	{Args: []string{"-s", "-日本"}, Valid: true, Field: "StringSlice", Value: []string{"-日本"}},
   595  	{Args: []string{"-s", "--日本"}, Valid: true, Field: "StringSlice", Value: []string{"--日本"}},
   596  	{Args: []string{"-s", " 日本"}, Valid: true, Field: "StringSlice", Value: []string{" 日本"}},
   597  	{Args: []string{"-s", "日本 "}, Valid: true, Field: "StringSlice", Value: []string{"日本 "}},
   598  	{Args: []string{"-s", "日 本"}, Valid: true, Field: "StringSlice", Value: []string{"日 本"}},
   599  	{Args: []string{"-s", "A relatively long string to make sure we aren't doing any silly truncation anywhere, since that would be bad..."}, Valid: true, Field: "StringSlice", Value: []string{"A relatively long string to make sure we aren't doing any silly truncation anywhere, since that would be bad..."}},
   600  	{Args: []string{"-s"}, Valid: false},
   601  
   602  	// String Map
   603  	{Args: []string{"-m", "a=b"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "b"}},
   604  	{Args: []string{"-m", "a=b=c"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "b=c"}},
   605  	{Args: []string{"-m", "a=b "}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "b "}},
   606  	{Args: []string{"-m", "a= b"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": " b"}},
   607  	{Args: []string{"-m", "a =b"}, Valid: true, Field: "StringMap", Value: map[string]string{"a ": "b"}},
   608  	{Args: []string{"-m", " a=b"}, Valid: true, Field: "StringMap", Value: map[string]string{" a": "b"}},
   609  	{Args: []string{"-m", " a=b "}, Valid: true, Field: "StringMap", Value: map[string]string{" a": "b "}},
   610  	{Args: []string{"-m", "a = b "}, Valid: true, Field: "StringMap", Value: map[string]string{"a ": " b "}},
   611  	{Args: []string{"-m", " a = b "}, Valid: true, Field: "StringMap", Value: map[string]string{" a ": " b "}},
   612  	{Args: []string{"-m", "a=b", "-m", "a=c"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "c"}},
   613  	{Args: []string{"-m", "a=b", "-m", "c=d"}, Valid: true, Field: "StringMap", Value: map[string]string{"a": "b", "c": "d"}},
   614  	{Args: []string{"-m", "日=本", "-m", "-日=本", "-m", "--日=--本"}, Valid: true, Field: "StringMap", Value: map[string]string{"日": "本", "-日": "本", "--日": "--本"}},
   615  	{Args: []string{"-m", "a", "=b"}, Valid: false},
   616  	{Args: []string{"-m", "a", "b"}, Valid: false},
   617  	{Args: []string{"-m", "foo"}, Valid: false},
   618  	{Args: []string{"-m", "a:b"}, Valid: false},
   619  	{Args: []string{"-m"}, Valid: false},
   620  }
   621  
   622  func TestMapSliceFields(t *testing.T) {
   623  	for _, test := range mapSliceFieldTests {
   624  		spec := &mapSliceFieldSpec{}
   625  		runFieldTest(t, spec, test)
   626  	}
   627  }
   628  
   629  /*
   630   * Test io field types
   631   */
   632  
   633  const ioTestText = "test IO"
   634  
   635  type ioFieldSpec struct {
   636  	Reader      io.Reader      `option:"reader" description:"An io.Reader input option"`
   637  	ReadCloser  io.ReadCloser  `option:"readcloser" description:"An io.ReadCloser input option"`
   638  	Writer      io.Writer      `option:"writer" description:"An io.Writer output option"`
   639  	WriteCloser io.WriteCloser `option:"writecloser" description:"An io.WriteCloser output option"`
   640  }
   641  
   642  func (r *ioFieldSpec) PerformIO() error {
   643  	input := []io.Reader{r.Reader, r.ReadCloser}
   644  	for _, in := range input {
   645  		if in != nil {
   646  			bytes, err := ioutil.ReadAll(in)
   647  			if err != nil {
   648  				return err
   649  			}
   650  			if string(bytes) != ioTestText {
   651  				return fmt.Errorf("Expected to read %q. Read %q instead.", ioTestText, string(bytes))
   652  			}
   653  			closer, ok := in.(io.Closer)
   654  			if ok {
   655  				err = closer.Close()
   656  				if err != nil {
   657  					return err
   658  				}
   659  			}
   660  		}
   661  	}
   662  	output := []io.Writer{r.Writer, r.WriteCloser}
   663  	for _, out := range output {
   664  		if out != nil {
   665  			_, err := io.WriteString(out, ioTestText)
   666  			if err != nil {
   667  				return err
   668  			}
   669  			closer, ok := out.(io.Closer)
   670  			if ok {
   671  				err = closer.Close()
   672  				if err != nil {
   673  					return err
   674  				}
   675  			}
   676  		}
   677  	}
   678  	return nil
   679  }
   680  
   681  type ioFieldTest struct {
   682  	Args       []string
   683  	Valid      bool
   684  	Field      string
   685  	InFiles    []string
   686  	OutFiles   []string
   687  	SkipReason string
   688  }
   689  
   690  var ioFieldTests = []ioFieldTest{
   691  	// No-op
   692  	{Args: []string{}, Valid: true, InFiles: []string{}, OutFiles: []string{}},
   693  
   694  	// io.Reader
   695  	{Args: []string{"--reader", "-"}, Valid: true, Field: "Reader", InFiles: []string{"stdin"}, OutFiles: []string{}},
   696  	{Args: []string{"--reader", "infile"}, Valid: true, Field: "Reader", InFiles: []string{"infile"}, OutFiles: []string{}},
   697  	{Args: []string{"--reader", "bogus/infile"}, Valid: false},
   698  	{Args: []string{"--reader", ""}, Valid: false},
   699  	{Args: []string{"--reader", "infile1", "--reader", "intput2"}, Valid: false},
   700  	{Args: []string{"--reader", "-", "--reader", "intput"}, Valid: false},
   701  	{Args: []string{"--reader", "infile", "--reader", "-"}, Valid: false},
   702  	{Args: []string{"--reader"}, Valid: false},
   703  
   704  	// io.ReadCloser
   705  	{Args: []string{"--readcloser", "-"}, Valid: true, Field: "ReadCloser", InFiles: []string{"stdin"}, OutFiles: []string{}},
   706  	{Args: []string{"--readcloser", "infile"}, Valid: true, Field: "ReadCloser", InFiles: []string{"infile"}, OutFiles: []string{}},
   707  	{Args: []string{"--readcloser", "bogus/infile"}, Valid: false},
   708  	{Args: []string{"--readcloser", ""}, Valid: false},
   709  	{Args: []string{"--readcloser", "infile1", "--readcloser", "intput2"}, Valid: false},
   710  	{Args: []string{"--readcloser", "-", "--readcloser", "intput"}, Valid: false},
   711  	{Args: []string{"--readcloser", "infile", "--readcloser", "-"}, Valid: false},
   712  	{Args: []string{"--readcloser"}, Valid: false},
   713  
   714  	// io.Writer
   715  	{Args: []string{"--writer", "-"}, Valid: true, Field: "Writer", InFiles: []string{}, OutFiles: []string{"stdout"}},
   716  	{Args: []string{"--writer", "outfile"}, Valid: true, Field: "Writer", InFiles: []string{}, OutFiles: []string{"outfile"}},
   717  	{Args: []string{"--writer", ""}, Valid: false},
   718  	{Args: []string{"--writer", "bogus/outfile"}, Valid: false},
   719  	{Args: []string{"--writer", "outfile1", "--writer", "outfile2"}, Valid: false},
   720  	{Args: []string{"--writer", "-", "--writer", "outfile"}, Valid: false},
   721  	{Args: []string{"--writer", "outfile", "--writer", "-"}, Valid: false},
   722  	{Args: []string{"--writer"}, Valid: false},
   723  
   724  	// io.WriteCloser
   725  	{Args: []string{"--writecloser", "-"}, Valid: true, Field: "WriteCloser", InFiles: []string{}, OutFiles: []string{"stdout"}},
   726  	{Args: []string{"--writecloser", "outfile"}, Valid: true, Field: "WriteCloser", InFiles: []string{}, OutFiles: []string{"outfile"}},
   727  	{Args: []string{"--writecloser", ""}, Valid: false},
   728  	{Args: []string{"--writecloser", "bogus/outfile"}, Valid: false},
   729  	{Args: []string{"--writecloser", "outfile1", "--writecloser", "outfile2"}, Valid: false},
   730  	{Args: []string{"--writecloser", "-", "--writecloser", "outfile"}, Valid: false},
   731  	{Args: []string{"--writecloser", "outfile", "--writecloser", "-"}, Valid: false},
   732  	{Args: []string{"--writecloser"}, Valid: false},
   733  }
   734  
   735  func TestIOFields(t *testing.T) {
   736  	for _, test := range ioFieldTests {
   737  		spec := &ioFieldSpec{}
   738  		runIOFieldTest(t, spec, test)
   739  	}
   740  }
   741  
   742  func runIOFieldTest(t *testing.T, spec *ioFieldSpec, test ioFieldTest) {
   743  	if test.SkipReason != "" {
   744  		t.Logf("Test skipped. Args: %q, Field: %s, Reason: %s", test.Args, test.Field, test.SkipReason)
   745  		return
   746  	}
   747  
   748  	realin, realout := os.Stdin, os.Stdout
   749  	defer restoreStdinStdout(realin, realout)
   750  
   751  	realdir, err := os.Getwd()
   752  	if err != nil {
   753  		t.Errorf("Failed to get working dir. Args: %q, Field: %s, Error: %s", test.Args, test.Field, err)
   754  		return
   755  	}
   756  	defer restoreWorkingDir(realdir)
   757  
   758  	err = setupIOFieldTest(test)
   759  	if err != nil {
   760  		t.Errorf("Failed to setup test. Args: %q, Field: %s, Error: %s", test.Args, test.Field, err)
   761  		return
   762  	}
   763  
   764  	cmd := New("test", spec)
   765  	_, _, err = cmd.Decode(test.Args)
   766  	if !test.Valid {
   767  		if err == nil {
   768  			t.Errorf("Expected error but none received. Args: %q", test.Args)
   769  		}
   770  		return
   771  	}
   772  	if err != nil {
   773  		t.Errorf("Received unexpected decode error. Args: %q, Field: %s, Error: %s", test.Args, test.Field, err)
   774  		return
   775  	}
   776  
   777  	err = validateIOFieldTest(spec, test)
   778  	if err != nil {
   779  		t.Errorf("Validation failed during IO field test. Args: %q, Field: %s, Error: %s", test.Args, test.Field, err)
   780  		return
   781  	}
   782  }
   783  
   784  func restoreStdinStdout(stdin *os.File, stdout *os.File) {
   785  	os.Stdin = stdin
   786  	os.Stdout = stdout
   787  }
   788  
   789  func restoreWorkingDir(dir string) {
   790  	err := os.Chdir(dir)
   791  	if err != nil {
   792  		panic(fmt.Sprintf("Failed to restore working dir. Dir: %q, Error: %s", dir, err))
   793  	}
   794  }
   795  
   796  func setupIOFieldTest(test ioFieldTest) error {
   797  	tmpdir, err := ioutil.TempDir("", "writ-iofieldtest")
   798  	if err != nil {
   799  		return err
   800  	}
   801  	err = os.Chdir(tmpdir)
   802  	if err != nil {
   803  		return err
   804  	}
   805  
   806  	for _, name := range test.InFiles {
   807  		f, err := os.Create(name)
   808  		if err != nil {
   809  			return err
   810  		}
   811  		_, err = io.WriteString(f, ioTestText)
   812  		if err != nil {
   813  			return err
   814  		}
   815  		err = f.Close()
   816  		if err != nil {
   817  			return err
   818  		}
   819  		if name == "stdin" {
   820  			in, err := os.Open(name)
   821  			if err != nil {
   822  				return err
   823  			}
   824  			os.Stdin = in
   825  		}
   826  	}
   827  	for _, name := range test.OutFiles {
   828  		if name == "stdout" {
   829  			out, err := os.Create(name)
   830  			if err != nil {
   831  				return err
   832  			}
   833  			os.Stdout = out
   834  		}
   835  	}
   836  	return nil
   837  }
   838  
   839  func validateIOFieldTest(spec *ioFieldSpec, test ioFieldTest) error {
   840  	err := spec.PerformIO()
   841  	if err != nil {
   842  		return err
   843  	}
   844  	for _, name := range test.OutFiles {
   845  		in, err := os.Open(name)
   846  		if err != nil {
   847  			return err
   848  		}
   849  		bytes, err := ioutil.ReadAll(in)
   850  		if err != nil {
   851  			return err
   852  		}
   853  		if string(bytes) != ioTestText {
   854  			return fmt.Errorf("Expected to read %q. Read %q instead.", ioTestText, string(bytes))
   855  		}
   856  		err = in.Close()
   857  		if err != nil {
   858  			return err
   859  		}
   860  	}
   861  	return nil
   862  }
   863  
   864  /*
   865   * Test custom flag and option decoders
   866   */
   867  
   868  type customTestFlag struct {
   869  	val *bool
   870  }
   871  
   872  func (d customTestFlag) Decode(arg string) error {
   873  	*d.val = true
   874  	return nil
   875  }
   876  
   877  type customTestFlagPtr struct {
   878  	val bool
   879  }
   880  
   881  func (d *customTestFlagPtr) Decode(arg string) error {
   882  	d.val = true
   883  	return nil
   884  }
   885  
   886  type customTestOption struct {
   887  	val *string
   888  }
   889  
   890  func (d customTestOption) Decode(arg string) error {
   891  	if strings.HasPrefix(arg, "foo") {
   892  		*d.val = arg
   893  		return nil
   894  	}
   895  	return fmt.Errorf("customTestOption values must begin with foo")
   896  }
   897  
   898  type customTestOptionPtr struct {
   899  	val string
   900  }
   901  
   902  func (d *customTestOptionPtr) Decode(arg string) error {
   903  	if strings.HasPrefix(arg, "foo") {
   904  		d.val = arg
   905  		return nil
   906  	}
   907  	return fmt.Errorf("customTestOptionPtr values must begin with foo")
   908  }
   909  
   910  type customDecoderFieldSpec struct {
   911  	CustomFlag      customTestFlag      `flag:"flag" description:"a custom flag field"`
   912  	CustomFlagPtr   customTestFlagPtr   `flag:"flagptr" description:"a custom flag field with pointer receiver"`
   913  	CustomOption    customTestOption    `option:"opt" description:"a custom option field"`
   914  	CustomOptionPtr customTestOptionPtr `option:"optptr" description:"a custom option field with pointer receiver"`
   915  }
   916  
   917  var trueval = true
   918  var foobarval = "foobar"
   919  
   920  var customDecoderFieldTests = []fieldTest{
   921  	// Custom flag
   922  	{Args: []string{"--flag"}, Valid: true, Field: "CustomFlag", Value: customTestFlag{val: &trueval}},
   923  	{Args: []string{"--flag", "--flag"}, Valid: false}, // Plural must be set explicitly
   924  
   925  	// Custom flag with pointer receiver
   926  	{Args: []string{"--flagptr"}, Valid: true, Field: "CustomFlagPtr", Value: customTestFlagPtr{val: true}},
   927  	{Args: []string{"--flagptr", "--flagptr"}, Valid: false}, // Plural must be set explicitly
   928  
   929  	// Custom option
   930  	{Args: []string{"--opt", "foobar"}, Valid: true, Field: "CustomOption", Value: customTestOption{val: &foobarval}},
   931  	{Args: []string{"--opt=foobar"}, Valid: true, Field: "CustomOption", Value: customTestOption{val: &foobarval}},
   932  	{Args: []string{"-opt=puppies"}, Valid: false},
   933  	{Args: []string{"-opt", "puppies"}, Valid: false},
   934  	{Args: []string{"--opt"}, Valid: false},
   935  	{Args: []string{"--opt", "foobar", "-opt", "foobar"}, Valid: false}, // Plural must be set explicitly
   936  
   937  	// Custom option with pointer receiver
   938  	{Args: []string{"--optptr", "foobar"}, Valid: true, Field: "CustomOptionPtr", Value: customTestOptionPtr{val: "foobar"}},
   939  	{Args: []string{"--optptr=foobar"}, Valid: true, Field: "CustomOptionPtr", Value: customTestOptionPtr{val: "foobar"}},
   940  	{Args: []string{"-optptr=puppies"}, Valid: false},
   941  	{Args: []string{"-optptr", "puppies"}, Valid: false},
   942  	{Args: []string{"--optptr"}, Valid: false},
   943  	{Args: []string{"--optptr", "foobar", "-optptr", "foobar"}, Valid: false}, // Plural must be set explicitly
   944  }
   945  
   946  func TestCustomDecoderFields(t *testing.T) {
   947  	for _, test := range customDecoderFieldTests {
   948  		var flagval bool
   949  		var optval string
   950  		spec := &customDecoderFieldSpec{
   951  			CustomFlag:   customTestFlag{&flagval},
   952  			CustomOption: customTestOption{&optval},
   953  		}
   954  		runFieldTest(t, spec, test)
   955  	}
   956  }
   957  
   958  /*
   959   * Test basic field types
   960   */
   961  
   962  type basicFieldSpec struct {
   963  	Int     int     `option:"int" description:"An int option" placeholder:"INT"`
   964  	Int8    int8    `option:"int8" description:"An int8 option" placeholder:"INT8"`
   965  	Int16   int16   `option:"int16" description:"An int16 option" placeholder:"INT16"`
   966  	Int32   int32   `option:"int32" description:"An int32 option" placeholder:"INT32"`
   967  	Int64   int64   `option:"int64" description:"An int64 option" placeholder:"INT64"`
   968  	Uint    uint    `option:"uint" description:"A uint option" placeholder:"UINT"`
   969  	Uint8   uint8   `option:"uint8" description:"A uint8 option" placeholder:"UINT8"`
   970  	Uint16  uint16  `option:"uint16" description:"A uint16 option" placeholder:"UINT16"`
   971  	Uint32  uint32  `option:"uint32" description:"A uint32 option" placeholder:"UINT32"`
   972  	Uint64  uint64  `option:"uint64" description:"A uint64 option" placeholder:"UINT64"`
   973  	Float32 float32 `option:"float32" description:"A float32 option" placeholder:"FLOAT32"`
   974  	Float64 float64 `option:"float64" description:"A float64 option" placeholder:"FLOAT64"`
   975  	String  string  `option:"string" description:"A string option" placeholder:"STRING"`
   976  }
   977  
   978  var basicFieldTests = []fieldTest{
   979  	// String
   980  	{Args: []string{"--string", "1"}, Valid: true, Field: "String", Value: "1"},
   981  	{Args: []string{"--string", "-1"}, Valid: true, Field: "String", Value: "-1"},
   982  	{Args: []string{"--string", "+1"}, Valid: true, Field: "String", Value: "+1"},
   983  	{Args: []string{"--string", "1.0"}, Valid: true, Field: "String", Value: "1.0"},
   984  	{Args: []string{"--string", "0x01"}, Valid: true, Field: "String", Value: "0x01"},
   985  	{Args: []string{"--string", "-"}, Valid: true, Field: "String", Value: "-"},
   986  	{Args: []string{"--string", "-a"}, Valid: true, Field: "String", Value: "-a"},
   987  	{Args: []string{"--string", "--"}, Valid: true, Field: "String", Value: "--"},
   988  	{Args: []string{"--string", "--a"}, Valid: true, Field: "String", Value: "--a"},
   989  	{Args: []string{"--string", ""}, Valid: true, Field: "String", Value: ""},
   990  	{Args: []string{"--string", " "}, Valid: true, Field: "String", Value: " "},
   991  	{Args: []string{"--string", " a"}, Valid: true, Field: "String", Value: " a"},
   992  	{Args: []string{"--string", "a "}, Valid: true, Field: "String", Value: "a "},
   993  	{Args: []string{"--string", "a b "}, Valid: true, Field: "String", Value: "a b "},
   994  	{Args: []string{"--string", " a b"}, Valid: true, Field: "String", Value: " a b"},
   995  	{Args: []string{"--string", "\n"}, Valid: true, Field: "String", Value: "\n"},
   996  	{Args: []string{"--string", "\t"}, Valid: true, Field: "String", Value: "\t"},
   997  	{Args: []string{"--string", "日本"}, Valid: true, Field: "String", Value: "日本"},
   998  	{Args: []string{"--string", "-日本"}, Valid: true, Field: "String", Value: "-日本"},
   999  	{Args: []string{"--string", "--日本"}, Valid: true, Field: "String", Value: "--日本"},
  1000  	{Args: []string{"--string", " 日本"}, Valid: true, Field: "String", Value: " 日本"},
  1001  	{Args: []string{"--string", "日本 "}, Valid: true, Field: "String", Value: "日本 "},
  1002  	{Args: []string{"--string", "日 本"}, Valid: true, Field: "String", Value: "日 本"},
  1003  	{Args: []string{"--string", "A relatively long string to make sure we aren't doing any silly truncation anywhere, since that would be bad..."}, Valid: true, Field: "String", Value: "A relatively long string to make sure we aren't doing any silly truncation anywhere, since that would be bad..."},
  1004  	{Args: []string{"--string", "a", "--string", "b"}, Valid: false},
  1005  	{Args: []string{"--string"}, Valid: false},
  1006  
  1007  	// Int8
  1008  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int8", Value: int8(math.MinInt8)},
  1009  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int8", Value: int8(math.MaxInt8)},
  1010  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false},
  1011  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: false},
  1012  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false},
  1013  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false},
  1014  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: false},
  1015  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: false},
  1016  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false},
  1017  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1018  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: false},
  1019  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false},
  1020  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false},
  1021  	{Args: []string{"--int8", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false},
  1022  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false},
  1023  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: false},
  1024  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: false},
  1025  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: false},
  1026  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: false},
  1027  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false},
  1028  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false},
  1029  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1030  	{Args: []string{"--int8", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1031  	{Args: []string{"--int8", "1", "--int8", "2"}, Valid: false},
  1032  	{Args: []string{"--int8", "1.0"}, Valid: false},
  1033  	{Args: []string{"--int8", ""}, Valid: false},
  1034  	{Args: []string{"--int8"}, Valid: false},
  1035  
  1036  	// Int16
  1037  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int16", Value: int16(math.MinInt8)},
  1038  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: true, Field: "Int16", Value: int16(math.MinInt8 - 1)},
  1039  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int16", Value: int16(math.MaxInt8)},
  1040  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Int16", Value: int16(math.MaxInt8 + 1)},
  1041  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: true, Field: "Int16", Value: int16(math.MinInt16)},
  1042  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Int16", Value: int16(math.MaxInt16)},
  1043  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Int16", Value: int16(math.MaxUint8)},
  1044  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Int16", Value: int16(math.MaxUint8 + 1)},
  1045  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false},
  1046  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: false},
  1047  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false},
  1048  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1049  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: false},
  1050  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false},
  1051  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false},
  1052  	{Args: []string{"--int16", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false},
  1053  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false},
  1054  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: false},
  1055  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: false},
  1056  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false},
  1057  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false},
  1058  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1059  	{Args: []string{"--int16", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1060  	{Args: []string{"--int16", "1", "--int16", "2"}, Valid: false},
  1061  	{Args: []string{"--int16", "1.0"}, Valid: false},
  1062  	{Args: []string{"--int16", ""}, Valid: false},
  1063  	{Args: []string{"--int16"}, Valid: false},
  1064  
  1065  	// Int32
  1066  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int32", Value: int32(math.MinInt8)},
  1067  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: true, Field: "Int32", Value: int32(math.MinInt8 - 1)},
  1068  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt8)},
  1069  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt8 + 1)},
  1070  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: true, Field: "Int32", Value: int32(math.MinInt16)},
  1071  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: true, Field: "Int32", Value: int32(math.MinInt16 - 1)},
  1072  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt16)},
  1073  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt16 + 1)},
  1074  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: true, Field: "Int32", Value: int32(math.MinInt32)},
  1075  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Int32", Value: int32(math.MaxInt32)},
  1076  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Int32", Value: int32(math.MaxUint8)},
  1077  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Int32", Value: int32(math.MaxUint8 + 1)},
  1078  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Int32", Value: int32(math.MaxUint16)},
  1079  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Int32", Value: int32(math.MaxUint16 + 1)},
  1080  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1081  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false},
  1082  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false},
  1083  	{Args: []string{"--int32", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false},
  1084  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false},
  1085  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false},
  1086  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false},
  1087  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1088  	{Args: []string{"--int32", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1089  	{Args: []string{"--int32", "1", "--int32", "2"}, Valid: false},
  1090  	{Args: []string{"--int32", "1.0"}, Valid: false},
  1091  	{Args: []string{"--int32", ""}, Valid: false},
  1092  	{Args: []string{"--int32"}, Valid: false},
  1093  
  1094  	// Int64
  1095  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int64", Value: int64(math.MinInt8)},
  1096  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: true, Field: "Int64", Value: int64(math.MinInt8 - 1)},
  1097  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt8)},
  1098  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt8 + 1)},
  1099  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: true, Field: "Int64", Value: int64(math.MinInt16)},
  1100  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: true, Field: "Int64", Value: int64(math.MinInt16 - 1)},
  1101  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt16)},
  1102  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt16 + 1)},
  1103  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: true, Field: "Int64", Value: int64(math.MinInt32)},
  1104  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: true, Field: "Int64", Value: int64(math.MinInt32 - 1)},
  1105  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt32)},
  1106  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt32 + 1)},
  1107  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: true, Field: "Int64", Value: int64(math.MinInt64)},
  1108  	{Args: []string{"--int64", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: true, Field: "Int64", Value: int64(math.MaxInt64)},
  1109  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint8)},
  1110  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint8 + 1)},
  1111  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint16)},
  1112  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint16 + 1)},
  1113  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint32)},
  1114  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: true, Field: "Int64", Value: int64(math.MaxUint32 + 1)},
  1115  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false},
  1116  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1117  	{Args: []string{"--int64", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1118  	{Args: []string{"--int64", "1", "--int64", "2"}, Valid: false},
  1119  	{Args: []string{"--int64", "1.0"}, Valid: false},
  1120  	{Args: []string{"--int64", ""}, Valid: false},
  1121  	{Args: []string{"--int64"}, Valid: false},
  1122  
  1123  	// Int
  1124  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: true, Field: "Int", Value: int(math.MinInt8)},
  1125  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: true, Field: "Int", Value: int(math.MinInt8 - 1)},
  1126  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Int", Value: int(math.MaxInt8)},
  1127  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Int", Value: int(math.MaxInt8 + 1)},
  1128  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: true, Field: "Int", Value: int(math.MinInt16)},
  1129  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: true, Field: "Int", Value: int(math.MinInt16 - 1)},
  1130  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Int", Value: int(math.MaxInt16)},
  1131  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Int", Value: int(math.MaxInt16 + 1)},
  1132  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: true, Field: "Int", Value: int(math.MinInt32)},
  1133  	{Args: []string{"--int", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Int", Value: int(math.MaxInt32)},
  1134  	{Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Int", Value: int(math.MaxUint8)},
  1135  	{Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Int", Value: int(math.MaxUint8 + 1)},
  1136  	{Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Int", Value: int(math.MaxUint16)},
  1137  	{Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Int", Value: int(math.MaxUint16 + 1)},
  1138  	{Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false},
  1139  	{Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1140  	{Args: []string{"--int", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1141  	{Args: []string{"--int", "1", "--int", "2"}, Valid: false},
  1142  	{Args: []string{"--int", "1.0"}, Valid: false},
  1143  	{Args: []string{"--int", ""}, Valid: false},
  1144  	{Args: []string{"--int"}, Valid: false},
  1145  
  1146  	// Uint8
  1147  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint8", Value: uint8(math.MaxInt8)},
  1148  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint8", Value: uint8(math.MaxInt8 + 1)},
  1149  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint8", Value: uint8(math.MaxUint8)},
  1150  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false},
  1151  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false},
  1152  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false},
  1153  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false},
  1154  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: false},
  1155  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: false},
  1156  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false},
  1157  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1158  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: false},
  1159  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false},
  1160  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false},
  1161  	{Args: []string{"--uint8", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false},
  1162  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false},
  1163  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: false},
  1164  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: false},
  1165  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: false},
  1166  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false},
  1167  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false},
  1168  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1169  	{Args: []string{"--uint8", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1170  	{Args: []string{"--uint8", "1", "--uint8", "2"}, Valid: false},
  1171  	{Args: []string{"--uint8", "1.0"}, Valid: false},
  1172  	{Args: []string{"--uint8", ""}, Valid: false},
  1173  	{Args: []string{"--uint8"}, Valid: false},
  1174  
  1175  	// Uint16
  1176  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxInt8)},
  1177  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxInt8 + 1)},
  1178  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxInt16)},
  1179  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxInt16 + 1)},
  1180  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxUint8)},
  1181  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxUint8 + 1)},
  1182  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Uint16", Value: uint16(math.MaxUint16)},
  1183  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false},
  1184  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false},
  1185  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false},
  1186  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false},
  1187  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false},
  1188  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1189  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: false},
  1190  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: false},
  1191  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false},
  1192  	{Args: []string{"--uint16", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false},
  1193  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false},
  1194  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: false},
  1195  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: false},
  1196  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false},
  1197  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1198  	{Args: []string{"--uint16", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1199  	{Args: []string{"--uint16", "1", "--uint16", "2"}, Valid: false},
  1200  	{Args: []string{"--uint16", "1.0"}, Valid: false},
  1201  	{Args: []string{"--uint16", ""}, Valid: false},
  1202  	{Args: []string{"--uint16"}, Valid: false},
  1203  
  1204  	// Uint32
  1205  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt8)},
  1206  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt8 + 1)},
  1207  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt16)},
  1208  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt16 + 1)},
  1209  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt32)},
  1210  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxInt32 + 1)},
  1211  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint8)},
  1212  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint8 + 1)},
  1213  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint16)},
  1214  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint16 + 1)},
  1215  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: true, Field: "Uint32", Value: uint32(math.MaxUint32)},
  1216  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false},
  1217  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false},
  1218  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false},
  1219  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false},
  1220  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false},
  1221  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1222  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false},
  1223  	{Args: []string{"--uint32", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: false},
  1224  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: false},
  1225  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: false},
  1226  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1227  	{Args: []string{"--uint32", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: false},
  1228  	{Args: []string{"--uint32", "1", "--uint32", "1"}, Valid: false},
  1229  	{Args: []string{"--uint32", "1.0"}, Valid: false},
  1230  	{Args: []string{"--uint32", ""}, Valid: false},
  1231  	{Args: []string{"--uint32"}, Valid: false},
  1232  
  1233  	// Uint64
  1234  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt8)},
  1235  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt8 + 1)},
  1236  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt16)},
  1237  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt16 + 1)},
  1238  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt32)},
  1239  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt32 + 1)},
  1240  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MaxInt64))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt64)},
  1241  	{Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxInt64+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxInt64 + 1)},
  1242  	{Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint8)},
  1243  	{Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint8 + 1)},
  1244  	{Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint16)},
  1245  	{Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint16 + 1)},
  1246  	{Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint32)},
  1247  	{Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint32+1))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint32 + 1)},
  1248  	{Args: []string{"--uint64", fmt.Sprintf("%d", uint64(math.MaxUint64))}, Valid: true, Field: "Uint64", Value: uint64(math.MaxUint64)},
  1249  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false},
  1250  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false},
  1251  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false},
  1252  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false},
  1253  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false},
  1254  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1255  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false},
  1256  	{Args: []string{"--uint64", fmt.Sprintf("%d", int64(math.MinInt64))}, Valid: false},
  1257  	{Args: []string{"--uint64", "1", "--uint64", "1"}, Valid: false},
  1258  	{Args: []string{"--uint64", "1.0"}, Valid: false},
  1259  	{Args: []string{"--uint64", ""}, Valid: false},
  1260  	{Args: []string{"--uint64"}, Valid: false},
  1261  
  1262  	// Uint
  1263  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt8))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt8)},
  1264  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt8+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt8 + 1)},
  1265  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt16))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt16)},
  1266  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt16+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt16 + 1)},
  1267  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt32))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt32)},
  1268  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MaxInt32+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxInt32 + 1)},
  1269  	{Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint8))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint8)},
  1270  	{Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint8+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint8 + 1)},
  1271  	{Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint16))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint16)},
  1272  	{Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint16+1))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint16 + 1)},
  1273  	{Args: []string{"--uint", fmt.Sprintf("%d", uint64(math.MaxUint32))}, Valid: true, Field: "Uint", Value: uint(math.MaxUint32)},
  1274  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt8))}, Valid: false},
  1275  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt8-1))}, Valid: false},
  1276  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt16))}, Valid: false},
  1277  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt16-1))}, Valid: false},
  1278  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt32))}, Valid: false},
  1279  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1280  	{Args: []string{"--uint", fmt.Sprintf("%d", int64(math.MinInt32-1))}, Valid: false},
  1281  	{Args: []string{"--uint", "1", "--uint", "2"}, Valid: false},
  1282  	{Args: []string{"--uint", "1.0"}, Valid: false},
  1283  	{Args: []string{"--uint", ""}, Valid: false},
  1284  	{Args: []string{"--uint"}, Valid: false},
  1285  
  1286  	// Float32
  1287  	{Args: []string{"--float32", "-1.23"}, Valid: true, Field: "Float32", Value: float32(-1.23)},
  1288  	{Args: []string{"--float32", "4.56"}, Valid: true, Field: "Float32", Value: float32(4.56)},
  1289  	{Args: []string{"--float32", "-1.2e3"}, Valid: true, Field: "Float32", Value: float32(-1.2e3)},
  1290  	{Args: []string{"--float32", "4.5e6"}, Valid: true, Field: "Float32", Value: float32(4.5e6)},
  1291  	{Args: []string{"--float32", "-1.2E3"}, Valid: true, Field: "Float32", Value: float32(-1.2e3)},
  1292  	{Args: []string{"--float32", "4.5E6"}, Valid: true, Field: "Float32", Value: float32(4.5e6)},
  1293  	{Args: []string{"--float32", "-1.2e+3"}, Valid: true, Field: "Float32", Value: float32(-1.2e3)},
  1294  	{Args: []string{"--float32", "4.5e+6"}, Valid: true, Field: "Float32", Value: float32(4.5e6)},
  1295  	{Args: []string{"--float32", "-1.2E+3"}, Valid: true, Field: "Float32", Value: float32(-1.2e3)},
  1296  	{Args: []string{"--float32", "4.5E+6"}, Valid: true, Field: "Float32", Value: float32(4.5e6)},
  1297  	{Args: []string{"--float32", "-1.2e-3"}, Valid: true, Field: "Float32", Value: float32(-1.2e-3)},
  1298  	{Args: []string{"--float32", "4.5e-6"}, Valid: true, Field: "Float32", Value: float32(4.5e-6)},
  1299  	{Args: []string{"--float32", "-1.2E-3"}, Valid: true, Field: "Float32", Value: float32(-1.2e-3)},
  1300  	{Args: []string{"--float32", "4.5E-6"}, Valid: true, Field: "Float32", Value: float32(4.5e-6)},
  1301  	{Args: []string{"--float32", strconv.FormatFloat(math.SmallestNonzeroFloat32, 'f', -1, 64)}, Valid: true, Field: "Float32", Value: float32(math.SmallestNonzeroFloat32)},
  1302  	{Args: []string{"--float32", strconv.FormatFloat(math.MaxFloat32, 'f', -1, 64)}, Valid: true, Field: "Float32", Value: float32(math.MaxFloat32)},
  1303  	{Args: []string{"--float32", strconv.FormatFloat(math.MaxFloat32, 'f', -1, 64)}, Valid: true, Field: "Float32", Value: float32(math.MaxFloat32)},
  1304  	// XXX Skipped -- Not sure how to handle this!!
  1305  	{Args: []string{"--float32", strconv.FormatFloat(math.SmallestNonzeroFloat64, 'f', -1, 64)}, Field: "Float32", SkipReason: "Not sure how to handle the precision on this"},
  1306  	{Args: []string{"--float32", strconv.FormatFloat(math.MaxFloat64, 'f', -1, 64)}, Valid: false},
  1307  	{Args: []string{"--float32", strconv.FormatFloat(math.MaxFloat64, 'f', -1, 64)}, Valid: false},
  1308  	{Args: []string{"--float32", "1"}, Valid: true, Field: "Float32", Value: float32(1)},
  1309  	{Args: []string{"--float32", "-1"}, Valid: true, Field: "Float32", Value: float32(-1)},
  1310  	{Args: []string{"--float32", "1.0", "--float32", "2.0"}, Valid: false},
  1311  	{Args: []string{"--float32", ""}, Valid: false},
  1312  	{Args: []string{"--float32"}, Valid: false},
  1313  
  1314  	// Float64
  1315  	{Args: []string{"--float64", "-1.23"}, Valid: true, Field: "Float64", Value: float64(-1.23)},
  1316  	{Args: []string{"--float64", "4.56"}, Valid: true, Field: "Float64", Value: float64(4.56)},
  1317  	{Args: []string{"--float64", "-1.2e3"}, Valid: true, Field: "Float64", Value: float64(-1.2e3)},
  1318  	{Args: []string{"--float64", "4.5e6"}, Valid: true, Field: "Float64", Value: float64(4.5e6)},
  1319  	{Args: []string{"--float64", "-1.2E3"}, Valid: true, Field: "Float64", Value: float64(-1.2e3)},
  1320  	{Args: []string{"--float64", "4.5E6"}, Valid: true, Field: "Float64", Value: float64(4.5e6)},
  1321  	{Args: []string{"--float64", "-1.2e+3"}, Valid: true, Field: "Float64", Value: float64(-1.2e3)},
  1322  	{Args: []string{"--float64", "4.5e+6"}, Valid: true, Field: "Float64", Value: float64(4.5e6)},
  1323  	{Args: []string{"--float64", "-1.2E+3"}, Valid: true, Field: "Float64", Value: float64(-1.2e3)},
  1324  	{Args: []string{"--float64", "4.5E+6"}, Valid: true, Field: "Float64", Value: float64(4.5e6)},
  1325  	{Args: []string{"--float64", "-1.2e-3"}, Valid: true, Field: "Float64", Value: float64(-1.2e-3)},
  1326  	{Args: []string{"--float64", "4.5e-6"}, Valid: true, Field: "Float64", Value: float64(4.5e-6)},
  1327  	{Args: []string{"--float64", "-1.2E-3"}, Valid: true, Field: "Float64", Value: float64(-1.2e-3)},
  1328  	{Args: []string{"--float64", "4.5E-6"}, Valid: true, Field: "Float64", Value: float64(4.5e-6)},
  1329  	{Args: []string{"--float64", strconv.FormatFloat(math.SmallestNonzeroFloat32, 'f', -1, 64)}, Valid: true, Field: "Float64", Value: float64(math.SmallestNonzeroFloat32)},
  1330  	{Args: []string{"--float64", strconv.FormatFloat(math.MaxFloat32, 'f', -1, 64)}, Valid: true, Field: "Float64", Value: float64(math.MaxFloat32)},
  1331  	{Args: []string{"--float64", strconv.FormatFloat(math.SmallestNonzeroFloat64, 'f', -1, 64)}, Valid: true, Field: "Float64", Value: float64(math.SmallestNonzeroFloat64)},
  1332  	{Args: []string{"--float64", strconv.FormatFloat(math.MaxFloat64, 'f', -1, 64)}, Valid: true, Field: "Float64", Value: float64(math.MaxFloat64)},
  1333  	{Args: []string{"--float64", "1"}, Valid: true, Field: "Float64", Value: float64(1)},
  1334  	{Args: []string{"--float64", "-1"}, Valid: true, Field: "Float64", Value: float64(-1)},
  1335  	{Args: []string{"--float64", "1.0", "--float64", "2.0"}, Valid: false},
  1336  	{Args: []string{"--float64", ""}, Valid: false},
  1337  	{Args: []string{"--float64"}, Valid: false},
  1338  }
  1339  
  1340  func TestBasicFields(t *testing.T) {
  1341  	for _, test := range basicFieldTests {
  1342  		spec := &basicFieldSpec{}
  1343  		runFieldTest(t, spec, test)
  1344  	}
  1345  }
  1346  
  1347  /*
  1348   * Test invalid specs
  1349   */
  1350  
  1351  var invalidSpecTests = []struct {
  1352  	Description string
  1353  	Spec        interface{}
  1354  }{
  1355  	// Invalid command specs
  1356  	{
  1357  		Description: "Commands must have a name 1",
  1358  		Spec: &struct {
  1359  			Command struct{} `command:","`
  1360  		}{},
  1361  	},
  1362  	{
  1363  		Description: "Commands must have a name 2",
  1364  		Spec: &struct {
  1365  			Command struct{} `command:" "`
  1366  		}{},
  1367  	},
  1368  	{
  1369  		Description: "Commands must have a single name",
  1370  		Spec: &struct {
  1371  			Command struct{} `command:"one,two"`
  1372  		}{},
  1373  	},
  1374  	{
  1375  		Description: "Command names cannot have a leading '-' prefix",
  1376  		Spec: &struct {
  1377  			Command struct{} `command:"-command"`
  1378  		}{},
  1379  	},
  1380  	{
  1381  		Description: "Command aliases cannot have a leading '-' prefix",
  1382  		Spec: &struct {
  1383  			Command struct{} `command:"command" alias:"-alias"`
  1384  		}{},
  1385  	},
  1386  	{
  1387  		Description: "Commands cannot have placeholders",
  1388  		Spec: &struct {
  1389  			Command struct{} `command:"command" placeholder:"PLACEHOLDER"`
  1390  		}{},
  1391  	},
  1392  	{
  1393  		Description: "Commands cannot have default values",
  1394  		Spec: &struct {
  1395  			Command struct{} `command:"command" default:"default"`
  1396  		}{},
  1397  	},
  1398  	{
  1399  		Description: "Commands cannot have env values",
  1400  		Spec: &struct {
  1401  			Command struct{} `command:"command" env:"ENV_VALUE"`
  1402  		}{},
  1403  	},
  1404  	{
  1405  		Description: "Command fields must be exported",
  1406  		Spec: &struct {
  1407  			command struct{} `command:"command"`
  1408  		}{},
  1409  	},
  1410  	{
  1411  		Description: "Command and alias names must be unique 1",
  1412  		Spec: &struct {
  1413  			Command struct{} `command:"foo" alias:"foo"`
  1414  		}{},
  1415  	},
  1416  	{
  1417  		Description: "Command and alias names must be unique 2",
  1418  		Spec: &struct {
  1419  			Command1 struct{} `command:"foo"`
  1420  			Command2 struct{} `command:"foo"`
  1421  		}{},
  1422  	},
  1423  	{
  1424  		Description: "Command and alias names must be unique 3",
  1425  		Spec: &struct {
  1426  			Command1 struct{} `command:"foo"`
  1427  			Command2 struct{} `command:"b" alias:"foo"`
  1428  		}{},
  1429  	},
  1430  	{
  1431  		Description: "Command and alias names must be unique 4",
  1432  		Spec: &struct {
  1433  			Command1 struct{} `command:"a" alias:"foo"`
  1434  			Command2 struct{} `command:"b" alias:"foo"`
  1435  		}{},
  1436  	},
  1437  	{
  1438  		Description: "Command specs must be a pointer to struct 1",
  1439  		Spec:        struct{}{},
  1440  	},
  1441  	{
  1442  		Description: "Command specs must be a pointer to struct 2",
  1443  		Spec:        42,
  1444  	},
  1445  	{
  1446  		Description: "Command specs must be a pointer to struct 3",
  1447  		Spec:        (*int)(nil),
  1448  	},
  1449  
  1450  	// Invalid option specs
  1451  	{
  1452  		Description: "Options cannot have aliases",
  1453  		Spec: &struct {
  1454  			Option int `option:"option" alias:"alias" description:"option with an alias"`
  1455  		}{},
  1456  	},
  1457  	{
  1458  		Description: "Options must have a name 1",
  1459  		Spec: &struct {
  1460  			Option int `option:"," description:"option with no name"`
  1461  		}{},
  1462  	},
  1463  	{
  1464  		Description: "Options must have a name 2",
  1465  		Spec: &struct {
  1466  			Option int `option:" " description:"option with no name"`
  1467  		}{},
  1468  	},
  1469  	{
  1470  		Description: "Long option names cannot have a leading '-' prefix",
  1471  		Spec: &struct {
  1472  			Option int `option:"-option" description:"leading dash prefix"`
  1473  		}{},
  1474  	},
  1475  	{
  1476  		Description: "Short option names cannot have a leading '-' prefix",
  1477  		Spec: &struct {
  1478  			Option int `option:"-o" description:"leading dash prefix"`
  1479  		}{},
  1480  	},
  1481  	{
  1482  		Description: "Option fields must be exported",
  1483  		Spec: &struct {
  1484  			option int `option:"option" description:"non-exported field"`
  1485  		}{},
  1486  	},
  1487  	{
  1488  		Description: "Bools cannot be options",
  1489  		Spec: &struct {
  1490  			Option bool `option:"b" description:"boolean option"`
  1491  		}{},
  1492  	},
  1493  	{
  1494  		Description: "Option names must be unique 1",
  1495  		Spec: &struct {
  1496  			Option1 int `option:"foo"`
  1497  			Option2 int `option:"foo"`
  1498  		}{},
  1499  	},
  1500  	{
  1501  		Description: "Option names must be unique 2",
  1502  		Spec: &struct {
  1503  			Option1 int `option:"a, foo"`
  1504  			Option2 int `option:"b, foo"`
  1505  		}{},
  1506  	},
  1507  	{
  1508  		Description: "Option names must be unique 3",
  1509  		Spec: &struct {
  1510  			Flag   bool `flag:"foo"`
  1511  			Option int  `option:"foo"`
  1512  		}{},
  1513  	},
  1514  	{
  1515  		Description: "Not a supported option type",
  1516  		Spec: &struct {
  1517  			Option map[string]int `option:"foo"`
  1518  		}{},
  1519  	},
  1520  
  1521  	// Invalid flag specs
  1522  	{
  1523  		Description: "Flags cannot have aliases",
  1524  		Spec: &struct {
  1525  			Flag bool `flag:"flag" alias:"alias" description:"flag with an alias"`
  1526  		}{},
  1527  	},
  1528  	{
  1529  		Description: "Flags cannot have placeholders",
  1530  		Spec: &struct {
  1531  			Flag bool `flag:"flag" placeholder:"PLACEHOLDER" description:"placeholder on flag"`
  1532  		}{},
  1533  	},
  1534  	{
  1535  		Description: "Flags cannot have default values",
  1536  		Spec: &struct {
  1537  			Flag bool `flag:"flag" default:"default" description:"default on flag"`
  1538  		}{},
  1539  	},
  1540  	{
  1541  		Description: "Flags cannot have env values",
  1542  		Spec: &struct {
  1543  			Flag bool `flag:"flag" env:"ENV_VALUE" description:"env on flag"`
  1544  		}{},
  1545  	},
  1546  	{
  1547  		Description: "Flags must have a name 1",
  1548  		Spec: &struct {
  1549  			Flag bool `flag:"," description:"flag with no name"`
  1550  		}{},
  1551  	},
  1552  	{
  1553  		Description: "Flags must have a name 2",
  1554  		Spec: &struct {
  1555  			Flag bool `flag:" " description:"flag with no name"`
  1556  		}{},
  1557  	},
  1558  	{
  1559  		Description: "Long flag names cannot have a leading '-' prefix",
  1560  		Spec: &struct {
  1561  			Flag bool `flag:"-flag" description:"leading dash prefix"`
  1562  		}{},
  1563  	},
  1564  	{
  1565  		Description: "Short flag names cannot have a leading '-' prefix",
  1566  		Spec: &struct {
  1567  			Flag bool `flag:"-f" description:"leading dash prefix"`
  1568  		}{},
  1569  	},
  1570  	{
  1571  		Description: "Flag fields must be exported",
  1572  		Spec: &struct {
  1573  			flag int `flag:"flag" description:"non-exported field"`
  1574  		}{},
  1575  	},
  1576  	{
  1577  		Description: "Flag names must be unique 1",
  1578  		Spec: &struct {
  1579  			Flag1 bool `flag:"foo"`
  1580  			Flag2 bool `flag:"foo"`
  1581  		}{},
  1582  	},
  1583  	{
  1584  		Description: "Flag names must be unique 2",
  1585  		Spec: &struct {
  1586  			Flag1 bool `flag:"a, foo"`
  1587  			Flag2 bool `flag:"b, foo"`
  1588  		}{},
  1589  	},
  1590  	{
  1591  		Description: "Flag names must be unique 3",
  1592  		Spec: &struct {
  1593  			Flag   bool `flag:"foo"`
  1594  			Option int  `option:"foo"`
  1595  		}{},
  1596  	},
  1597  	{
  1598  		Description: "Flag may only be bools, ints, and OptionDecoders 1",
  1599  		Spec: &struct {
  1600  			Flag string `flag:"foo"`
  1601  		}{},
  1602  	},
  1603  	{
  1604  		Description: "Flag may only be bools, ints, and OptionDecoders 2",
  1605  		Spec: &struct {
  1606  			Flag int32 `flag:"foo"`
  1607  		}{},
  1608  	},
  1609  	{
  1610  		Description: "Flag may only be bools, ints, and OptionDecoders 3",
  1611  		Spec: &struct {
  1612  			Flag struct{} `flag:"foo"`
  1613  		}{},
  1614  	},
  1615  
  1616  	// Invalid mixes of command, flag, and option
  1617  	{
  1618  		Description: "Commands cannot be options",
  1619  		Spec: &struct {
  1620  			Command struct{} `command:"command" option:"option" description:"command as option"`
  1621  		}{},
  1622  	},
  1623  	{
  1624  		Description: "Commands cannot be flags",
  1625  		Spec: &struct {
  1626  			Command struct{} `command:"command" flag:"flag" description:"command as flag"`
  1627  		}{},
  1628  	},
  1629  	{
  1630  		Description: "Options cannot be commands",
  1631  		Spec: &struct {
  1632  			Option int `option:"option" command:"command" description:"option as command"`
  1633  		}{},
  1634  	},
  1635  	{
  1636  		Description: "Options cannot be flags",
  1637  		Spec: &struct {
  1638  			Option int `option:"option" flag:"flag" description:"option as flag"`
  1639  		}{},
  1640  	},
  1641  	{
  1642  		Description: "Flags cannot be commands",
  1643  		Spec: &struct {
  1644  			Flag bool `flag:"flag" command:"command" description:"flag as command"`
  1645  		}{},
  1646  	},
  1647  	{
  1648  		Description: "Flags cannot be options",
  1649  		Spec: &struct {
  1650  			Flag bool `flag:"flag" option:"option" description:"flag as option"`
  1651  		}{},
  1652  	},
  1653  }
  1654  
  1655  func TestInvalidSpecs(t *testing.T) {
  1656  	for _, test := range invalidSpecTests {
  1657  		err := newInvalidCommand(test.Spec)
  1658  		if err == nil {
  1659  			t.Errorf("Expected error creating spec, but none received.  Test: %s", test.Description)
  1660  			continue
  1661  		}
  1662  	}
  1663  }
  1664  
  1665  func newInvalidCommand(spec interface{}) (err error) {
  1666  	defer func() {
  1667  		r := recover()
  1668  		if r != nil {
  1669  			switch e := r.(type) {
  1670  			case commandError:
  1671  				err = e
  1672  			case optionError:
  1673  				err = e
  1674  			default:
  1675  				panic(e)
  1676  			}
  1677  		}
  1678  	}()
  1679  	New("test", spec)
  1680  	return nil
  1681  }
  1682  
  1683  var invalidCommandTests = []struct {
  1684  	Description string
  1685  	Command     *Command
  1686  }{
  1687  	{
  1688  		Description: "Command name cannot be empty",
  1689  		Command:     &Command{Name: ""},
  1690  	},
  1691  	{
  1692  		Description: "Command names cannot begin with -",
  1693  		Command:     &Command{Name: "-command"},
  1694  	},
  1695  	{
  1696  		Description: "Command aliases cannot begin with -",
  1697  		Command:     &Command{Name: "command", Aliases: []string{"-alias"}},
  1698  	},
  1699  	{
  1700  		Description: "Command names cannot have spaces 1",
  1701  		Command:     &Command{Name: " command"},
  1702  	},
  1703  	{
  1704  		Description: "Command names cannot have spaces 2",
  1705  		Command:     &Command{Name: "command "},
  1706  	},
  1707  	{
  1708  		Description: "Command names cannot have spaces 3",
  1709  		Command:     &Command{Name: "command spaces"},
  1710  	},
  1711  	{
  1712  		Description: "Command aliases cannot begin with -",
  1713  		Command:     &Command{Name: "command", Aliases: []string{"-alias"}},
  1714  	},
  1715  	{
  1716  		Description: "Command aliases cannot have spaces 1",
  1717  		Command:     &Command{Name: "command", Aliases: []string{" alias"}},
  1718  	},
  1719  	{
  1720  		Description: "Command aliases cannot have spaces 2",
  1721  		Command:     &Command{Name: "command", Aliases: []string{"alias "}},
  1722  	},
  1723  	{
  1724  		Description: "Command aliases cannot have spaces 3",
  1725  		Command:     &Command{Name: "command", Aliases: []string{"alias spaces"}},
  1726  	},
  1727  }
  1728  
  1729  func TestDirectCommandValidation(t *testing.T) {
  1730  	for _, test := range invalidCommandTests {
  1731  		err := checkInvalidCommand(test.Command)
  1732  		if err == nil {
  1733  			t.Errorf("Expected error validating command, but none received.  Test: %s", test.Description)
  1734  			continue
  1735  		}
  1736  	}
  1737  }
  1738  
  1739  func checkInvalidCommand(cmd *Command) (err error) {
  1740  	defer func() {
  1741  		r := recover()
  1742  		if r != nil {
  1743  			switch e := r.(type) {
  1744  			case commandError:
  1745  				err = e
  1746  			case optionError:
  1747  				err = e
  1748  			default:
  1749  				panic(e)
  1750  			}
  1751  		}
  1752  	}()
  1753  	cmd.validate()
  1754  	return nil
  1755  }
  1756  
  1757  func TestGroupCommands(t *testing.T) {
  1758  	spec := &struct {
  1759  		Command1 struct{} `command:"command1"`
  1760  		Command2 struct{} `command:"command2"`
  1761  	}{}
  1762  	cmd := New("test", spec)
  1763  
  1764  	group := cmd.GroupCommands("command1")
  1765  	if len(group.Commands) != 1 || group.Commands[0].Name != "command1" {
  1766  		t.Errorf("Expected a single command group with command %q", "command1")
  1767  	}
  1768  	group = cmd.GroupCommands("command2")
  1769  	if len(group.Commands) != 1 || group.Commands[0].Name != "command2" {
  1770  		t.Errorf("Expected a single command group with command %q", "command2")
  1771  	}
  1772  	group = cmd.GroupCommands("command1", "command2")
  1773  	if len(group.Commands) != 2 || group.Commands[0].Name != "command1" || group.Commands[1].Name != "command2" {
  1774  		t.Errorf("Expected a single command group with commands %q and %q", "command1", "command2")
  1775  	}
  1776  	group = cmd.GroupCommands("command2", "command1")
  1777  	if len(group.Commands) != 2 || group.Commands[0].Name != "command2" || group.Commands[1].Name != "command1" {
  1778  		t.Errorf("Expected a single command group with commands %q and %q", "command2", "command1")
  1779  	}
  1780  	err := checkInvalidCommandGroup(cmd, "command3")
  1781  	if err == nil {
  1782  		t.Errorf("Expected an error to occur grouping an unknown command, but none encountered.")
  1783  	}
  1784  	err = checkInvalidCommandGroup(cmd, "command1", "command3")
  1785  	if err == nil {
  1786  		t.Errorf("Expected an error to occur grouping an unknown command, but none encountered.")
  1787  	}
  1788  }
  1789  
  1790  func checkInvalidCommandGroup(cmd *Command, name ...string) (err error) {
  1791  	defer func() {
  1792  		r := recover()
  1793  		if r != nil {
  1794  			switch e := r.(type) {
  1795  			case commandError:
  1796  				err = e
  1797  			case optionError:
  1798  				err = e
  1799  			default:
  1800  				panic(e)
  1801  			}
  1802  		}
  1803  	}()
  1804  	cmd.GroupCommands(name...)
  1805  	return nil
  1806  }
  1807  
  1808  func TestGroupOptions(t *testing.T) {
  1809  	spec := &struct {
  1810  		Option1 int `option:"option1"`
  1811  		Option2 int `option:"option2"`
  1812  	}{}
  1813  	cmd := New("test", spec)
  1814  
  1815  	group := cmd.GroupOptions("option1")
  1816  	if len(group.Options) != 1 || group.Options[0].Names[0] != "option1" {
  1817  		t.Errorf("Expected a single option group with option %q", "option1")
  1818  	}
  1819  	group = cmd.GroupOptions("option2")
  1820  	if len(group.Options) != 1 || group.Options[0].Names[0] != "option2" {
  1821  		t.Errorf("Expected a single option group with option %q", "option2")
  1822  	}
  1823  	group = cmd.GroupOptions("option1", "option2")
  1824  	if len(group.Options) != 2 || group.Options[0].Names[0] != "option1" || group.Options[1].Names[0] != "option2" {
  1825  		t.Errorf("Expected a single option group with options %q and %q", "option1", "option2")
  1826  	}
  1827  	group = cmd.GroupOptions("option2", "option1")
  1828  	if len(group.Options) != 2 || group.Options[0].Names[0] != "option2" || group.Options[1].Names[0] != "option1" {
  1829  		t.Errorf("Expected a single option group with options %q and %q", "option2", "option1")
  1830  	}
  1831  	err := checkInvalidOptionGroup(cmd, "option3")
  1832  	if err == nil {
  1833  		t.Errorf("Expected an error to occur grouping an unknown option, but none encountered.")
  1834  	}
  1835  	err = checkInvalidOptionGroup(cmd, "option1", "option3")
  1836  	if err == nil {
  1837  		t.Errorf("Expected an error to occur grouping an unknown option, but none encountered.")
  1838  	}
  1839  }
  1840  
  1841  func checkInvalidOptionGroup(cmd *Command, name ...string) (err error) {
  1842  	defer func() {
  1843  		r := recover()
  1844  		if r != nil {
  1845  			switch e := r.(type) {
  1846  			case commandError:
  1847  				err = e
  1848  			case optionError:
  1849  				err = e
  1850  			default:
  1851  				panic(e)
  1852  			}
  1853  		}
  1854  	}()
  1855  	cmd.GroupOptions(name...)
  1856  	return nil
  1857  }
  1858  
  1859  func TestCheckUnknownTagType(t *testing.T) {
  1860  	defer func() {
  1861  		spec := struct {
  1862  			Bogus int `bogus:"bogus"`
  1863  		}{}
  1864  		rval := reflect.ValueOf(spec)
  1865  		field, present := rval.Type().FieldByName("Bogus")
  1866  		if !present {
  1867  			t.Errorf("Expected Bogus field to be present")
  1868  			return
  1869  		}
  1870  
  1871  		defer func() { recover() }()
  1872  		checkTags(field, "bogus")
  1873  		t.Errorf("Expected checkFields() to panic on unknown tag %q, but it didn't happen", "bogus")
  1874  	}()
  1875  }
  1876  
  1877  /*
  1878   * Misc coverage tests to ensure code doesn't panic/blow-up
  1879   */
  1880  
  1881  func TestCommandError(t *testing.T) {
  1882  	err := commandError{fmt.Errorf("test")}
  1883  	if err.Error() != "test" {
  1884  		t.Errorf("Expected commandError to return underlying error string.  Expected: %q, Received: %q", "test", err.Error())
  1885  	}
  1886  }