diff --git a/test/conformance/BUILD b/test/conformance/BUILD index 31fafb4174f..4ba5ff36368 100644 --- a/test/conformance/BUILD +++ b/test/conformance/BUILD @@ -24,6 +24,7 @@ filegroup( name = "all-srcs", srcs = [ ":package-srcs", + "//test/conformance/behaviors:all-srcs", "//test/conformance/kubetestgen:all-srcs", ], tags = ["automanaged"], diff --git a/test/conformance/behaviors/BUILD b/test/conformance/behaviors/BUILD new file mode 100644 index 00000000000..c628970328e --- /dev/null +++ b/test/conformance/behaviors/BUILD @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["types.go"], + importpath = "k8s.io/kubernetes/test/conformance/behaviors", + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = ["behaviors_test.go"], + data = glob(["*/*.yaml"]), + embed = [":go_default_library"], + deps = [ + "//vendor/gopkg.in/yaml.v2:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/test/conformance/behaviors/behaviors_test.go b/test/conformance/behaviors/behaviors_test.go new file mode 100644 index 00000000000..6bec6bdcbbb --- /dev/null +++ b/test/conformance/behaviors/behaviors_test.go @@ -0,0 +1,75 @@ +/* +Copyright 2020 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 behaviors + +import ( + "io/ioutil" + "os" + "path/filepath" + "regexp" + "testing" + + "gopkg.in/yaml.v2" +) + +func TestValidate(t *testing.T) { + var behaviorFiles []string + + err := filepath.Walk(".", + func(path string, info os.FileInfo, err error) error { + if err != nil { + t.Errorf("%q", err.Error()) + } + + r, _ := regexp.Compile(".+.yaml$") + if r.MatchString(path) { + behaviorFiles = append(behaviorFiles, path) + } + return nil + }) + if err != nil { + t.Errorf("%q", err.Error()) + } + + for _, file := range behaviorFiles { + validateSuite(file, t) + } +} + +func validateSuite(path string, t *testing.T) { + var suite Suite + yamlFile, err := ioutil.ReadFile(path) + if err != nil { + t.Errorf("%q", err.Error()) + } + err = yaml.UnmarshalStrict(yamlFile, &suite) + + if err != nil { + t.Errorf("%q", err.Error()) + } + + behaviorIDList := make(map[string]bool) + + for _, behavior := range suite.Behaviors { + + // Ensure no behavior IDs are duplicated + if _, ok := behaviorIDList[behavior.ID]; ok { + t.Errorf("Duplicate behavior ID: %s", behavior.ID) + } + behaviorIDList[behavior.ID] = true + } +} diff --git a/test/conformance/kubetestgen/types.go b/test/conformance/behaviors/types.go similarity index 95% rename from test/conformance/kubetestgen/types.go rename to test/conformance/behaviors/types.go index 0cbf1b7d806..1632ab3d306 100644 --- a/test/conformance/kubetestgen/types.go +++ b/test/conformance/behaviors/types.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Kubernetes Authors. +Copyright 2020 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. @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package main +package behaviors // Area is a conformance area composed of a list of test suites type Area struct { diff --git a/test/conformance/kubetestgen/BUILD b/test/conformance/kubetestgen/BUILD index d3529c14b0b..3ad33a7eafe 100644 --- a/test/conformance/kubetestgen/BUILD +++ b/test/conformance/kubetestgen/BUILD @@ -2,13 +2,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") go_library( name = "go_default_library", - srcs = [ - "kubetestgen.go", - "types.go", - ], + srcs = ["kubetestgen.go"], importpath = "k8s.io/kubernetes/test/conformance/kubetestgen", visibility = ["//visibility:private"], deps = [ + "//test/conformance/behaviors:go_default_library", "//vendor/github.com/go-openapi/analysis:go_default_library", "//vendor/github.com/go-openapi/loads:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library", diff --git a/test/conformance/kubetestgen/kubetestgen.go b/test/conformance/kubetestgen/kubetestgen.go index d96a19305a0..c47e96baca5 100644 --- a/test/conformance/kubetestgen/kubetestgen.go +++ b/test/conformance/kubetestgen/kubetestgen.go @@ -27,6 +27,7 @@ import ( "github.com/go-openapi/loads" "github.com/go-openapi/spec" "gopkg.in/yaml.v2" + "k8s.io/kubernetes/test/conformance/behaviors" ) type options struct { @@ -67,8 +68,8 @@ func main() { defMap[d.Ref.String()] = d } - var suites []Suite - var suiteMapping = make(map[string]*Suite) + var suites []behaviors.Suite + var suiteMapping = make(map[string]*behaviors.Suite) for _, v := range defs { if !v.TopLevel || o.resource != v.Name { @@ -76,10 +77,10 @@ func main() { } name := trimObjectName(v.Name) - defaultsuite := Suite{ + defaultsuite := behaviors.Suite{ Suite: o.area + "/spec", Description: "Base suite for " + o.area, - Behaviors: []Behavior{}, + Behaviors: []behaviors.Behavior{}, } _ = defaultsuite @@ -89,10 +90,10 @@ func main() { if propSchema.Ref.String() != "" || propSchema.Type[0] == "array" { if _, ok := suiteMapping[id]; !ok { - newsuite := Suite{ + newsuite := behaviors.Suite{ Suite: o.area + "/" + p, Description: "Suite for " + o.area + "/" + p, - Behaviors: []Behavior{}, + Behaviors: []behaviors.Behavior{}, } suiteMapping[id] = &newsuite } @@ -101,10 +102,10 @@ func main() { suiteMapping[id].Behaviors = behaviors } else { if _, ok := suiteMapping["default"]; !ok { - newsuite := Suite{ + newsuite := behaviors.Suite{ Suite: o.area + "/spec", Description: "Base suite for " + o.area, - Behaviors: []Behavior{}, + Behaviors: []behaviors.Behavior{}, } suiteMapping["default"] = &newsuite } @@ -122,12 +123,12 @@ func main() { break } - var area Area = Area{o.area, suites} + var area behaviors.Area = behaviors.Area{Area: o.area, Suites: suites} countFields(suites) printYAML(o.behaviorsDir+o.area, area) } -func printYAML(fileName string, areaO Area) { +func printYAML(fileName string, areaO behaviors.Area) { f, err := os.Create(fileName + ".yaml") if err != nil { fmt.Printf("ERROR: %s\n", err.Error()) @@ -147,7 +148,7 @@ func printYAML(fileName string, areaO Area) { } } -func countFields(suites []Suite) { +func countFields(suites []behaviors.Suite) { var fieldsMapping map[string]int fieldsMapping = make(map[string]int) for _, suite := range suites { @@ -174,21 +175,21 @@ func trimObjectName(name string) string { return name } -func objectBehaviors(id string, s *spec.Schema) []Behavior { +func objectBehaviors(id string, s *spec.Schema) []behaviors.Behavior { if strings.Contains(id, "openAPIV3Schema") || strings.Contains(id, "JSONSchema") || strings.Contains(s.Ref.String(), "JSONSchema") { - return []Behavior{} + return []behaviors.Behavior{} } ref, ok := defMap[s.Ref.String()] if !ok { - return []Behavior{} + return []behaviors.Behavior{} } return schemaBehaviors(id, trimObjectName(ref.Name), ref.Schema) } -func schemaBehaviors(base, apiObject string, s *spec.Schema) []Behavior { - var behaviors []Behavior +func schemaBehaviors(base, apiObject string, s *spec.Schema) []behaviors.Behavior { + var behaviors []behaviors.Behavior for p, propSchema := range s.Properties { b := schemaBehavior(base, apiObject, p, propSchema) behaviors = append(behaviors, b...) @@ -196,21 +197,21 @@ func schemaBehaviors(base, apiObject string, s *spec.Schema) []Behavior { return behaviors } -func schemaBehavior(base, apiObject, p string, propSchema spec.Schema) []Behavior { +func schemaBehavior(base, apiObject, p string, propSchema spec.Schema) []behaviors.Behavior { id := strings.Join([]string{base, p}, "/") if propSchema.Ref.String() != "" { if apiObject == trimObjectName(propSchema.Ref.String()) { - return []Behavior{} + return []behaviors.Behavior{} } return objectBehaviors(id, &propSchema) } - var b []Behavior + var b []behaviors.Behavior switch propSchema.Type[0] { case "array": b = objectBehaviors(id, propSchema.Items.Schema) case "boolean": - b = []Behavior{ + b = []behaviors.Behavior{ { ID: id, APIObject: apiObject, @@ -227,7 +228,7 @@ func schemaBehavior(base, apiObject, p string, propSchema spec.Schema) []Behavio }, } default: - b = []Behavior{{ + b = []behaviors.Behavior{{ ID: id, APIObject: apiObject, APIField: p,