| /* |
| Copyright 2018 The Kubernetes Authors. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package config |
| |
| import ( |
| "flag" |
| "testing" |
| "time" |
| |
| "github.com/stretchr/testify/assert" |
| "github.com/stretchr/testify/require" |
| ) |
| |
| func TestInt(t *testing.T) { |
| CommandLine = flag.NewFlagSet("test", 0) |
| var context struct { |
| Number int `default:"5" usage:"some number"` |
| } |
| require.NotPanics(t, func() { |
| AddOptions(&context, "") |
| }) |
| require.Equal(t, []simpleFlag{ |
| { |
| name: "number", |
| usage: "some number", |
| defValue: "5", |
| }}, |
| allFlags(CommandLine)) |
| assert.Equal(t, 5, context.Number) |
| } |
| |
| func TestLower(t *testing.T) { |
| CommandLine = flag.NewFlagSet("test", 0) |
| var context struct { |
| Ähem string |
| MixedCase string |
| } |
| require.NotPanics(t, func() { |
| AddOptions(&context, "") |
| }) |
| require.Equal(t, []simpleFlag{ |
| { |
| name: "mixedCase", |
| }, |
| { |
| name: "ähem", |
| }, |
| }, |
| allFlags(CommandLine)) |
| } |
| |
| func TestPrefix(t *testing.T) { |
| CommandLine = flag.NewFlagSet("test", 0) |
| var context struct { |
| Number int `usage:"some number"` |
| } |
| require.NotPanics(t, func() { |
| AddOptions(&context, "some.prefix") |
| }) |
| require.Equal(t, []simpleFlag{ |
| { |
| name: "some.prefix.number", |
| usage: "some number", |
| defValue: "0", |
| }}, |
| allFlags(CommandLine)) |
| } |
| |
| func TestRecursion(t *testing.T) { |
| CommandLine = flag.NewFlagSet("test", 0) |
| type Nested struct { |
| Number1 int `usage:"embedded number"` |
| } |
| var context struct { |
| Nested |
| A struct { |
| B struct { |
| C struct { |
| Number2 int `usage:"some number"` |
| } |
| } |
| } |
| } |
| require.NotPanics(t, func() { |
| AddOptions(&context, "") |
| }) |
| require.Equal(t, []simpleFlag{ |
| { |
| name: "a.b.c.number2", |
| usage: "some number", |
| defValue: "0", |
| }, |
| { |
| name: "number1", |
| usage: "embedded number", |
| defValue: "0", |
| }, |
| }, |
| allFlags(CommandLine)) |
| } |
| |
| func TestPanics(t *testing.T) { |
| assert.PanicsWithValue(t, `invalid default "a" for int entry prefix.number: strconv.Atoi: parsing "a": invalid syntax`, func() { |
| var context struct { |
| Number int `default:"a"` |
| } |
| AddOptions(&context, "prefix") |
| }) |
| |
| assert.PanicsWithValue(t, `invalid default "10000000000000000000" for int entry prefix.number: strconv.Atoi: parsing "10000000000000000000": value out of range`, func() { |
| var context struct { |
| Number int `default:"10000000000000000000"` |
| } |
| AddOptions(&context, "prefix") |
| }) |
| |
| assert.PanicsWithValue(t, `options parameter without a type - nil?!`, func() { |
| AddOptions(nil, "") |
| }) |
| |
| assert.PanicsWithValue(t, `need a pointer to a struct, got instead: *int`, func() { |
| number := 0 |
| AddOptions(&number, "") |
| }) |
| |
| assert.PanicsWithValue(t, `struct entry "prefix.number" not exported`, func() { |
| var context struct { |
| number int |
| } |
| AddOptions(&context, "prefix") |
| }) |
| |
| assert.PanicsWithValue(t, `unsupported struct entry type "prefix.someNumber": config.MyInt`, func() { |
| type MyInt int |
| var context struct { |
| SomeNumber MyInt |
| } |
| AddOptions(&context, "prefix") |
| }) |
| } |
| |
| func TestTypes(t *testing.T) { |
| CommandLine = flag.NewFlagSet("test", 0) |
| type Context struct { |
| Bool bool `default:"true"` |
| Duration time.Duration `default:"1ms"` |
| Float64 float64 `default:"1.23456789"` |
| String string `default:"hello world"` |
| Int int `default:"-1" usage:"some number"` |
| Int64 int64 `default:"-1234567890123456789"` |
| Uint uint `default:"1"` |
| Uint64 uint64 `default:"1234567890123456789"` |
| } |
| var context Context |
| require.NotPanics(t, func() { |
| AddOptions(&context, "") |
| }) |
| require.Equal(t, []simpleFlag{ |
| { |
| name: "bool", |
| defValue: "true", |
| isBool: true, |
| }, |
| { |
| name: "duration", |
| defValue: "1ms", |
| }, |
| { |
| name: "float64", |
| defValue: "1.23456789", |
| }, |
| { |
| name: "int", |
| usage: "some number", |
| defValue: "-1", |
| }, |
| { |
| name: "int64", |
| defValue: "-1234567890123456789", |
| }, |
| { |
| name: "string", |
| defValue: "hello world", |
| }, |
| { |
| name: "uint", |
| defValue: "1", |
| }, |
| { |
| name: "uint64", |
| defValue: "1234567890123456789", |
| }, |
| }, |
| allFlags(CommandLine)) |
| assert.Equal(t, |
| Context{true, time.Millisecond, 1.23456789, "hello world", |
| -1, -1234567890123456789, 1, 1234567890123456789, |
| }, |
| context, |
| "default values must match") |
| require.NoError(t, CommandLine.Parse([]string{ |
| "-int", "-2", |
| "-int64", "-9123456789012345678", |
| "-uint", "2", |
| "-uint64", "9123456789012345678", |
| "-string", "pong", |
| "-float64", "-1.23456789", |
| "-bool=false", |
| "-duration=1s", |
| })) |
| assert.Equal(t, |
| Context{false, time.Second, -1.23456789, "pong", |
| -2, -9123456789012345678, 2, 9123456789012345678, |
| }, |
| context, |
| "parsed values must match") |
| } |
| |
| func allFlags(fs *flag.FlagSet) []simpleFlag { |
| var flags []simpleFlag |
| fs.VisitAll(func(f *flag.Flag) { |
| s := simpleFlag{ |
| name: f.Name, |
| usage: f.Usage, |
| defValue: f.DefValue, |
| } |
| type boolFlag interface { |
| flag.Value |
| IsBoolFlag() bool |
| } |
| if fv, ok := f.Value.(boolFlag); ok && fv.IsBoolFlag() { |
| s.isBool = true |
| } |
| flags = append(flags, s) |
| }) |
| return flags |
| } |
| |
| type simpleFlag struct { |
| name string |
| usage string |
| defValue string |
| isBool bool |
| } |