github.com/gogo/protobuf@v1.3.2/proto/any_test.go (about) 1 // Go support for Protocol Buffers - Google's data interchange format 2 // 3 // Copyright 2016 The Go Authors. All rights reserved. 4 // https://github.com/golang/protobuf 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // * Neither the name of Google Inc. nor the names of its 17 // contributors may be used to endorse or promote products derived from 18 // this software without specific prior written permission. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32 package proto_test 33 34 import ( 35 "strings" 36 "testing" 37 38 "github.com/gogo/protobuf/proto" 39 40 pb "github.com/gogo/protobuf/proto/proto3_proto" 41 testpb "github.com/gogo/protobuf/proto/test_proto" 42 "github.com/gogo/protobuf/types" 43 ) 44 45 var ( 46 expandedMarshaler = proto.TextMarshaler{ExpandAny: true} 47 expandedCompactMarshaler = proto.TextMarshaler{Compact: true, ExpandAny: true} 48 ) 49 50 // anyEqual reports whether two messages which may be google.protobuf.Any or may 51 // contain google.protobuf.Any fields are equal. We can't use proto.Equal for 52 // comparison, because semantically equivalent messages may be marshaled to 53 // binary in different tag order. Instead, trust that TextMarshaler with 54 // ExpandAny option works and compare the text marshaling results. 55 func anyEqual(got, want proto.Message) bool { 56 // if messages are proto.Equal, no need to marshal. 57 if proto.Equal(got, want) { 58 return true 59 } 60 g := expandedMarshaler.Text(got) 61 w := expandedMarshaler.Text(want) 62 return g == w 63 } 64 65 type golden struct { 66 m proto.Message 67 t, c string 68 } 69 70 var goldenMessages = makeGolden() 71 72 func makeGolden() []golden { 73 nested := &pb.Nested{Bunny: "Monty"} 74 nb, err := proto.Marshal(nested) 75 if err != nil { 76 panic(err) 77 } 78 m1 := &pb.Message{ 79 Name: "David", 80 ResultCount: 47, 81 Anything: &types.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb}, 82 } 83 m2 := &pb.Message{ 84 Name: "David", 85 ResultCount: 47, 86 Anything: &types.Any{TypeUrl: "http://[::1]/type.googleapis.com/" + proto.MessageName(nested), Value: nb}, 87 } 88 m3 := &pb.Message{ 89 Name: "David", 90 ResultCount: 47, 91 Anything: &types.Any{TypeUrl: `type.googleapis.com/"/` + proto.MessageName(nested), Value: nb}, 92 } 93 m4 := &pb.Message{ 94 Name: "David", 95 ResultCount: 47, 96 Anything: &types.Any{TypeUrl: "type.googleapis.com/a/path/" + proto.MessageName(nested), Value: nb}, 97 } 98 m5 := &types.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(nested), Value: nb} 99 100 any1 := &testpb.MyMessage{Count: proto.Int32(47), Name: proto.String("David")} 101 proto.SetExtension(any1, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("foo")}) 102 proto.SetExtension(any1, testpb.E_Ext_Text, proto.String("bar")) 103 any1b, err := proto.Marshal(any1) 104 if err != nil { 105 panic(err) 106 } 107 any2 := &testpb.MyMessage{Count: proto.Int32(42), Bikeshed: testpb.MyMessage_GREEN.Enum(), RepBytes: [][]byte{[]byte("roboto")}} 108 proto.SetExtension(any2, testpb.E_Ext_More, &testpb.Ext{Data: proto.String("baz")}) 109 any2b, err := proto.Marshal(any2) 110 if err != nil { 111 panic(err) 112 } 113 m6 := &pb.Message{ 114 Name: "David", 115 ResultCount: 47, 116 Anything: &types.Any{TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b}, 117 ManyThings: []*types.Any{ 118 {TypeUrl: "type.googleapis.com/" + proto.MessageName(any2), Value: any2b}, 119 {TypeUrl: "type.googleapis.com/" + proto.MessageName(any1), Value: any1b}, 120 }, 121 } 122 123 const ( 124 m1Golden = ` 125 name: "David" 126 result_count: 47 127 anything: < 128 [type.googleapis.com/proto3_proto.Nested]: < 129 bunny: "Monty" 130 > 131 > 132 ` 133 m2Golden = ` 134 name: "David" 135 result_count: 47 136 anything: < 137 ["http://[::1]/type.googleapis.com/proto3_proto.Nested"]: < 138 bunny: "Monty" 139 > 140 > 141 ` 142 m3Golden = ` 143 name: "David" 144 result_count: 47 145 anything: < 146 ["type.googleapis.com/\"/proto3_proto.Nested"]: < 147 bunny: "Monty" 148 > 149 > 150 ` 151 m4Golden = ` 152 name: "David" 153 result_count: 47 154 anything: < 155 [type.googleapis.com/a/path/proto3_proto.Nested]: < 156 bunny: "Monty" 157 > 158 > 159 ` 160 m5Golden = ` 161 [type.googleapis.com/proto3_proto.Nested]: < 162 bunny: "Monty" 163 > 164 ` 165 m6Golden = ` 166 name: "David" 167 result_count: 47 168 anything: < 169 [type.googleapis.com/test_proto.MyMessage]: < 170 count: 47 171 name: "David" 172 [test_proto.Ext.more]: < 173 data: "foo" 174 > 175 [test_proto.Ext.text]: "bar" 176 > 177 > 178 many_things: < 179 [type.googleapis.com/test_proto.MyMessage]: < 180 count: 42 181 bikeshed: GREEN 182 rep_bytes: "roboto" 183 [test_proto.Ext.more]: < 184 data: "baz" 185 > 186 > 187 > 188 many_things: < 189 [type.googleapis.com/test_proto.MyMessage]: < 190 count: 47 191 name: "David" 192 [test_proto.Ext.more]: < 193 data: "foo" 194 > 195 [test_proto.Ext.text]: "bar" 196 > 197 > 198 ` 199 ) 200 return []golden{ 201 {m1, strings.TrimSpace(m1Golden) + "\n", strings.TrimSpace(compact(m1Golden)) + " "}, 202 {m2, strings.TrimSpace(m2Golden) + "\n", strings.TrimSpace(compact(m2Golden)) + " "}, 203 {m3, strings.TrimSpace(m3Golden) + "\n", strings.TrimSpace(compact(m3Golden)) + " "}, 204 {m4, strings.TrimSpace(m4Golden) + "\n", strings.TrimSpace(compact(m4Golden)) + " "}, 205 {m5, strings.TrimSpace(m5Golden) + "\n", strings.TrimSpace(compact(m5Golden)) + " "}, 206 {m6, strings.TrimSpace(m6Golden) + "\n", strings.TrimSpace(compact(m6Golden)) + " "}, 207 } 208 } 209 210 func TestMarshalGolden(t *testing.T) { 211 for _, tt := range goldenMessages { 212 if got, want := expandedMarshaler.Text(tt.m), tt.t; got != want { 213 t.Errorf("message %v: got:\n%s\nwant:\n%s", tt.m, got, want) 214 } 215 if got, want := expandedCompactMarshaler.Text(tt.m), tt.c; got != want { 216 t.Errorf("message %v: got:\n`%s`\nwant:\n`%s`", tt.m, got, want) 217 } 218 } 219 } 220 221 func TestUnmarshalGolden(t *testing.T) { 222 for _, tt := range goldenMessages { 223 want := tt.m 224 got := proto.Clone(tt.m) 225 got.Reset() 226 if err := proto.UnmarshalText(tt.t, got); err != nil { 227 t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.t, err) 228 } 229 if !anyEqual(got, want) { 230 t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.t, got, want) 231 } 232 got.Reset() 233 if err := proto.UnmarshalText(tt.c, got); err != nil { 234 t.Errorf("failed to unmarshal\n%s\nerror: %v", tt.c, err) 235 } 236 if !anyEqual(got, want) { 237 t.Errorf("message:\n%s\ngot:\n%s\nwant:\n%s", tt.c, got, want) 238 } 239 } 240 } 241 242 func TestMarshalUnknownAny(t *testing.T) { 243 m := &pb.Message{ 244 Anything: &types.Any{ 245 TypeUrl: "foo", 246 Value: []byte("bar"), 247 }, 248 } 249 want := `anything: < 250 type_url: "foo" 251 value: "bar" 252 > 253 ` 254 got := expandedMarshaler.Text(m) 255 if got != want { 256 t.Errorf("got\n`%s`\nwant\n`%s`", got, want) 257 } 258 } 259 260 func TestAmbiguousAny(t *testing.T) { 261 pb := &types.Any{} 262 err := proto.UnmarshalText(` 263 type_url: "ttt/proto3_proto.Nested" 264 value: "\n\x05Monty" 265 `, pb) 266 t.Logf("result: %v (error: %v)", expandedMarshaler.Text(pb), err) 267 if err != nil { 268 t.Errorf("failed to parse ambiguous Any message: %v", err) 269 } 270 } 271 272 func TestUnmarshalOverwriteAny(t *testing.T) { 273 pb := &types.Any{} 274 err := proto.UnmarshalText(` 275 [type.googleapis.com/a/path/proto3_proto.Nested]: < 276 bunny: "Monty" 277 > 278 [type.googleapis.com/a/path/proto3_proto.Nested]: < 279 bunny: "Rabbit of Caerbannog" 280 > 281 `, pb) 282 want := `line 7: Any message unpacked multiple times, or "type_url" already set` 283 if err.Error() != want { 284 t.Errorf("incorrect error.\nHave: %v\nWant: %v", err.Error(), want) 285 } 286 } 287 288 func TestUnmarshalAnyMixAndMatch(t *testing.T) { 289 pb := &types.Any{} 290 err := proto.UnmarshalText(` 291 value: "\n\x05Monty" 292 [type.googleapis.com/a/path/proto3_proto.Nested]: < 293 bunny: "Rabbit of Caerbannog" 294 > 295 `, pb) 296 want := `line 5: Any message unpacked multiple times, or "value" already set` 297 if err.Error() != want { 298 t.Errorf("incorrect error.\nHave: %v\nWant: %v", err.Error(), want) 299 } 300 }