// SPDX-FileCopyrightText: © 2022 Grégoire Duchêne // SPDX-License-Identifier: ISC package core_test import ( "flag" "regexp" "strconv" "testing" "github.com/google/go-cmp/cmp" "go.awhk.org/core" ) func TestFeature_Disable(t *testing.T) { f := core.Feature{} f.Disable() (&core.T{T: t}).AssertEqual(false, f.Enabled()) } func TestFeature_Enable(t *testing.T) { f := core.Feature{} f.Enable() (&core.T{T: t}).AssertEqual(true, f.Enabled()) } func TestFlag(s *testing.T) { t := core.T{T: s} fs := flag.NewFlagSet("", flag.PanicOnError) fl := core.Flag(fs, "test", 42, "", strconv.Atoi) t.AssertEqual(42, *fl) t.AssertErrorIs(nil, fs.Parse([]string{"-test=84"})) t.AssertEqual(84, *fl) } func TestFlagFeature(s *testing.T) { t := core.T{T: s} fs := flag.NewFlagSet("", flag.PanicOnError) ff := core.FlagFeature(fs, "some-feature", false, "") t.AssertErrorIs(nil, fs.Parse([]string{"-some-feature"})) t.AssertEqual(true, ff.Enabled()) } func TestFlagVar(s *testing.T) { t := core.T{T: s} fs := flag.NewFlagSet("", flag.PanicOnError) fl := 42 core.FlagVar(fs, &fl, "test", "", strconv.Atoi) t.AssertEqual(42, fl) t.AssertErrorIs(nil, fs.Parse([]string{"-test=84"})) t.AssertEqual(84, fl) } func TestFlagSlice(s *testing.T) { t := core.T{T: s} fs := flag.NewFlagSet("", flag.PanicOnError) fl := core.FlagSlice(fs, "test", []int{42}, "", strconv.Atoi, ",") t.AssertEqual([]int{42}, *fl) t.AssertErrorIs(nil, fs.Parse([]string{"-test=1", "-test=2", "-test=42,84"})) t.AssertEqual([]int{1, 2, 42, 84}, *fl) } func TestFlagSliceVar(s *testing.T) { t := core.T{T: s} fs := flag.NewFlagSet("", flag.PanicOnError) fl := []int{42} core.FlagSliceVar(fs, &fl, "test", "", strconv.Atoi, ",") t.AssertEqual([]int{42}, fl) t.AssertErrorIs(nil, fs.Parse([]string{"-test=1", "-test=2", "-test=42,84"})) t.AssertEqual([]int{1, 2, 42, 84}, fl) } func TestInitFlagSet(s *testing.T) { t := core.T{T: s} for _, tc := range []struct { name string env []string cfg map[string]string args []string expInt int expIntSlice []int expStr string expErr error }{ { name: "ArgsOnly", args: []string{"-int=42", "-int-slice=42,84"}, expInt: 42, expIntSlice: []int{42, 84}, }, { name: "EnvOnly", env: []string{"INT=42", "INT_SLICE=42,84"}, expInt: 42, expIntSlice: []int{42, 84}, }, { name: "CfgOnly", cfg: map[string]string{"int": "42", "int-slice": "42,84"}, expInt: 42, expIntSlice: []int{42, 84}, }, { name: "InOrder", env: []string{"STRING=Hello World!"}, cfg: map[string]string{"string": "Hello Universe!", "int-slice": "42,84"}, args: []string{"-int=42", "-int-slice=21,42"}, expInt: 42, expIntSlice: []int{21, 42}, expStr: "Hello Universe!", }, } { t.Run(tc.name, func(t *core.T) { fs := flag.NewFlagSet("", flag.PanicOnError) fi := fs.Int("int", 0, "") fl := core.FlagSlice(fs, "int-slice", nil, "", strconv.Atoi, ",") fm := fs.String("string", "", "") t.AssertErrorIs(tc.expErr, core.InitFlagSet(fs, tc.env, tc.cfg, tc.args)) t.AssertEqual(tc.expInt, *fi) t.AssertEqual(tc.expIntSlice, *fl) t.AssertEqual(tc.expStr, *fm) }) } t.Run("NoMutableFlagValue", func(t *core.T) { fs := flag.NewFlagSet("", flag.PanicOnError) fi := fs.Int("int", 0, "") t.AssertErrorIs(nil, core.InitFlagSet(fs, nil, nil, []string{"-int=42"})) t.AssertEqual(42, *fi) t.AssertErrorIs(nil, core.InitFlagSet(fs, nil, nil, []string{"-int=21"})) t.AssertEqual(42, *fi) }) } func TestParseProtobufEnum(s *testing.T) { t := &core.T{T: s, Options: cmp.Options{sortStrings}} // That type and map emulate code generated by protoc. type fakeEnum int32 values := map[string]int32{ "FAKE_UNKNOWN": 0, "FOO": 1, "BAR": 2, } parse := core.ParseProtobufEnum[fakeEnum](values) t.Run("Match", func(t *core.T) { val, err := parse("FOO") t.AssertErrorIs(nil, err) t.AssertEqual(fakeEnum(1), val) }) t.Run("MatchCase", func(t *core.T) { val, err := parse("Foo") t.AssertErrorIs(nil, err) t.AssertEqual(fakeEnum(1), val) }) t.Run("UnknownValue", func(t *core.T) { val, err := parse("BAZ") var exp core.UnknownEnumValueError[string] if t.AssertErrorAs(&exp, err) { t.AssertEqual("BAZ", exp.Actual) t.AssertEqual([]string{"BAR", "FAKE_UNKNOWN", "FOO"}, exp.Expected) } t.AssertEqual(fakeEnum(0), val) }) } func TestParseStringEnum(s *testing.T) { t := &core.T{T: s} parse := core.ParseStringEnum("foo", "bar") t.Run("Match", func(t *core.T) { val, err := parse("foo") t.AssertErrorIs(nil, err) t.AssertEqual("foo", val) val, err = parse("bar") t.AssertErrorIs(nil, err) t.AssertEqual("bar", val) }) t.Run("UnknownValue", func(t *core.T) { val, err := parse("baz") var exp core.UnknownEnumValueError[string] if t.AssertErrorAs(&exp, err) { t.AssertEqual("baz", exp.Actual) t.AssertEqual([]string{"foo", "bar"}, exp.Expected) } t.AssertEqual("", val) }) } func TestParseStringRegexp(s *testing.T) { t := &core.T{T: s} parse := core.ParseStringRegexp(regexp.MustCompile("Hello( World!)?")) t.Run("Match", func(t *core.T) { val, err := parse("Hello") t.AssertErrorIs(nil, err) t.AssertEqual("Hello", val) val, err = parse("Hello World!") t.AssertErrorIs(nil, err) t.AssertEqual("Hello World!", val) }) t.Run("NoMatch", func(t *core.T) { val, err := parse("something else") t.AssertErrorIs(core.ErrStringRegexpNoMatch, err) t.AssertEqual("", val) }) } func TestParseStringerEnum(s *testing.T) { t := &core.T{T: s, Options: cmp.Options{fakeEnumComparer}} parser := core.ParseStringerEnum(fakeEnumFoo, fakeEnumBar) t.Run("Match", func(t *core.T) { val, err := parser("FOO") t.AssertErrorIs(nil, err) t.AssertEqual(fakeEnumFoo, val) }) t.Run("UnknownValue", func(t *core.T) { val, err := parser("baz") var exp core.UnknownEnumValueError[fakeEnum] if t.AssertErrorAs(&exp, err) { t.AssertEqual("baz", exp.Actual) t.AssertEqual([]fakeEnum{fakeEnumFoo, fakeEnumBar}, exp.Expected) } t.AssertEqual(fakeEnum{}, val) }) } type fakeEnum struct{ string } var ( fakeEnumFoo = fakeEnum{"FOO"} fakeEnumBar = fakeEnum{"BAR"} fakeEnumComparer = cmp.Comparer(func(x, y fakeEnum) bool { return x == y }) ) func (e fakeEnum) String() string { return e.string }