blob: 02aacb3b582207f69e0aa7bae248b3c14713aa37 [file] [log] [blame]
// Copyright 2016 The LUCI 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 flagenum
import (
"encoding/json"
"flag"
"fmt"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
type testValue string
const (
testFoo testValue = "test-foo"
testBar testValue = "test-bar"
testEmpty testValue = "test-empty"
testQuoted testValue = `test-'"`
)
var (
testEnum = Enum{
"foo": testFoo,
"bar": testBar,
"": testEmpty,
`'"`: testQuoted,
"failboat": 123,
}
)
func (tv *testValue) Set(v string) error {
return testEnum.FlagSet(tv, v)
}
func (tv *testValue) String() string {
return testEnum.FlagString(tv)
}
func (tv *testValue) UnmarshalJSON(data []byte) error {
return testEnum.JSONUnmarshal(tv, data)
}
func (tv testValue) MarshalJSON() ([]byte, error) {
return testEnum.JSONMarshal(tv)
}
type testStruct struct {
Name string
A string
}
var (
testStructFoo = testStruct{"testStructFoo", "FOO"}
testStructBar = testStruct{"testStructBar", "BAR"}
testStructQuotes = testStruct{"testStructQuotes", "QUOTES"}
testStructEnum = Enum{
"foo": testStructFoo,
"bar": testStructBar,
`'"`: testStructQuotes,
}
)
func (tv *testStruct) Set(v string) error {
return testStructEnum.FlagSet(tv, v)
}
func (tv *testStruct) String() string {
return testStructEnum.FlagString(tv)
}
func (tv *testStruct) UnmarshalJSON(data []byte) error {
return testStructEnum.JSONUnmarshal(tv, data)
}
func (tv testStruct) MarshalJSON() ([]byte, error) {
return testStructEnum.JSONMarshal(tv)
}
func TestEnum(t *testing.T) {
Convey(`A testing enumeration`, t, func() {
Convey(`String for "testFoo" is "foo".`, func() {
So(testEnum.GetKey(testFoo), ShouldEqual, "foo")
})
Convey(`String for "testQuoted" is ['"].`, func() {
So(testEnum.GetKey(testQuoted), ShouldEqual, `'"`)
})
Convey(`String for unregistered type is "".`, func() {
So(testEnum.GetKey(0x10), ShouldEqual, "")
})
Convey(`Lists choices as: "", '", bar, failboat, foo.`, func() {
So(testEnum.Choices(), ShouldEqual, `"", '", bar, failboat, foo`)
})
})
}
func TestStringEnum(t *testing.T) {
Convey(`A testing enumeration for a string type`, t, func() {
Convey(`Panics when deserialized from an incompatible type.`, func() {
var value testValue
So(func() { testEnum.setValue(&value, "failboat") }, ShouldPanic)
})
Convey(`Panics when attempting to set an unsettable value.`, func() {
So(func() { testEnum.setValue(123, "foo") }, ShouldPanic)
})
Convey(`Binds to an flag field.`, func() {
var value testValue
Convey(`When used as a flag`, func() {
fs := flag.NewFlagSet("test", flag.ContinueOnError)
fs.Var(&value, "test", "Test flag.")
Convey(`Sets "value" when set.`, func() {
err := fs.Parse([]string{"-test", "bar"})
So(err, ShouldBeNil)
So(value, ShouldEqual, testBar)
})
Convey(`Rejects an invalid enum mapping.`, func() {
err := fs.Parse([]string{"-test", "baz"})
So(err, ShouldNotBeNil)
})
})
Convey(`When marshalling to JSON`, func() {
var s struct {
Value testValue `json:"value"`
}
Convey(`Marshals to JSON as its enumeration value.`, func() {
s.Value = testFoo
data, err := json.Marshal(&s)
So(err, ShouldBeNil)
So(string(data), ShouldEqual, `{"value":"foo"}`)
Convey(`And unmarshals to its Value.`, func() {
err := json.Unmarshal(data, &s)
So(err, ShouldBeNil)
So(s.Value, ShouldEqual, testFoo)
})
})
Convey(`Unmarshals {"value":"bar"} to "testBar".`, func() {
err := json.Unmarshal([]byte(`{"value":"bar"}`), &s)
So(err, ShouldBeNil)
So(s.Value, ShouldEqual, testBar)
})
Convey(`Fails to Unmarshal invalid JSON.`, func() {
err := json.Unmarshal([]byte(`{"value":123}`), &s)
So(err, ShouldNotBeNil)
})
})
})
})
}
func TestStructEnum(t *testing.T) {
Convey(`A testing enumeration for a struct type`, t, func() {
Convey(`Panics when attempting to set an unsettable value.`, func() {
So(func() { testStructEnum.setValue(123, "foo") }, ShouldPanic)
})
Convey(`Binds to an flag field.`, func() {
var value testStruct
testCases := []struct {
V string
S testStruct
}{
{"foo", testStructFoo},
{"bar", testStructBar},
{`'"`, testStructQuotes},
}
Convey(`When used as a flag`, func() {
fs := flag.NewFlagSet("test", flag.ContinueOnError)
fs.Var(&value, "test", "Test flag.")
for _, tc := range testCases {
Convey(fmt.Sprintf(`Sets "value" to %s when set to [%s].`, tc.S.Name, tc.V), func() {
err := fs.Parse([]string{"-test", tc.V})
So(err, ShouldBeNil)
So(value, ShouldResemble, tc.S)
})
}
Convey(`Rejects an invalid enum mapping.`, func() {
err := fs.Parse([]string{"-test", "baz"})
So(err, ShouldNotBeNil)
})
})
Convey(`When marshalling to JSON`, func() {
var s struct {
Value testStruct `json:"value"`
}
for _, tc := range testCases {
Convey(fmt.Sprintf(`Marshals %s to JSON as its enumeration value, [%s].`,
tc.S.Name, tc.V), func() {
s.Value = tc.S
data, err := json.Marshal(&s)
So(err, ShouldBeNil)
mkey, _ := json.Marshal(&tc.V)
So(string(data), ShouldEqual, fmt.Sprintf(`{"value":%s}`, string(mkey)))
Convey(`And unmarshals to its Value.`, func() {
err := json.Unmarshal(data, &s)
So(err, ShouldBeNil)
So(s.Value, ShouldResemble, tc.S)
})
})
}
Convey(`Unmarshals {"value":"bar"} to "testStructBar".`, func() {
err := json.Unmarshal([]byte(`{"value":"bar"}`), &s)
So(err, ShouldBeNil)
So(s.Value, ShouldResemble, testStructBar)
})
Convey(`Fails to Unmarshal invalid JSON.`, func() {
err := json.Unmarshal([]byte(`{"value":123}`), &s)
So(err, ShouldNotBeNil)
})
})
})
})
}