github.com/puellanivis/breton@v0.2.16/lib/gnuflag/struct_test.go (about)

     1  package gnuflag
     2  
     3  import (
     4  	"net/url"
     5  	"reflect"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  type mockFlag struct {
    11  	Value string
    12  }
    13  
    14  func (m mockFlag) String() string {
    15  	return m.Value
    16  }
    17  
    18  func (m *mockFlag) Set(s string) error {
    19  	m.Value = s
    20  	return nil
    21  }
    22  
    23  func (m mockFlag) Get() interface{} {
    24  	return m.Value
    25  }
    26  
    27  var _ Value = &mockFlag{}
    28  
    29  type badType chan struct{}
    30  
    31  func TestStructVar(t *testing.T) {
    32  	var Flags struct {
    33  		ignored               bool
    34  		True                  bool    `flag:"" desc:"a bool value" default:"true"`
    35  		ZeroValueBool         bool    `flag:"" desc:"a zero value bool"`
    36  		PiApprox              float64 `flag:",def=3.1415" desc:"a float64 with an approximation for pi"`
    37  		NegativeFortyTwo      int     `flag:",default=-42" desc:"an int with -42"`
    38  		SkipEvenThoughBadType badType `flag:"-"`
    39  		NegativeSixtyFour     int64   `desc:"an int64 with -64" default:"-64"`
    40  		Foo                   string  `desc:"a string with foo" default:"foo"`
    41  		Renamed               string  `flag:"bar" desc:"a field renamed to bar"`
    42  		FortyTwo              uint    `desc:"a uint with 42" default:"42"`
    43  		SixtyFour             uint64  `desc:"a uint64 with 64" default:"64"`
    44  		ANamedFlagValue       mockFlag
    45  		AnUnnamedIntType      int
    46  
    47  		SubFlags struct {
    48  			Value   int
    49  			Pointer *int
    50  		}
    51  	}
    52  
    53  	var fs FlagSet
    54  
    55  	if err := fs.structVar("", reflect.ValueOf(&Flags).Elem()); err != nil {
    56  		t.Fatal("unexpected error running structVar:", err)
    57  	}
    58  
    59  	if len(fs.formal)+len(fs.short) == 0 {
    60  		t.Fatal("no flags set on FlagSet")
    61  	}
    62  
    63  	checkedKeys := make(map[string]bool)
    64  	checkFlag := func(name, value, usage string, val interface{}) {
    65  		checkedKeys[name] = true
    66  
    67  		f, ok := fs.formal[name]
    68  		if !ok {
    69  			t.Errorf("expected flag %q to exist", name)
    70  			return
    71  		}
    72  		if f.DefValue != value {
    73  			t.Errorf("flag %q has default value %q, but epected %q", name, f.Value, value)
    74  		}
    75  		if f.Usage != usage {
    76  			t.Errorf("flag %q has usage %q, but expected %q", name, f.Usage, usage)
    77  		}
    78  
    79  		ptr := reflect.ValueOf(val).Elem().Addr().Pointer()
    80  		p := reflect.ValueOf(f.Value).Elem().Addr().Pointer()
    81  		if ptr != p {
    82  			t.Errorf("flag %q points to %#v, but expected %#v", name, p, ptr)
    83  		}
    84  	}
    85  
    86  	checkFlag("true", "true", "a bool value", &(Flags.True))
    87  	checkFlag("zero-value-bool", "false", "a zero value bool", &(Flags.ZeroValueBool))
    88  	checkFlag("pi-approx", "3.1415", "a float64 with an approximation for pi", &(Flags.PiApprox))
    89  	checkFlag("negative-forty-two", "-42", "an int with -42", &(Flags.NegativeFortyTwo))
    90  	checkFlag("negative-sixty-four", "-64", "an int64 with -64", &(Flags.NegativeSixtyFour))
    91  	checkFlag("foo", "foo", "a string with foo", &(Flags.Foo))
    92  	checkFlag("bar", "", "a field renamed to bar", &(Flags.Renamed))
    93  	checkFlag("forty-two", "42", "a uint with 42", &(Flags.FortyTwo))
    94  	checkFlag("sixty-four", "64", "a uint64 with 64", &(Flags.SixtyFour))
    95  	checkFlag("a-named-flag-value", "", "ANamedFlagValue `gnuflag.mockFlag`", &(Flags.ANamedFlagValue))
    96  	checkFlag("an-unnamed-int-type", "0", "AnUnnamedIntType `int`", &(Flags.AnUnnamedIntType))
    97  	checkFlag("sub-flags-value", "0", "Value `int`", &(Flags.SubFlags.Value))
    98  	checkFlag("sub-flags-pointer", "0", "Pointer `*int`", Flags.SubFlags.Pointer)
    99  
   100  	for k := range fs.formal {
   101  		if !checkedKeys[k] {
   102  			t.Errorf("unexpected key found: %q", k)
   103  		}
   104  	}
   105  }
   106  
   107  func TestSpecialValues(t *testing.T) {
   108  	var Flags struct {
   109  		AUint8Alias byte
   110  		MyURL       *url.URL
   111  	}
   112  
   113  	Flags.AUint8Alias = 42
   114  	MyURL, err := url.Parse("scheme://hostname:12345/path")
   115  	if err != nil {
   116  		t.Fatal("unexpected error parsing url:", err)
   117  	}
   118  	Flags.MyURL = MyURL
   119  
   120  	var fs FlagSet
   121  
   122  	if err := fs.structVar("", reflect.ValueOf(&Flags).Elem()); err != nil {
   123  		t.Fatal("unexpected error running structVar:", err)
   124  	}
   125  
   126  	if len(fs.formal)+len(fs.short) == 0 {
   127  		t.Fatal("no flags set on FlagSet")
   128  	}
   129  
   130  	checkedKeys := make(map[string]bool)
   131  	checkFlag := func(name, value, usage string, val, expect interface{}) {
   132  		checkedKeys[name] = true
   133  
   134  		f, ok := fs.formal[name]
   135  		if !ok {
   136  			t.Errorf("expected flag %q to exist", name)
   137  			return
   138  		}
   139  		if f.DefValue != value {
   140  			t.Errorf("flag %q has default value %q, but epected %q", name, f.Value, value)
   141  		}
   142  		if f.Usage != usage {
   143  			t.Errorf("flag %q has usage %q, but expected %q", name, f.Usage, usage)
   144  		}
   145  		if !reflect.DeepEqual(val, expect) {
   146  			t.Errorf("flag %q is %#v, but expected %#v", name, val, expect)
   147  		}
   148  	}
   149  
   150  	checkFlag("a-uint8-alias", "42", "AUint8Alias `uint8`", Flags.AUint8Alias, byte(42))
   151  	checkFlag("my-url", "scheme://hostname:12345/path", "MyURL `*url.URL`", Flags.MyURL, MyURL)
   152  
   153  	for k := range fs.formal {
   154  		if !checkedKeys[k] {
   155  			t.Errorf("unexpected key found: %q", k)
   156  		}
   157  	}
   158  
   159  	fs.Set("a-uint8-alias", "13")
   160  	if Flags.AUint8Alias != 13 {
   161  		t.Errorf("expected %q to be set to 13, but got %d", "AUint8Alias", 13)
   162  	}
   163  
   164  	expect := &url.URL{
   165  		Scheme: "http",
   166  		Host:   "example.com:80",
   167  		Path:   "/a/different/path",
   168  	}
   169  	fs.Set("my-url", "http://example.com:80/a/different/path")
   170  	if !reflect.DeepEqual(Flags.MyURL, expect) {
   171  		t.Errorf("expected %q to be set to %q, but got %q", "MyURL", expect, Flags.MyURL)
   172  	}
   173  }
   174  
   175  func TestStructVar_BadInputs(t *testing.T) {
   176  	type test struct {
   177  		name string
   178  		s    interface{}
   179  	}
   180  
   181  	tests := []test{
   182  		{
   183  			name: "bad name: contains '='",
   184  			s: &struct {
   185  				F int `flag:"="`
   186  			}{},
   187  		},
   188  		{
   189  			name: "bad name: begins with -",
   190  			s: &struct {
   191  				F int `flag:"-flag"`
   192  			}{},
   193  		},
   194  		{
   195  			name: "unsupported flag type",
   196  			s: &struct {
   197  				F badType
   198  			}{},
   199  		},
   200  		{
   201  			name: "bad bool default",
   202  			s: &struct {
   203  				F bool `default:"foo"`
   204  			}{},
   205  		},
   206  		{
   207  			name: "bad time.Duration default",
   208  			s: &struct {
   209  				F time.Duration `default:"foo"`
   210  			}{},
   211  		},
   212  		{
   213  			name: "bad float64 default",
   214  			s: &struct {
   215  				F float64 `default:"foo"`
   216  			}{},
   217  		},
   218  		{
   219  			name: "bad int default",
   220  			s: &struct {
   221  				F int `default:"foo"`
   222  			}{},
   223  		},
   224  		{
   225  			name: "bad int64 default",
   226  			s: &struct {
   227  				F int64 `default:"foo"`
   228  			}{},
   229  		},
   230  		{
   231  			name: "bad uint default",
   232  			s: &struct {
   233  				F uint `default:"foo"`
   234  			}{},
   235  		},
   236  		{
   237  			name: "bad uint64 default",
   238  			s: &struct {
   239  				F uint64 `default:"foo"`
   240  			}{},
   241  		},
   242  		{
   243  			name: "uint8 default too large",
   244  			s: &struct {
   245  				F uint8 `default:"256"`
   246  			}{},
   247  		},
   248  		{
   249  			name: "uint16 default too large",
   250  			s: &struct {
   251  				F uint16 `default:"65536"`
   252  			}{},
   253  		},
   254  		{
   255  			name: "uint32 default too large",
   256  			s: &struct {
   257  				F uint32 `default:"4294967296"`
   258  			}{},
   259  		},
   260  		{
   261  			name: "int8 default too large",
   262  			s: &struct {
   263  				F int8 `default:"128"`
   264  			}{},
   265  		},
   266  		{
   267  			name: "int16 default too large",
   268  			s: &struct {
   269  				F int16 `default:"32768"`
   270  			}{},
   271  		},
   272  		{
   273  			name: "int32 default too large",
   274  			s: &struct {
   275  				F int32 `default:"2147483648"`
   276  			}{},
   277  		},
   278  		{
   279  			name: "float32 default too large",
   280  			s: &struct {
   281  				F int32 `default:"1e39"`
   282  			}{},
   283  		},
   284  	}
   285  
   286  	for _, tt := range tests {
   287  		var fs FlagSet
   288  
   289  		if err := fs.structVar("", reflect.ValueOf(tt.s).Elem()); err == nil {
   290  			t.Errorf("%s: expected error running structVar, but got none", tt.name)
   291  		}
   292  	}
   293  
   294  }
   295  
   296  func Test_flagName(t *testing.T) {
   297  	type test struct {
   298  		name     string
   299  		flagName string
   300  		want     string
   301  	}
   302  
   303  	tests := []test{
   304  		{
   305  			name:     "create DSN variable for a country",
   306  			flagName: "DNS",
   307  			want:     "dns",
   308  		},
   309  		{
   310  			name:     "create variable lower-casing the first character",
   311  			flagName: "Name",
   312  			want:     "name",
   313  		},
   314  		{
   315  			name:     "one letter variable with no prefix",
   316  			flagName: "N",
   317  			want:     "n",
   318  		},
   319  		{
   320  			name:     "split flag name depending on cases",
   321  			flagName: "NaNo",
   322  			want:     "na-no",
   323  		},
   324  		{
   325  			name:     "split flag name, but acronym together",
   326  			flagName: "GetMyURLFromString",
   327  			want:     "get-my-url-from-string",
   328  		},
   329  		{
   330  			name:     "split flag name, but acronym last",
   331  			flagName: "GetMyURL",
   332  			want:     "get-my-url",
   333  		},
   334  		{
   335  			name:     "split flag name, but acronym first",
   336  			flagName: "URLFromString",
   337  			want:     "url-from-string",
   338  		},
   339  		// Legend: a = expected acronym, n = word inital capital, x = rest of non-acronym words
   340  		{
   341  			name:     "pathological acronym tests",
   342  			flagName: "NxNxxANxANxxAANxAANxx",
   343  			want:     "nx-nxx-a-nx-a-nxx-aa-nx-aa-nxx",
   344  		},
   345  		{
   346  			name:     "pathological acronym tests, with initial one-letter 'acronym'",
   347  			flagName: "ANxNxxANxANxxAANxAANxx",
   348  			want:     "a-nx-nxx-a-nx-a-nxx-aa-nx-aa-nxx",
   349  		},
   350  	}
   351  	for _, tt := range tests {
   352  		t.Run(tt.name, func(t *testing.T) {
   353  			if got := flagName(tt.flagName); got != tt.want {
   354  				t.Errorf("flagName() = %v, want %v", got, tt.want)
   355  			}
   356  		})
   357  	}
   358  }