From e7703f94bfd9362c8c2537f294caf72efd804580 Mon Sep 17 00:00:00 2001 From: eschao Date: Mon, 11 Dec 2017 23:13:02 +0800 Subject: [PATCH] add test and refactor codes --- cli/{cmd.go => cli.go} | 28 ++++++++++- cli/{cmd_test.go => cli_test.go} | 28 +++++++++++ test/data.go | 74 ++++++++++++++-------------- utils/utils_test.go | 84 ++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 39 deletions(-) rename cli/{cmd.go => cli.go} (90%) rename cli/{cmd_test.go => cli_test.go} (89%) create mode 100644 utils/utils_test.go diff --git a/cli/cmd.go b/cli/cli.go similarity index 90% rename from cli/cmd.go rename to cli/cli.go index 6d79310..62d88cf 100644 --- a/cli/cmd.go +++ b/cli/cli.go @@ -78,6 +78,27 @@ func (this *anyValue) Set(v string) error { return nil } +type sliceValue struct { + value reflect.Value + separator string +} + +func newSliceValue(v reflect.Value, separator string) *sliceValue { + return &sliceValue{value: v, separator: separator} +} + +func (this *sliceValue) String() string { + return this.value.String() +} + +func (this *sliceValue) Set(v string) error { + sp := this.separator + if sp == "" { + sp = ":" + } + return utils.SetValueWithSlice(this.value, v, sp) +} + var errorHandling = flag.ExitOnError type UsageFunc func(*Command) func() @@ -161,7 +182,7 @@ func (this *Command) parseValue(v reflect.Value) error { } func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error { - cmdTag, ok := f.Tag.Lookup("cmd") + cmdTag, ok := f.Tag.Lookup("cli") if !ok || cmdTag == "" { return nil } @@ -195,6 +216,9 @@ func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error { reflect.Float64: anyValue := newAnyValue(v) this.FlagSet.Var(anyValue, name, usage) + case reflect.Slice: + sliceValue := newSliceValue(v, f.Tag.Get("separator")) + this.FlagSet.Var(sliceValue, name, usage) default: return fmt.Errorf("Can't support type %s", kind.String()) } @@ -203,7 +227,7 @@ func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error { } func (this *Command) createSubCommand(tag reflect.StructTag) *Command { - cmdTag, ok := tag.Lookup("cmd") + cmdTag, ok := tag.Lookup("cli") if !ok || cmdTag == "" { return this } diff --git a/cli/cmd_test.go b/cli/cli_test.go similarity index 89% rename from cli/cmd_test.go rename to cli/cli_test.go index f7b30ed..81a0f0d 100644 --- a/cli/cmd_test.go +++ b/cli/cli_test.go @@ -207,3 +207,31 @@ func TestVariousTypeCommand(t *testing.T) { assert.Equal(float64(2.345), typesConfig.Float64Value) assert.Error(cmd.Parse([]string{"-float64=xxx"})) } + +func TestCommandWithSlices(t *testing.T) { + assert := assert.New(t) + conf := test.SlicesConfig{} + cmd := New("Slice") + assert.NoError(cmd.Init(&conf), "Can't init slice command") + + paths := "/var:/home:/log" + debugs := "error;info;debug" + values := "100,200,300" + args := []string{"-paths", paths, "-debugs", debugs, "-values", values} + assert.NoError(cmd.Parse(args)) + + assert.Equal(3, len(conf.Paths)) + assert.Equal("/var", conf.Paths[0]) + assert.Equal("/home", conf.Paths[1]) + assert.Equal("/log", conf.Paths[2]) + + assert.Equal(3, len(conf.Debugs)) + assert.Equal("error", conf.Debugs[0]) + assert.Equal("info", conf.Debugs[1]) + assert.Equal("debug", conf.Debugs[2]) + + assert.Equal(3, len(conf.Values)) + assert.Equal(100, conf.Values[0]) + assert.Equal(200, conf.Values[1]) + assert.Equal(300, conf.Values[2]) +} diff --git a/test/data.go b/test/data.go index 295e3cc..f6f8288 100644 --- a/test/data.go +++ b/test/data.go @@ -1,59 +1,59 @@ package test type DBConfig struct { - Host string `json:"dbHost" yaml:"dbHost" env:"HOST" cmd:"dbHost database server hostname"` - Port int `json:"dbPort" yaml:"dbPort" env:"PORT" cmd:"dbPort database server port"` - User string `json:"dbUser" yaml:"dbUser" env:"USER" cmd:"dbUser database username"` - Password string `json:"dbPassword" yaml:"dbPassword" env:"PASSWORD" cmd:"dbPassword database user password"` - Log LogConfig `json:"log" yaml:"log" env:"LOG_" cmd:"log database log configuration"` + Host string `json:"dbHost" yaml:"dbHost" env:"HOST" cli:"dbHost database server hostname"` + Port int `json:"dbPort" yaml:"dbPort" env:"PORT" cli:"dbPort database server port"` + User string `json:"dbUser" yaml:"dbUser" env:"USER" cli:"dbUser database username"` + Password string `json:"dbPassword" yaml:"dbPassword" env:"PASSWORD" cli:"dbPassword database user password"` + Log LogConfig `json:"log" yaml:"log" env:"LOG_" cli:"log database log configuration"` } type LoginConfig struct { - User string `json:"user" yaml:"user" env:"USER" cmd:"user login username"` - Password string `json:"password" yaml:"password" env:"PASSWORD" cmd:"password login password"` + User string `json:"user" yaml:"user" env:"USER" prop:"user" cli:"user login username"` + Password string `json:"password" yaml:"password" env:"PASSWORD" prop:"password" cli:"password login password"` } type LogConfig struct { - Path string `json:"path" yaml:"path" env:"PATH" cmd:"path log path"` - Level string `json:"level" yaml:"level" env:"LEVEL" cmd:"level log level {debug|warning|error}"` + Path string `json:"path" yaml:"path" env:"PATH" prop:"path" cli:"path log path"` + Level string `json:"level" yaml:"level" env:"LEVEL" porp:"level" cli:"level log level {debug|warning|error}"` } type ServiceConfig struct { - Host string `env:"CONFIG_TEST_SERVICE_HOST" cmd:"hostname service hostname"` - Port int `env:"CONFIG_TEST_SERVICE_PORT" cmd:"port service port"` - DBConfig DBConfig `env:"CONFIG_TEST_SERVICE_DB_" cmd:"database database configuration"` - Login *LoginConfig `env:"CONFIG_TEST_SERVICE_LOGIN_" cmd:"login login user and password"` - Log LogConfig `env:"CONFIG_TEST_SERVICE_LOG_" cmd:"log service log configuration"` + Host string `env:"CONFIG_TEST_SERVICE_HOST" cli:"hostname service hostname"` + Port int `env:"CONFIG_TEST_SERVICE_PORT" cli:"port service port"` + DBConfig DBConfig `env:"CONFIG_TEST_SERVICE_DB_" cli:"database database configuration"` + Login *LoginConfig `env:"CONFIG_TEST_SERVICE_LOGIN_" cli:"login login user and password"` + Log LogConfig `env:"CONFIG_TEST_SERVICE_LOG_" cli:"log service log configuration"` } type TypesConfig struct { - BoolValue bool `env:"CONFIG_TEST_BOOL" cmd:"bool boolean value"` - StrValue string `env:"CONFIG_TEST_STR" cmd:"str string value"` - Int8Value int8 `env:"CONFIG_TEST_INT8" cmd:"int8 int8 value"` - Int16Value int16 `env:"CONFIG_TEST_INT16" cmd:"int16 int16 value"` - IntValue int `env:"CONFIG_TEST_INT" cmd:"int int value"` - Int32Value int32 `env:"CONFIG_TEST_INT32" cmd:"int32 int32 value"` - Int64Value int64 `env:"CONFIG_TEST_INT64" cmd:"int64 int64 value"` - Uint8Value uint8 `env:"CONFIG_TEST_UINT8" cmd:"uint8 uint8 value"` - Uint16Value uint16 `env:"CONFIG_TEST_UINT16" cmd:"uint16 uint16 value"` - UintValue uint `env:"CONFIG_TEST_UINT" cmd:"uint uint value"` - Uint32Value uint32 `env:"CONFIG_TEST_UINT32" cmd:"uint32 uint32 value"` - Uint64Value uint64 `env:"CONFIG_TEST_UINT64" cmd:"uint64 uint64 value"` - Float32Value float32 `env:"CONFIG_TEST_FLOAT32" cmd:"float32 float32 value"` - Float64Value float64 `env:"CONFIG_TEST_FLOAT64" cmd:"float64 float64 value"` + BoolValue bool `env:"CONFIG_TEST_BOOL" cli:"bool boolean value"` + StrValue string `env:"CONFIG_TEST_STR" cli:"str string value"` + Int8Value int8 `env:"CONFIG_TEST_INT8" cli:"int8 int8 value"` + Int16Value int16 `env:"CONFIG_TEST_INT16" cli:"int16 int16 value"` + IntValue int `env:"CONFIG_TEST_INT" cli:"int int value"` + Int32Value int32 `env:"CONFIG_TEST_INT32" cli:"int32 int32 value"` + Int64Value int64 `env:"CONFIG_TEST_INT64" cli:"int64 int64 value"` + Uint8Value uint8 `env:"CONFIG_TEST_UINT8" cli:"uint8 uint8 value"` + Uint16Value uint16 `env:"CONFIG_TEST_UINT16" cli:"uint16 uint16 value"` + UintValue uint `env:"CONFIG_TEST_UINT" cli:"uint uint value"` + Uint32Value uint32 `env:"CONFIG_TEST_UINT32" cli:"uint32 uint32 value"` + Uint64Value uint64 `env:"CONFIG_TEST_UINT64" cli:"uint64 uint64 value"` + Float32Value float32 `env:"CONFIG_TEST_FLOAT32" cli:"float32 float32 value"` + Float64Value float64 `env:"CONFIG_TEST_FLOAT64" cli:"float64 float64 value"` } type DefValueConfig struct { - BoolValue bool `env:"CONFIG_TEST_BOOL" cmd:"bool boolean value" default:"true"` - IntValue int `env:"CONFIG_TEST_INT" cmd:"int int value" default:"123"` - Float64Value float64 `env:"CONFIG_TEST_FLOAT64" cmd:"float64 float64 value" default:"123.4567"` - StrValue string `env:"CONFIG_TEST_STR" cmd:"str string value" default:"default-string"` - SliceValue []string `env:"CONFIG_TEST_SLICE" cmd:"slice slice values" default:"xx:yy:zz"` - NoDefValue string `env:"CONFIG_TEST_NO_DEFVALUE" cmd:"nodefvalue no default value"` + BoolValue bool `env:"CONFIG_TEST_BOOL" cli:"bool boolean value" default:"true"` + IntValue int `env:"CONFIG_TEST_INT" cli:"int int value" default:"123"` + Float64Value float64 `env:"CONFIG_TEST_FLOAT64" cli:"float64 float64 value" default:"123.4567"` + StrValue string `env:"CONFIG_TEST_STR" cli:"str string value" default:"default-string"` + SliceValue []string `env:"CONFIG_TEST_SLICE" cli:"slice slice values" default:"xx:yy:zz"` + NoDefValue string `env:"CONFIG_TEST_NO_DEFVALUE" cli:"nodefvalue no default value"` } type SlicesConfig struct { - Paths []string `env:"CONFIG_TEST_SLICES_PATHS"` - Debugs []string `env:"CONFIG_TEST_SLICES_DEBUG" separator:";"` - Values []int `env:"CONFIG_TEST_SLICES_VALUES" separator:","` + Paths []string `env:"CONFIG_TEST_SLICES_PATHS" cli:"paths multiple path"` + Debugs []string `env:"CONFIG_TEST_SLICES_DEBUG" cli:"debugs multiple debug" separator:";"` + Values []int `env:"CONFIG_TEST_SLICES_VALUES" cli:"values multiple value" separator:","` } diff --git a/utils/utils_test.go b/utils/utils_test.go new file mode 100644 index 0000000..d157478 --- /dev/null +++ b/utils/utils_test.go @@ -0,0 +1,84 @@ +package utils + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +type data struct { + BoolValue bool + Int8Value int8 + IntValue int + Uint16Value uint16 + UintValue uint + Float32Value float32 + Names []string +} + +func TestSetValueWithBool(t *testing.T) { + d := data{} + v := reflect.ValueOf(&d).Elem().FieldByName("BoolValue") + + assert := assert.New(t) + assert.NoError(SetValueWithBool(v, "true")) + assert.Equal(true, d.BoolValue) +} + +func TestSetValueWithFloat32(t *testing.T) { + d := data{} + v := reflect.ValueOf(&d).Elem().FieldByName("Float32Value") + + assert := assert.New(t) + assert.NoError(SetValueWithFloatX(v, "123.456", 32)) + assert.Equal(float32(123.456), d.Float32Value) +} + +func TestSetValueWithInt8(t *testing.T) { + d := data{} + v := reflect.ValueOf(&d).Elem().FieldByName("Int8Value") + + assert := assert.New(t) + assert.NoError(SetValueWithIntX(v, "10", 8)) + assert.Equal(int8(10), d.Int8Value) +} + +func TestSetValueWithInt(t *testing.T) { + d := data{} + v := reflect.ValueOf(&d).Elem().FieldByName("IntValue") + + assert := assert.New(t) + assert.NoError(SetValueWithIntX(v, "10000", 32)) + assert.Equal(10000, d.IntValue) +} + +func TestSetValueWithUint16(t *testing.T) { + d := data{} + v := reflect.ValueOf(&d).Elem().FieldByName("Uint16Value") + + assert := assert.New(t) + assert.NoError(SetValueWithUintX(v, "100", 16)) + assert.Equal(uint16(100), d.Uint16Value) +} + +func TestSetValueWithUint(t *testing.T) { + d := data{} + v := reflect.ValueOf(&d).Elem().FieldByName("UintValue") + + assert := assert.New(t) + assert.NoError(SetValueWithUintX(v, "2000", 32)) + assert.Equal(uint(2000), d.UintValue) +} + +func TestSetValueWithSlice(t *testing.T) { + d := data{} + v := reflect.ValueOf(&d).Elem().FieldByName("Names") + + assert := assert.New(t) + assert.NoError(SetValueWithSlice(v, "xx:yy:zz", ":")) + assert.Equal(3, len(d.Names)) + assert.Equal("xx", d.Names[0]) + assert.Equal("yy", d.Names[1]) + assert.Equal("zz", d.Names[2]) +}