go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/proto/google/descutil/printer/printer_test.go (about) 1 // Copyright 2021 The LUCI Authors. 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 // http://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 printer 16 17 import ( 18 "bytes" 19 "os" 20 "strings" 21 "testing" 22 23 "google.golang.org/protobuf/proto" 24 "google.golang.org/protobuf/types/descriptorpb" 25 "google.golang.org/protobuf/types/known/structpb" 26 27 "go.chromium.org/luci/common/proto/google/descutil" 28 29 // Register proto extensions defined in util.proto. 30 _ "go.chromium.org/luci/common/proto/google/descutil/internal" 31 32 . "github.com/smartystreets/goconvey/convey" 33 ) 34 35 func TestPrinter(t *testing.T) { 36 t.Parallel() 37 38 Convey("Printer", t, func() { 39 protoFile, err := os.ReadFile("../internal/util.proto") 40 So(err, ShouldBeNil) 41 protoFileLines := strings.Split(string(protoFile), "\n") 42 43 descFileBytes, err := os.ReadFile("../internal/util.desc") 44 So(err, ShouldBeNil) 45 46 var desc descriptorpb.FileDescriptorSet 47 err = proto.Unmarshal(descFileBytes, &desc) 48 So(err, ShouldBeNil) 49 50 var file *descriptorpb.FileDescriptorProto 51 for _, filePb := range desc.File { 52 if filePb.GetName() == "go.chromium.org/luci/common/proto/google/descutil/internal/util.proto" { 53 file = filePb 54 break 55 } 56 } 57 // we must find the util_test.proto file in `desc` 58 So(file, ShouldNotBeNil) 59 60 sourceCodeInfo, err := descutil.IndexSourceCodeInfo(file) 61 So(err, ShouldBeNil) 62 63 getExpectedDef := func(ptr any, unindent int) string { 64 loc := sourceCodeInfo[ptr] 65 So(loc, ShouldNotBeNil) 66 startLine := loc.Span[0] 67 endLine := startLine 68 if len(loc.Span) > 3 { 69 endLine = loc.Span[2] 70 } 71 72 for startLine > 0 && strings.HasPrefix(strings.TrimSpace(protoFileLines[startLine-1]), "//") { 73 startLine-- 74 } 75 76 expected := make([]string, endLine-startLine+1) 77 for i := 0; i < len(expected); i++ { 78 expected[i] = protoFileLines[int(startLine)+i][unindent:] 79 } 80 81 return strings.Join(expected, "\n") + "\n" 82 } 83 84 var buf bytes.Buffer 85 printer := NewPrinter(&buf) 86 So(printer.SetFile(file), ShouldBeNil) 87 88 checkOutput := func(ptr any, unindent int) { 89 So(buf.String(), ShouldEqual, getExpectedDef(ptr, unindent)) 90 } 91 92 Convey("package", func() { 93 printer.Package(file.GetPackage()) 94 checkOutput(file.Package, 0) 95 }) 96 97 Convey("service", func() { 98 for _, s := range file.Service { 99 Convey(s.GetName(), func() { 100 printer.Service(s, -1) 101 checkOutput(s, 0) 102 }) 103 } 104 }) 105 106 testEnum := func(e *descriptorpb.EnumDescriptorProto, unindent int) { 107 Convey(e.GetName(), func() { 108 printer.Enum(e) 109 checkOutput(e, unindent) 110 }) 111 } 112 113 Convey("enum", func() { 114 for _, e := range file.EnumType { 115 testEnum(e, 0) 116 } 117 }) 118 119 Convey("message", func() { 120 var testMsg func(*descriptorpb.DescriptorProto, int) 121 testMsg = func(m *descriptorpb.DescriptorProto, unindent int) { 122 Convey(m.GetName(), func() { 123 if len(m.NestedType) == 0 && len(m.EnumType) == 0 { 124 printer.Message(m) 125 checkOutput(m, unindent) 126 } else { 127 for _, m := range m.NestedType { 128 testMsg(m, unindent+1) 129 } 130 for _, e := range m.EnumType { 131 testEnum(e, unindent+1) 132 } 133 } 134 }) 135 } 136 for _, m := range file.MessageType { 137 testMsg(m, 0) 138 } 139 }) 140 141 Convey("synthesized message", func() { 142 myFakeMessage := mkMessage( 143 "myMessage", 144 mkField("f1", 1, descriptorpb.FieldDescriptorProto_TYPE_STRING, nil), 145 mkField("st", 2, descriptorpb.FieldDescriptorProto_TYPE_MESSAGE, &structpb.Struct{}), 146 ) 147 printer.AppendLeadingComments(myFakeMessage, []string{"Message comment", "second line."}) 148 printer.AppendLeadingComments(myFakeMessage.Field[0], []string{"simple string"}) 149 printer.AppendLeadingComments(myFakeMessage.Field[1], []string{"cool message type"}) 150 151 printer.Message(myFakeMessage) 152 So(buf.String(), ShouldEqual, `// Message comment 153 // second line. 154 message myMessage { 155 // simple string 156 string f1 = 1; 157 // cool message type 158 google.protobuf.Struct st = 2; 159 } 160 `) 161 }) 162 }) 163 } 164 165 func mkField(name string, num int32, typ descriptorpb.FieldDescriptorProto_Type, msg proto.Message) *descriptorpb.FieldDescriptorProto { 166 ret := &descriptorpb.FieldDescriptorProto{} 167 ret.Name = &name 168 camelName := camel(name) 169 ret.JsonName = &camelName 170 ret.Number = &num 171 ret.Type = &typ 172 if typ == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE { 173 fn := string(msg.ProtoReflect().Descriptor().FullName()) 174 ret.TypeName = &fn 175 } 176 return ret 177 } 178 179 func mkMessage(name string, fields ...*descriptorpb.FieldDescriptorProto) *descriptorpb.DescriptorProto { 180 ret := &descriptorpb.DescriptorProto{} 181 ret.Name = &name 182 ret.Field = fields 183 return ret 184 }