github.com/googleapis/api-linter@v1.65.2/rules/aip0191/file_option_consistency_test.go (about) 1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package aip0191 16 17 import ( 18 "reflect" 19 "sort" 20 "testing" 21 22 "github.com/googleapis/api-linter/lint" 23 "github.com/googleapis/api-linter/rules/internal/testutils" 24 "github.com/jhump/protoreflect/desc" 25 "github.com/jhump/protoreflect/desc/builder" 26 "github.com/stoewer/go-strcase" 27 "google.golang.org/protobuf/proto" 28 dpb "google.golang.org/protobuf/types/descriptorpb" 29 ) 30 31 func TestFileOptionConsistency(t *testing.T) { 32 // Make a "control file". 33 // This is a situation where using a builder is much easier 34 // than parsing a proto template. 35 controlFile := builder.NewFile("control.proto").SetPackageName( 36 "google.example.v1", 37 ).SetOptions(getOptions(nil)) 38 39 // Run our tests. Each test will make a "test file" that imports the 40 // control file. 41 for _, test := range []consistencyTest{ 42 {"ValidSame", map[string]string{}}, 43 {"InvalidCsharpNamespaceMissing", map[string]string{"csharp_namespace": ""}}, 44 {"InvalidCsharpNamespaceMismatch", map[string]string{"csharp_namespace": "Example.V1"}}, 45 {"InvalidJavaPackageMissing", map[string]string{"java_package": ""}}, 46 {"InvalidJavaPackageMismatch", map[string]string{"java_package": "com.example.v1"}}, 47 {"InvalidPhpNamespaceMissing", map[string]string{"php_namespace": ""}}, 48 {"InvalidPhpNamespaceMismatch", map[string]string{"php_namespace": "Example\\V1"}}, 49 {"InvalidPhpMetadataNamespace", map[string]string{"php_metadata_namespace": "Example\\V1"}}, 50 {"InvalidPhpClassPrefix", map[string]string{"php_class_prefix": "ExampleProto"}}, 51 {"InvalidObjcClassPrefixMissing", map[string]string{"objc_class_prefix": ""}}, 52 {"InvalidObjcClassPrefixMismatch", map[string]string{"objc_class_prefix": "GEXV1"}}, 53 {"InvalidRubyPackageMissing", map[string]string{"ruby_package": ""}}, 54 {"InvalidRubyPackageMismatch", map[string]string{"ruby_package": "Example::V1"}}, 55 {"InvalidSwiftPrefixMissing", map[string]string{"swift_prefix": ""}}, 56 {"InvalidSwiftPrefixMismatch", map[string]string{"swift_prefix": "ExampleProto"}}, 57 {"InvalidLotsOfReasons", map[string]string{ 58 "csharp_namespace": "Example.V1", 59 "go_package": "google.golang.org/googleapis/genproto/googleapis/example/v1;example", 60 "java_package": "com.example.v1", 61 "ruby_package": "Example::V1", 62 "swift_prefix": "", 63 }}, 64 } { 65 t.Run(test.name, func(t *testing.T) { 66 // Build our test file, which imports the control file. 67 testFile, err := builder.NewFile("test.proto").AddDependency( 68 controlFile, 69 ).SetPackageName("google.example.v1").SetOptions(getOptions(test.options)).Build() 70 if err != nil { 71 t.Fatalf("Could not build test file.") 72 } 73 if diff := test.getProblems(testFile).Diff(fileOptionConsistency.Lint(testFile)); diff != "" { 74 t.Errorf(diff) 75 } 76 }) 77 } 78 79 // Also ensure separate packages are ignored. 80 t.Run("ValidDifferentPackages", func(t *testing.T) { 81 testFile, err := builder.NewFile("test.proto").AddDependency(controlFile).SetOptions( 82 getOptions(map[string]string{"java_package": "com.wrong"}), 83 ).SetPackageName("google.different.v1").Build() 84 if err != nil { 85 t.Fatalf("Could not build test file.") 86 } 87 if diff := (testutils.Problems{}).Diff(fileOptionConsistency.Lint(testFile)); diff != "" { 88 t.Errorf(diff) 89 } 90 }) 91 } 92 93 func getOptions(fileopts map[string]string) *dpb.FileOptions { 94 opts := &dpb.FileOptions{ 95 CsharpNamespace: proto.String("Google.Example.V1"), 96 GoPackage: proto.String("github.com/googleapis/genproto/googleapis/example/v1;example"), 97 JavaPackage: proto.String("com.google.example.v1"), 98 PhpNamespace: proto.String("Google\\Example\\V1"), 99 ObjcClassPrefix: proto.String("GEX"), 100 RubyPackage: proto.String("Google::Example::V1"), 101 SwiftPrefix: proto.String("Google_Example_V1"), 102 } 103 for k, v := range fileopts { 104 field := strcase.UpperCamelCase(k) 105 reflect.ValueOf(opts).Elem().FieldByName(field).Set(reflect.ValueOf(proto.String(v))) 106 } 107 return opts 108 } 109 110 type consistencyTest struct { 111 name string 112 options map[string]string 113 } 114 115 func (ct *consistencyTest) getProblems(f *desc.FileDescriptor) testutils.Problems { 116 p := testutils.Problems{} 117 for k := range ct.options { 118 p = append(p, lint.Problem{Message: k, Descriptor: f}) 119 } 120 sort.Slice(p, func(i, j int) bool { 121 return p[i].Message < p[j].Message 122 }) 123 return p 124 }