| /* |
| Copyright 2018 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 ( |
| "reflect" |
| "strings" |
| "testing" |
| |
| "k8s.io/gengo/examples/set-gen/sets" |
| "k8s.io/gengo/types" |
| ) |
| |
| func TestSingleTagExtension(t *testing.T) { |
| |
| // Comments only contain one tag extension and one value. |
| var tests = []struct { |
| comments []string |
| extensionTag string |
| extensionName string |
| extensionValues []string |
| }{ |
| { |
| comments: []string{"+patchMergeKey=name"}, |
| extensionTag: "patchMergeKey", |
| extensionName: "x-kubernetes-patch-merge-key", |
| extensionValues: []string{"name"}, |
| }, |
| { |
| comments: []string{"+patchStrategy=merge"}, |
| extensionTag: "patchStrategy", |
| extensionName: "x-kubernetes-patch-strategy", |
| extensionValues: []string{"merge"}, |
| }, |
| { |
| comments: []string{"+listType=atomic"}, |
| extensionTag: "listType", |
| extensionName: "x-kubernetes-list-type", |
| extensionValues: []string{"atomic"}, |
| }, |
| { |
| comments: []string{"+listMapKey=port"}, |
| extensionTag: "listMapKey", |
| extensionName: "x-kubernetes-list-map-keys", |
| extensionValues: []string{"port"}, |
| }, |
| { |
| comments: []string{"+k8s:openapi-gen=x-kubernetes-member-tag:member_test"}, |
| extensionTag: "k8s:openapi-gen", |
| extensionName: "x-kubernetes-member-tag", |
| extensionValues: []string{"member_test"}, |
| }, |
| { |
| comments: []string{"+k8s:openapi-gen=x-kubernetes-member-tag:member_test:member_test2"}, |
| extensionTag: "k8s:openapi-gen", |
| extensionName: "x-kubernetes-member-tag", |
| extensionValues: []string{"member_test:member_test2"}, |
| }, |
| { |
| // Test that poorly formatted extensions aren't added. |
| comments: []string{ |
| "+k8s:openapi-gen=x-kubernetes-no-value", |
| "+k8s:openapi-gen=x-kubernetes-member-success:success", |
| "+k8s:openapi-gen=x-kubernetes-wrong-separator;error", |
| }, |
| extensionTag: "k8s:openapi-gen", |
| extensionName: "x-kubernetes-member-success", |
| extensionValues: []string{"success"}, |
| }, |
| } |
| for _, test := range tests { |
| extensions, _ := parseExtensions(test.comments) |
| actual := extensions[0] |
| if actual.idlTag != test.extensionTag { |
| t.Errorf("Extension Tag: expected (%s), actual (%s)\n", test.extensionTag, actual.idlTag) |
| } |
| if actual.xName != test.extensionName { |
| t.Errorf("Extension Name: expected (%s), actual (%s)\n", test.extensionName, actual.xName) |
| } |
| if !reflect.DeepEqual(actual.values, test.extensionValues) { |
| t.Errorf("Extension Values: expected (%s), actual (%s)\n", test.extensionValues, actual.values) |
| } |
| if actual.hasMultipleValues() { |
| t.Errorf("%s: hasMultipleValues() should be false\n", actual.xName) |
| } |
| } |
| |
| } |
| |
| func TestMultipleTagExtensions(t *testing.T) { |
| |
| var tests = []struct { |
| comments []string |
| extensionTag string |
| extensionName string |
| extensionValues []string |
| }{ |
| { |
| comments: []string{ |
| "+listMapKey=port", |
| "+listMapKey=protocol", |
| }, |
| extensionTag: "listMapKey", |
| extensionName: "x-kubernetes-list-map-keys", |
| extensionValues: []string{"port", "protocol"}, |
| }, |
| } |
| for _, test := range tests { |
| extensions, errors := parseExtensions(test.comments) |
| if len(errors) > 0 { |
| t.Errorf("Unexpected errors: %v\n", errors) |
| } |
| actual := extensions[0] |
| if actual.idlTag != test.extensionTag { |
| t.Errorf("Extension Tag: expected (%s), actual (%s)\n", test.extensionTag, actual.idlTag) |
| } |
| if actual.xName != test.extensionName { |
| t.Errorf("Extension Name: expected (%s), actual (%s)\n", test.extensionName, actual.xName) |
| } |
| if !reflect.DeepEqual(actual.values, test.extensionValues) { |
| t.Errorf("Extension Values: expected (%s), actual (%s)\n", test.extensionValues, actual.values) |
| } |
| if !actual.hasMultipleValues() { |
| t.Errorf("%s: hasMultipleValues() should be true\n", actual.xName) |
| } |
| } |
| |
| } |
| |
| func TestExtensionParseErrors(t *testing.T) { |
| |
| var tests = []struct { |
| comments []string |
| errorMessage string |
| }{ |
| { |
| // Missing extension value should be an error. |
| comments: []string{ |
| "+k8s:openapi-gen=x-kubernetes-no-value", |
| }, |
| errorMessage: "x-kubernetes-no-value", |
| }, |
| { |
| // Wrong separator should be an error. |
| comments: []string{ |
| "+k8s:openapi-gen=x-kubernetes-wrong-separator;error", |
| }, |
| errorMessage: "x-kubernetes-wrong-separator;error", |
| }, |
| } |
| |
| for _, test := range tests { |
| _, errors := parseExtensions(test.comments) |
| if len(errors) == 0 { |
| t.Errorf("Expected errors while parsing: %v\n", test.comments) |
| } |
| error := errors[0] |
| if !strings.Contains(error.Error(), test.errorMessage) { |
| t.Errorf("Error (%v) should contain substring (%s)\n", error, test.errorMessage) |
| } |
| } |
| } |
| |
| func TestExtensionAllowedValues(t *testing.T) { |
| |
| var methodTests = []struct { |
| e extension |
| allowedValues sets.String |
| }{ |
| { |
| e: extension{ |
| idlTag: "patchStrategy", |
| }, |
| allowedValues: sets.NewString("merge", "retainKeys"), |
| }, |
| { |
| e: extension{ |
| idlTag: "patchMergeKey", |
| }, |
| allowedValues: nil, |
| }, |
| { |
| e: extension{ |
| idlTag: "listType", |
| }, |
| allowedValues: sets.NewString("atomic", "set", "map"), |
| }, |
| { |
| e: extension{ |
| idlTag: "listMapKey", |
| }, |
| allowedValues: nil, |
| }, |
| { |
| e: extension{ |
| idlTag: "k8s:openapi-gen", |
| }, |
| allowedValues: nil, |
| }, |
| } |
| for _, test := range methodTests { |
| if test.allowedValues != nil { |
| if !test.e.hasAllowedValues() { |
| t.Errorf("hasAllowedValues() expected (true), but received: false") |
| } |
| if !reflect.DeepEqual(test.allowedValues, test.e.allowedValues()) { |
| t.Errorf("allowedValues() expected (%v), but received: %v", |
| test.allowedValues, test.e.allowedValues()) |
| } |
| } |
| if test.allowedValues == nil && test.e.hasAllowedValues() { |
| t.Errorf("hasAllowedValues() expected (false), but received: true") |
| } |
| } |
| |
| var successTests = []struct { |
| e extension |
| }{ |
| { |
| e: extension{ |
| idlTag: "patchStrategy", |
| xName: "x-kubernetes-patch-strategy", |
| values: []string{"merge"}, |
| }, |
| }, |
| { |
| // Validate multiple values. |
| e: extension{ |
| idlTag: "patchStrategy", |
| xName: "x-kubernetes-patch-strategy", |
| values: []string{"merge", "retainKeys"}, |
| }, |
| }, |
| { |
| e: extension{ |
| idlTag: "patchMergeKey", |
| xName: "x-kubernetes-patch-merge-key", |
| values: []string{"key1"}, |
| }, |
| }, |
| { |
| e: extension{ |
| idlTag: "listType", |
| xName: "x-kubernetes-list-type", |
| values: []string{"atomic"}, |
| }, |
| }, |
| } |
| for _, test := range successTests { |
| actualErr := test.e.validateAllowedValues() |
| if actualErr != nil { |
| t.Errorf("Expected no error for (%v), but received: %v\n", test.e, actualErr) |
| } |
| } |
| |
| var failureTests = []struct { |
| e extension |
| }{ |
| { |
| // Every value must be allowed. |
| e: extension{ |
| idlTag: "patchStrategy", |
| xName: "x-kubernetes-patch-strategy", |
| values: []string{"disallowed", "merge"}, |
| }, |
| }, |
| { |
| e: extension{ |
| idlTag: "patchStrategy", |
| xName: "x-kubernetes-patch-strategy", |
| values: []string{"foo"}, |
| }, |
| }, |
| { |
| e: extension{ |
| idlTag: "listType", |
| xName: "x-kubernetes-list-type", |
| values: []string{"not-allowed"}, |
| }, |
| }, |
| } |
| for _, test := range failureTests { |
| actualErr := test.e.validateAllowedValues() |
| if actualErr == nil { |
| t.Errorf("Expected error, but received none: %v\n", test.e) |
| } |
| } |
| |
| } |
| |
| func TestExtensionKind(t *testing.T) { |
| |
| var methodTests = []struct { |
| e extension |
| kind types.Kind |
| }{ |
| { |
| e: extension{ |
| idlTag: "patchStrategy", |
| }, |
| kind: types.Slice, |
| }, |
| { |
| e: extension{ |
| idlTag: "patchMergeKey", |
| }, |
| kind: types.Slice, |
| }, |
| { |
| e: extension{ |
| idlTag: "listType", |
| }, |
| kind: types.Slice, |
| }, |
| { |
| e: extension{ |
| idlTag: "listMapKey", |
| }, |
| kind: types.Slice, |
| }, |
| { |
| e: extension{ |
| idlTag: "k8s:openapi-gen", |
| }, |
| kind: "", |
| }, |
| } |
| for _, test := range methodTests { |
| if len(test.kind) > 0 { |
| if !test.e.hasKind() { |
| t.Errorf("%v: hasKind() expected (true), but received: false", test.e) |
| } |
| if test.kind != test.e.kind() { |
| t.Errorf("%v: kind() expected (%v), but received: %v", test.e, test.kind, test.e.kind()) |
| } |
| } else { |
| if test.e.hasKind() { |
| t.Errorf("%v: hasKind() expected (false), but received: true", test.e) |
| } |
| } |
| } |
| } |
| |
| func TestValidateMemberExtensions(t *testing.T) { |
| |
| patchStrategyExtension := extension{ |
| idlTag: "patchStrategy", |
| xName: "x-kubernetes-patch-strategy", |
| values: []string{"merge"}, |
| } |
| patchMergeKeyExtension := extension{ |
| idlTag: "patchMergeKey", |
| xName: "x-kubernetes-patch-merge-key", |
| values: []string{"key1", "key2"}, |
| } |
| listTypeExtension := extension{ |
| idlTag: "listType", |
| xName: "x-kubernetes-list-type", |
| values: []string{"atomic"}, |
| } |
| listMapKeysExtension := extension{ |
| idlTag: "listMapKey", |
| xName: "x-kubernetes-map-keys", |
| values: []string{"key1"}, |
| } |
| genExtension := extension{ |
| idlTag: "k8s:openapi-gen", |
| xName: "x-kubernetes-member-type", |
| values: []string{"value1"}, |
| } |
| |
| sliceField := types.Member{ |
| Name: "Containers", |
| Type: &types.Type{ |
| Kind: types.Slice, |
| }, |
| } |
| mapField := types.Member{ |
| Name: "Containers", |
| Type: &types.Type{ |
| Kind: types.Map, |
| }, |
| } |
| |
| var successTests = []struct { |
| extensions []extension |
| member types.Member |
| }{ |
| // Test single member extension |
| { |
| extensions: []extension{patchStrategyExtension}, |
| member: sliceField, |
| }, |
| // Test multiple member extensions |
| { |
| extensions: []extension{ |
| patchMergeKeyExtension, |
| listTypeExtension, |
| listMapKeysExtension, |
| genExtension, // Should not generate errors during type validation |
| }, |
| member: sliceField, |
| }, |
| } |
| for _, test := range successTests { |
| errors := validateMemberExtensions(test.extensions, &test.member) |
| if len(errors) > 0 { |
| t.Errorf("validateMemberExtensions: %v should have produced no errors. Errors: %v", |
| test.extensions, errors) |
| } |
| } |
| |
| var failureTests = []struct { |
| extensions []extension |
| member types.Member |
| }{ |
| // Test single member extension |
| { |
| extensions: []extension{patchStrategyExtension}, |
| member: mapField, |
| }, |
| // Test multiple member extensions |
| { |
| extensions: []extension{ |
| patchMergeKeyExtension, |
| listTypeExtension, |
| listMapKeysExtension, |
| }, |
| member: mapField, |
| }, |
| } |
| for _, test := range failureTests { |
| errors := validateMemberExtensions(test.extensions, &test.member) |
| if len(errors) != len(test.extensions) { |
| t.Errorf("validateMemberExtensions: %v should have produced all errors. Errors: %v", |
| test.extensions, errors) |
| } |
| } |
| |
| } |