go2idl: Allow multiple values for a comment-tag

This means that tags like:
  // +foo=bar
  // +foo=bat
..will produce []string{"bar", "bat"}.  This is needed for later commits which
will want to use this to make code generation more self contained.
This commit is contained in:
Tim Hockin
2016-06-17 05:17:29 -07:00
parent b01ac4726f
commit 4a00a0fd6d
17 changed files with 300 additions and 52 deletions

View File

@@ -120,7 +120,7 @@ func packageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packag
return generators return generators
}, },
FilterFunc: func(c *generator.Context, t *types.Type) bool { FilterFunc: func(c *generator.Context, t *types.Type) bool {
return types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] == "true" return extractBoolTagOrDie("genclient", t.SecondClosestCommentLines) == true
}, },
} }
} }
@@ -190,7 +190,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
} else { } else {
// User has not specified any override for this group version. // User has not specified any override for this group version.
// filter out types which dont have genclient=true. // filter out types which dont have genclient=true.
if types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] != "true" { if extractBoolTagOrDie("genclient", t.SecondClosestCommentLines) == false {
continue continue
} }
} }

View File

@@ -20,6 +20,8 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/golang/glog"
clientgenargs "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/args" clientgenargs "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/args"
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators/normalization" "k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators/normalization"
"k8s.io/kubernetes/cmd/libs/go2idl/generator" "k8s.io/kubernetes/cmd/libs/go2idl/generator"
@@ -75,11 +77,19 @@ func PackageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packag
return generators return generators
}, },
FilterFunc: func(c *generator.Context, t *types.Type) bool { FilterFunc: func(c *generator.Context, t *types.Type) bool {
return types.ExtractCommentTags("+", t.SecondClosestCommentLines)["genclient"] == "true" return extractBoolTagOrDie("genclient", t.SecondClosestCommentLines) == true
}, },
} }
} }
func extractBoolTagOrDie(key string, lines []string) bool {
val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines)
if err != nil {
glog.Fatalf(err.Error())
}
return val
}
func PackageForClientset(customArgs clientgenargs.Args, typedClientBasePath string, boilerplate []byte, generatedBy string) generator.Package { func PackageForClientset(customArgs clientgenargs.Args, typedClientBasePath string, boilerplate []byte, generatedBy string) generator.Package {
return &generator.DefaultPackage{ return &generator.DefaultPackage{
// TODO: we'll generate fake clientset for different release in the future. // TODO: we'll generate fake clientset for different release in the future.

View File

@@ -72,7 +72,7 @@ func (g *genFakeForGroup) GenerateType(c *generator.Context, t *types.Type, w io
"Group": namer.IC(g.group), "Group": namer.IC(g.group),
"realClientPackage": filepath.Base(g.realClientPath), "realClientPackage": filepath.Base(g.realClientPath),
} }
namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") namespaced := !extractBoolTagOrDie("nonNamespaced", t.SecondClosestCommentLines)
if namespaced { if namespaced {
sw.Do(getterImplNamespaced, wrapper) sw.Do(getterImplNamespaced, wrapper)
} else { } else {

View File

@@ -79,7 +79,7 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
pkg := filepath.Base(t.Name.Package) pkg := filepath.Base(t.Name.Package)
const pkgTestingCore = "k8s.io/kubernetes/pkg/client/testing/core" const pkgTestingCore = "k8s.io/kubernetes/pkg/client/testing/core"
namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") namespaced := !extractBoolTagOrDie("nonNamespaced", t.SecondClosestCommentLines)
canonicalGroup := g.group canonicalGroup := g.group
if canonicalGroup == "core" { if canonicalGroup == "core" {
canonicalGroup = "" canonicalGroup = ""
@@ -96,8 +96,8 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
// allow user to define a group name that's different from the one parsed from the directory. // allow user to define a group name that's different from the one parsed from the directory.
p := c.Universe.Package(g.inputPackage) p := c.Universe.Package(g.inputPackage)
if override, ok := types.ExtractCommentTags("+", p.DocComments)["groupName"]; ok { if override := types.ExtractCommentTags("+", p.DocComments)["groupName"]; override != nil {
groupName = override groupName = override[0]
} }
m := map[string]interface{}{ m := map[string]interface{}{
@@ -136,7 +136,7 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
"NewPatchAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewPatchAction"}), "NewPatchAction": c.Universe.Function(types.Name{Package: pkgTestingCore, Name: "NewPatchAction"}),
} }
noMethods := types.ExtractCommentTags("+", t.SecondClosestCommentLines)["noMethods"] == "true" noMethods := extractBoolTagOrDie("noMethods", t.SecondClosestCommentLines) == true
if namespaced { if namespaced {
sw.Do(structNamespaced, m) sw.Do(structNamespaced, m)

View File

@@ -74,8 +74,8 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
} }
// allow user to define a group name that's different from the one parsed from the directory. // allow user to define a group name that's different from the one parsed from the directory.
p := c.Universe.Package(g.inputPacakge) p := c.Universe.Package(g.inputPacakge)
if override, ok := types.ExtractCommentTags("+", p.DocComments)["groupName"]; ok && override != "" { if override := types.ExtractCommentTags("+", p.DocComments)["groupName"]; override != nil {
groupName = override groupName = override[0]
} }
m := map[string]interface{}{ m := map[string]interface{}{
@@ -101,7 +101,7 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
"type": t, "type": t,
"Group": namer.IC(normalization.BeforeFirstDot(g.group)), "Group": namer.IC(normalization.BeforeFirstDot(g.group)),
} }
namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") namespaced := !extractBoolTagOrDie("nonNamespaced", t.SecondClosestCommentLines)
if namespaced { if namespaced {
sw.Do(getterImplNamespaced, wrapper) sw.Do(getterImplNamespaced, wrapper)
} else { } else {

View File

@@ -66,7 +66,7 @@ func hasStatus(t *types.Type) bool {
func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
pkg := filepath.Base(t.Name.Package) pkg := filepath.Base(t.Name.Package)
namespaced := !(types.ExtractCommentTags("+", t.SecondClosestCommentLines)["nonNamespaced"] == "true") namespaced := !extractBoolTagOrDie("nonNamespaced", t.SecondClosestCommentLines)
m := map[string]interface{}{ m := map[string]interface{}{
"type": t, "type": t,
"package": pkg, "package": pkg,
@@ -86,7 +86,7 @@ func (g *genClientForType) GenerateType(c *generator.Context, t *types.Type, w i
} else { } else {
sw.Do(getterNonNamesapced, m) sw.Do(getterNonNamesapced, m)
} }
noMethods := types.ExtractCommentTags("+", t.SecondClosestCommentLines)["noMethods"] == "true" noMethods := extractBoolTagOrDie("noMethods", t.SecondClosestCommentLines) == true
sw.Do(interfaceTemplate1, m) sw.Do(interfaceTemplate1, m)
if !noMethods { if !noMethods {

View File

@@ -0,0 +1,33 @@
/*
Copyright 2016 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 generators
import (
"github.com/golang/glog"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
)
// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if
// it exists, the value is boolean. If the tag did not exist, it returns
// false.
func extractBoolTagOrDie(key string, lines []string) bool {
val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines)
if err != nil {
glog.Fatalf(err.Error())
}
return val
}

View File

@@ -232,7 +232,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
// Only generate conversions for package which explicitly requested it // Only generate conversions for package which explicitly requested it
// byt setting "+genversion=true" in their doc.go file. // byt setting "+genversion=true" in their doc.go file.
filtered := false filtered := false
if types.ExtractCommentTags("+", p.DocComments)["genconversion"] == "true" { if extractBoolTagOrDie("genconversion", false, p.DocComments) == true {
filtered = true filtered = true
} }
if !filtered { if !filtered {
@@ -342,7 +342,7 @@ func isDirectlyConvertible(in, out *types.Type, preexisting conversions) bool {
// "+ genconversion=false" // "+ genconversion=false"
// comment to ignore this field for conversion. // comment to ignore this field for conversion.
// TODO: Switch to SecondClosestCommentLines. // TODO: Switch to SecondClosestCommentLines.
if types.ExtractCommentTags("+", inMember.CommentLines)["genconversion"] == "false" { if extractBoolTagOrDie("genconversion", true, inMember.CommentLines) == false {
continue continue
} }
return false return false
@@ -428,7 +428,7 @@ func (g *genConversion) convertibleOnlyWithinPackage(inType, outType *types.Type
if t.Name.Package != g.targetPackage { if t.Name.Package != g.targetPackage {
return false return false
} }
if types.ExtractCommentTags("+", t.CommentLines)["genconversion"] == "false" { if extractBoolTagOrDie("genconversion", true, t.CommentLines) == false {
return false return false
} }
// TODO: Consider generating functions for other kinds too. // TODO: Consider generating functions for other kinds too.

View File

@@ -0,0 +1,33 @@
/*
Copyright 2016 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 generators
import (
"github.com/golang/glog"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
)
// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if
// it exists, the value is boolean. If the tag did not exist, it returns
// defaultVal.
func extractBoolTagOrDie(key string, defaultVal bool, lines []string) bool {
val, err := types.ExtractSingleBoolCommentTag("+", key, defaultVal, lines)
if err != nil {
glog.Fatalf(err.Error())
}
return val
}

View File

@@ -200,7 +200,7 @@ func isRootedUnder(pkg string, roots []string) bool {
func copyableWithinPackage(t *types.Type, boundingDirs []string) bool { func copyableWithinPackage(t *types.Type, boundingDirs []string) bool {
// If the type opts out of copy-generation, stop. // If the type opts out of copy-generation, stop.
if types.ExtractCommentTags("+", t.CommentLines)["gencopy"] == "false" { if extractBoolTagOrDie("gencopy", true, t.CommentLines) == false {
return false return false
} }
// Only packages within the restricted range can be processed. // Only packages within the restricted range can be processed.

View File

@@ -0,0 +1,33 @@
/*
Copyright 2016 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 generators
import (
"github.com/golang/glog"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
)
// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if
// it exists, the value is boolean. If the tag did not exist, it returns
// defaultVal.
func extractBoolTagOrDie(key string, defaultVal bool, lines []string) bool {
val, err := types.ExtractSingleBoolCommentTag("+", key, defaultVal, lines)
if err != nil {
glog.Fatalf(err.Error())
}
return val
}

View File

@@ -25,6 +25,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/golang/glog"
"k8s.io/kubernetes/cmd/libs/go2idl/generator" "k8s.io/kubernetes/cmd/libs/go2idl/generator"
"k8s.io/kubernetes/cmd/libs/go2idl/namer" "k8s.io/kubernetes/cmd/libs/go2idl/namer"
"k8s.io/kubernetes/cmd/libs/go2idl/types" "k8s.io/kubernetes/cmd/libs/go2idl/types"
@@ -70,13 +72,20 @@ func (g *genProtoIDL) Namers(c *generator.Context) namer.NameSystems {
// Filter ignores types that are identified as not exportable. // Filter ignores types that are identified as not exportable.
func (g *genProtoIDL) Filter(c *generator.Context, t *types.Type) bool { func (g *genProtoIDL) Filter(c *generator.Context, t *types.Type) bool {
flags := types.ExtractCommentTags("+", t.CommentLines) tagVals := types.ExtractCommentTags("+", t.CommentLines)["protobuf"]
switch { if tagVals != nil {
case flags["protobuf"] == "false": if tagVals[0] == "false" {
// Type specified "false".
return false return false
case flags["protobuf"] == "true": }
if tagVals[0] == "true" {
// Type specified "true".
return true return true
case !g.generateAll: }
glog.Fatalf(`Comment tag "protobuf" must be true or false, found: %q`, tagVals[0])
}
if !g.generateAll {
// We're not generating everything.
return false return false
} }
seen := map[*types.Type]bool{} seen := map[*types.Type]bool{}
@@ -125,7 +134,7 @@ func isOptionalAlias(t *types.Type) bool {
if t.Underlying == nil || (t.Underlying.Kind != types.Map && t.Underlying.Kind != types.Slice) { if t.Underlying == nil || (t.Underlying.Kind != types.Map && t.Underlying.Kind != types.Slice) {
return false return false
} }
if types.ExtractCommentTags("+", t.CommentLines)["protobuf.nullable"] != "true" { if extractBoolTagOrDie("protobuf.nullable", t.CommentLines) == false {
return false return false
} }
return true return true
@@ -296,7 +305,7 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error {
key := strings.TrimPrefix(k, "protobuf.options.") key := strings.TrimPrefix(k, "protobuf.options.")
switch key { switch key {
case "marshal": case "marshal":
if v == "false" { if v[0] == "false" {
if !b.omitGogo { if !b.omitGogo {
options = append(options, options = append(options,
"(gogoproto.marshaler) = false", "(gogoproto.marshaler) = false",
@@ -307,14 +316,14 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error {
} }
default: default:
if !b.omitGogo || !strings.HasPrefix(key, "(gogoproto.") { if !b.omitGogo || !strings.HasPrefix(key, "(gogoproto.") {
options = append(options, fmt.Sprintf("%s = %s", key, v)) options = append(options, fmt.Sprintf("%s = %s", key, v[0]))
} }
} }
// protobuf.as allows a type to have the same message contents as another Go type // protobuf.as allows a type to have the same message contents as another Go type
case k == "protobuf.as": case k == "protobuf.as":
fields = nil fields = nil
if alias = b.locator.GoTypeForName(types.Name{Name: v}); alias == nil { if alias = b.locator.GoTypeForName(types.Name{Name: v[0]}); alias == nil {
return fmt.Errorf("type %v references alias %q which does not exist", b.t, v) return fmt.Errorf("type %v references alias %q which does not exist", b.t, v[0])
} }
// protobuf.embed instructs the generator to use the named type in this package // protobuf.embed instructs the generator to use the named type in this package
// as an embedded message. // as an embedded message.
@@ -322,10 +331,10 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error {
fields = []protoField{ fields = []protoField{
{ {
Tag: 1, Tag: 1,
Name: v, Name: v[0],
Type: &types.Type{ Type: &types.Type{
Name: types.Name{ Name: types.Name{
Name: v, Name: v[0],
Package: b.localPackage.Package, Package: b.localPackage.Package,
Path: b.localPackage.Path, Path: b.localPackage.Path,
}, },

View File

@@ -0,0 +1,33 @@
/*
Copyright 2016 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 protobuf
import (
"github.com/golang/glog"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
)
// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if
// it exists, the value is boolean. If the tag did not exist, it returns
// false.
func extractBoolTagOrDie(key string, lines []string) bool {
val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines)
if err != nil {
glog.Fatalf(err.Error())
}
return val
}

View File

@@ -108,7 +108,7 @@ func Packages(_ *generator.Context, arguments *args.GeneratorArgs) generator.Pac
// // +genset // // +genset
// or // or
// // +genset=true // // +genset=true
return types.ExtractCommentTags("+", t.CommentLines)["genset"] == "true" return extractBoolTagOrDie("genset", t.CommentLines) == true
} }
return false return false
}, },

View File

@@ -0,0 +1,33 @@
/*
Copyright 2016 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 generators
import (
"github.com/golang/glog"
"k8s.io/kubernetes/cmd/libs/go2idl/types"
)
// extractBoolTagOrDie gets the comment-tags for the key and asserts that, if
// it exists, the value is boolean. If the tag did not exist, it returns
// false.
func extractBoolTagOrDie(key string, lines []string) bool {
val, err := types.ExtractSingleBoolCommentTag("+", key, false, lines)
if err != nil {
glog.Fatalf(err.Error())
}
return val
}

View File

@@ -19,6 +19,7 @@ limitations under the License.
package types package types
import ( import (
"fmt"
"strings" "strings"
) )
@@ -26,21 +27,18 @@ import (
// //
// 'marker' + "key1=value1,key2=value2". // 'marker' + "key1=value1,key2=value2".
// //
// Values are optional; 'true' is the default. If a key is set multiple times, // Values are optional; "" is the default. A tag can be specified more than
// the last one wins. // one time and all values are returned. If the resulting map has an entry for
// a key, the value (a slice) is guaranteed to have at least 1 element.
// //
// Example: if you pass "+" for 'marker', and the following two lines are in // Example: if you pass "+" for 'marker', and the following two lines are in
// the comments: // the comments:
// +foo=value1,bar // +foo=value1,bar
// +foo=value2,baz="frobber" // +foo=value2,baz="qux"
// Then this function will return: // Then this function will return:
// map[string]string{"foo":"value2", "bar": "true", "baz": "frobber"} // map[string][]string{"foo":{"value1, "value2"}, "bar": {""}, "baz": {"qux"}}
// func ExtractCommentTags(marker string, lines []string) map[string][]string {
// TODO: Basically we need to define a standard way of giving instructions to out := map[string][]string{}
// autogenerators in the comments of a type. This is a first iteration of that.
// TODO: allow multiple values per key?
func ExtractCommentTags(marker string, lines []string) map[string]string {
out := map[string]string{}
for _, line := range lines { for _, line := range lines {
line = strings.Trim(line, " ") line = strings.Trim(line, " ")
if len(line) == 0 { if len(line) == 0 {
@@ -53,11 +51,32 @@ func ExtractCommentTags(marker string, lines []string) map[string]string {
for _, p := range pairs { for _, p := range pairs {
kv := strings.Split(p, "=") kv := strings.Split(p, "=")
if len(kv) == 2 { if len(kv) == 2 {
out[kv[0]] = kv[1] out[kv[0]] = append(out[kv[0]], kv[1])
} else if len(kv) == 1 { } else if len(kv) == 1 {
out[kv[0]] = "true" out[kv[0]] = append(out[kv[0]], "")
} }
} }
} }
return out return out
} }
// ExtractSingleBoolCommentTag parses comments for lines of the form:
//
// 'marker' + "key=value1"
//
// If the tag is not found, the default value is returned. Values are asserted
// to be boolean ("true" or "false"), and any other value will cause an error
// to be returned. If the key has multiple values, the first one will be used.
func ExtractSingleBoolCommentTag(marker string, key string, defaultVal bool, lines []string) (bool, error) {
values := ExtractCommentTags(marker, lines)[key]
if values == nil {
return defaultVal, nil
}
if values[0] == "true" {
return true, nil
}
if values[0] == "false" {
return false, nil
}
return false, fmt.Errorf("tag value for %q is not boolean: %q", key, values[0])
}

View File

@@ -18,18 +18,63 @@ package types
import ( import (
"reflect" "reflect"
"strings"
"testing" "testing"
) )
func TestExtractCommentTags(t *testing.T) { func TestExtractCommentTags(t *testing.T) {
commentLines := ` commentLines := []string{
Human comment that is ignored. "Human comment that is ignored.",
+foo=value1,bar "+foo=value1,bar",
+foo=value2,baz=frobber "+foo=value2,baz=qux",
` }
a := ExtractCommentTags("+", commentLines) a := ExtractCommentTags("+", commentLines)
e := map[string]string{"foo": "value2", "bar": "true", "baz": "frobber"} e := map[string][]string{"foo": {"value1", "value2"}, "bar": {""}, "baz": {"qux"}}
if !reflect.DeepEqual(e, a) { if !reflect.DeepEqual(e, a) {
t.Errorf("Wanted %#v, got %#v", e, a) t.Errorf("Wanted %q, got %q", e, a)
}
}
func TestExtractSingleBoolCommentTag(t *testing.T) {
commentLines := []string{
"Human comment that is ignored.",
"+TRUE=true",
"+FALSE=false",
"+MULTI=true",
"+MULTI=false",
"+MULTI=multi",
"+NOTBOOL=blue",
"+EMPTY",
}
testCases := []struct {
key string
def bool
exp bool
err string // if set, ignore exp.
}{
{"TRUE", false, true, ""},
{"FALSE", true, false, ""},
{"MULTI", false, true, ""},
{"NOTBOOL", false, true, "is not boolean"},
{"EMPTY", false, true, "is not boolean"},
{"ABSENT", true, true, ""},
{"ABSENT", false, false, ""},
}
for i, tc := range testCases {
v, err := ExtractSingleBoolCommentTag("+", tc.key, tc.def, commentLines)
if err != nil && tc.err == "" {
t.Errorf("[%d]: unexpected failure: %v", i, err)
} else if err == nil && tc.err != "" {
t.Errorf("[%d]: expected failure: %v", i, tc.err)
} else if err != nil {
if !strings.Contains(err.Error(), tc.err) {
t.Errorf("[%d]: unexpected error: expected %q, got %q", i, tc.err, err)
}
} else if v != tc.exp {
t.Errorf("[%d]: unexpected value: expected %t, got %t", i, tc.exp, v)
}
} }
} }