1// Go support for Protocol Buffers - Google's data interchange format 2// 3// Copyright 2011 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 32package proto_test 33 34import ( 35 "testing" 36 37 "github.com/golang/protobuf/proto" 38 39 proto3pb "github.com/golang/protobuf/proto/proto3_proto" 40 pb "github.com/golang/protobuf/proto/test_proto" 41) 42 43var cloneTestMessage = &pb.MyMessage{ 44 Count: proto.Int32(42), 45 Name: proto.String("Dave"), 46 Pet: []string{"bunny", "kitty", "horsey"}, 47 Inner: &pb.InnerMessage{ 48 Host: proto.String("niles"), 49 Port: proto.Int32(9099), 50 Connected: proto.Bool(true), 51 }, 52 Others: []*pb.OtherMessage{ 53 { 54 Value: []byte("some bytes"), 55 }, 56 }, 57 Somegroup: &pb.MyMessage_SomeGroup{ 58 GroupField: proto.Int32(6), 59 }, 60 RepBytes: [][]byte{[]byte("sham"), []byte("wow")}, 61} 62 63func init() { 64 ext := &pb.Ext{ 65 Data: proto.String("extension"), 66 } 67 if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_More, ext); err != nil { 68 panic("SetExtension: " + err.Error()) 69 } 70 if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_Text, proto.String("hello")); err != nil { 71 panic("SetExtension: " + err.Error()) 72 } 73 if err := proto.SetExtension(cloneTestMessage, pb.E_Greeting, []string{"one", "two"}); err != nil { 74 panic("SetExtension: " + err.Error()) 75 } 76} 77 78func TestClone(t *testing.T) { 79 // Create a clone using a marshal/unmarshal roundtrip. 80 vanilla := new(pb.MyMessage) 81 b, err := proto.Marshal(cloneTestMessage) 82 if err != nil { 83 t.Errorf("unexpected Marshal error: %v", err) 84 } 85 if err := proto.Unmarshal(b, vanilla); err != nil { 86 t.Errorf("unexpected Unarshal error: %v", err) 87 } 88 89 // Create a clone using Clone and verify that it is equal to the original. 90 m := proto.Clone(cloneTestMessage).(*pb.MyMessage) 91 if !proto.Equal(m, cloneTestMessage) { 92 t.Fatalf("Clone(%v) = %v", cloneTestMessage, m) 93 } 94 95 // Mutate the clone, which should not affect the original. 96 x1, err := proto.GetExtension(m, pb.E_Ext_More) 97 if err != nil { 98 t.Errorf("unexpected GetExtension(%v) error: %v", pb.E_Ext_More.Name, err) 99 } 100 x2, err := proto.GetExtension(m, pb.E_Ext_Text) 101 if err != nil { 102 t.Errorf("unexpected GetExtension(%v) error: %v", pb.E_Ext_Text.Name, err) 103 } 104 x3, err := proto.GetExtension(m, pb.E_Greeting) 105 if err != nil { 106 t.Errorf("unexpected GetExtension(%v) error: %v", pb.E_Greeting.Name, err) 107 } 108 *m.Inner.Port++ 109 *(x1.(*pb.Ext)).Data = "blah blah" 110 *(x2.(*string)) = "goodbye" 111 x3.([]string)[0] = "zero" 112 if !proto.Equal(cloneTestMessage, vanilla) { 113 t.Fatalf("mutation on original detected:\ngot %v\nwant %v", cloneTestMessage, vanilla) 114 } 115} 116 117func TestCloneNil(t *testing.T) { 118 var m *pb.MyMessage 119 if c := proto.Clone(m); !proto.Equal(m, c) { 120 t.Errorf("Clone(%v) = %v", m, c) 121 } 122} 123 124var mergeTests = []struct { 125 src, dst, want proto.Message 126}{ 127 { 128 src: &pb.MyMessage{ 129 Count: proto.Int32(42), 130 }, 131 dst: &pb.MyMessage{ 132 Name: proto.String("Dave"), 133 }, 134 want: &pb.MyMessage{ 135 Count: proto.Int32(42), 136 Name: proto.String("Dave"), 137 }, 138 }, 139 { 140 src: &pb.MyMessage{ 141 Inner: &pb.InnerMessage{ 142 Host: proto.String("hey"), 143 Connected: proto.Bool(true), 144 }, 145 Pet: []string{"horsey"}, 146 Others: []*pb.OtherMessage{ 147 { 148 Value: []byte("some bytes"), 149 }, 150 }, 151 }, 152 dst: &pb.MyMessage{ 153 Inner: &pb.InnerMessage{ 154 Host: proto.String("niles"), 155 Port: proto.Int32(9099), 156 }, 157 Pet: []string{"bunny", "kitty"}, 158 Others: []*pb.OtherMessage{ 159 { 160 Key: proto.Int64(31415926535), 161 }, 162 { 163 // Explicitly test a src=nil field 164 Inner: nil, 165 }, 166 }, 167 }, 168 want: &pb.MyMessage{ 169 Inner: &pb.InnerMessage{ 170 Host: proto.String("hey"), 171 Connected: proto.Bool(true), 172 Port: proto.Int32(9099), 173 }, 174 Pet: []string{"bunny", "kitty", "horsey"}, 175 Others: []*pb.OtherMessage{ 176 { 177 Key: proto.Int64(31415926535), 178 }, 179 {}, 180 { 181 Value: []byte("some bytes"), 182 }, 183 }, 184 }, 185 }, 186 { 187 src: &pb.MyMessage{ 188 RepBytes: [][]byte{[]byte("wow")}, 189 }, 190 dst: &pb.MyMessage{ 191 Somegroup: &pb.MyMessage_SomeGroup{ 192 GroupField: proto.Int32(6), 193 }, 194 RepBytes: [][]byte{[]byte("sham")}, 195 }, 196 want: &pb.MyMessage{ 197 Somegroup: &pb.MyMessage_SomeGroup{ 198 GroupField: proto.Int32(6), 199 }, 200 RepBytes: [][]byte{[]byte("sham"), []byte("wow")}, 201 }, 202 }, 203 // Check that a scalar bytes field replaces rather than appends. 204 { 205 src: &pb.OtherMessage{Value: []byte("foo")}, 206 dst: &pb.OtherMessage{Value: []byte("bar")}, 207 want: &pb.OtherMessage{Value: []byte("foo")}, 208 }, 209 { 210 src: &pb.MessageWithMap{ 211 NameMapping: map[int32]string{6: "Nigel"}, 212 MsgMapping: map[int64]*pb.FloatingPoint{ 213 0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)}, 214 0x4002: &pb.FloatingPoint{ 215 F: proto.Float64(2.0), 216 }, 217 }, 218 ByteMapping: map[bool][]byte{true: []byte("wowsa")}, 219 }, 220 dst: &pb.MessageWithMap{ 221 NameMapping: map[int32]string{ 222 6: "Bruce", // should be overwritten 223 7: "Andrew", 224 }, 225 MsgMapping: map[int64]*pb.FloatingPoint{ 226 0x4002: &pb.FloatingPoint{ 227 F: proto.Float64(3.0), 228 Exact: proto.Bool(true), 229 }, // the entire message should be overwritten 230 }, 231 }, 232 want: &pb.MessageWithMap{ 233 NameMapping: map[int32]string{ 234 6: "Nigel", 235 7: "Andrew", 236 }, 237 MsgMapping: map[int64]*pb.FloatingPoint{ 238 0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)}, 239 0x4002: &pb.FloatingPoint{ 240 F: proto.Float64(2.0), 241 }, 242 }, 243 ByteMapping: map[bool][]byte{true: []byte("wowsa")}, 244 }, 245 }, 246 // proto3 shouldn't merge zero values, 247 // in the same way that proto2 shouldn't merge nils. 248 { 249 src: &proto3pb.Message{ 250 Name: "Aaron", 251 Data: []byte(""), // zero value, but not nil 252 }, 253 dst: &proto3pb.Message{ 254 HeightInCm: 176, 255 Data: []byte("texas!"), 256 }, 257 want: &proto3pb.Message{ 258 Name: "Aaron", 259 HeightInCm: 176, 260 Data: []byte("texas!"), 261 }, 262 }, 263 { // Oneof fields should merge by assignment. 264 src: &pb.Communique{Union: &pb.Communique_Number{41}}, 265 dst: &pb.Communique{Union: &pb.Communique_Name{"Bobby Tables"}}, 266 want: &pb.Communique{Union: &pb.Communique_Number{41}}, 267 }, 268 { // Oneof nil is the same as not set. 269 src: &pb.Communique{}, 270 dst: &pb.Communique{Union: &pb.Communique_Name{"Bobby Tables"}}, 271 want: &pb.Communique{Union: &pb.Communique_Name{"Bobby Tables"}}, 272 }, 273 { 274 src: &pb.Communique{Union: &pb.Communique_Number{1337}}, 275 dst: &pb.Communique{}, 276 want: &pb.Communique{Union: &pb.Communique_Number{1337}}, 277 }, 278 { 279 src: &pb.Communique{Union: &pb.Communique_Col{pb.MyMessage_RED}}, 280 dst: &pb.Communique{}, 281 want: &pb.Communique{Union: &pb.Communique_Col{pb.MyMessage_RED}}, 282 }, 283 { 284 src: &pb.Communique{Union: &pb.Communique_Data{[]byte("hello")}}, 285 dst: &pb.Communique{}, 286 want: &pb.Communique{Union: &pb.Communique_Data{[]byte("hello")}}, 287 }, 288 { 289 src: &pb.Communique{Union: &pb.Communique_Msg{&pb.Strings{BytesField: []byte{1, 2, 3}}}}, 290 dst: &pb.Communique{}, 291 want: &pb.Communique{Union: &pb.Communique_Msg{&pb.Strings{BytesField: []byte{1, 2, 3}}}}, 292 }, 293 { 294 src: &pb.Communique{Union: &pb.Communique_Msg{}}, 295 dst: &pb.Communique{}, 296 want: &pb.Communique{Union: &pb.Communique_Msg{}}, 297 }, 298 { 299 src: &pb.Communique{Union: &pb.Communique_Msg{&pb.Strings{StringField: proto.String("123")}}}, 300 dst: &pb.Communique{Union: &pb.Communique_Msg{&pb.Strings{BytesField: []byte{1, 2, 3}}}}, 301 want: &pb.Communique{Union: &pb.Communique_Msg{&pb.Strings{StringField: proto.String("123"), BytesField: []byte{1, 2, 3}}}}, 302 }, 303 { 304 src: &proto3pb.Message{ 305 Terrain: map[string]*proto3pb.Nested{ 306 "kay_a": &proto3pb.Nested{Cute: true}, // replace 307 "kay_b": &proto3pb.Nested{Bunny: "rabbit"}, // insert 308 }, 309 }, 310 dst: &proto3pb.Message{ 311 Terrain: map[string]*proto3pb.Nested{ 312 "kay_a": &proto3pb.Nested{Bunny: "lost"}, // replaced 313 "kay_c": &proto3pb.Nested{Bunny: "bunny"}, // keep 314 }, 315 }, 316 want: &proto3pb.Message{ 317 Terrain: map[string]*proto3pb.Nested{ 318 "kay_a": &proto3pb.Nested{Cute: true}, 319 "kay_b": &proto3pb.Nested{Bunny: "rabbit"}, 320 "kay_c": &proto3pb.Nested{Bunny: "bunny"}, 321 }, 322 }, 323 }, 324 { 325 src: &pb.GoTest{ 326 F_BoolRepeated: []bool{}, 327 F_Int32Repeated: []int32{}, 328 F_Int64Repeated: []int64{}, 329 F_Uint32Repeated: []uint32{}, 330 F_Uint64Repeated: []uint64{}, 331 F_FloatRepeated: []float32{}, 332 F_DoubleRepeated: []float64{}, 333 F_StringRepeated: []string{}, 334 F_BytesRepeated: [][]byte{}, 335 }, 336 dst: &pb.GoTest{}, 337 want: &pb.GoTest{ 338 F_BoolRepeated: []bool{}, 339 F_Int32Repeated: []int32{}, 340 F_Int64Repeated: []int64{}, 341 F_Uint32Repeated: []uint32{}, 342 F_Uint64Repeated: []uint64{}, 343 F_FloatRepeated: []float32{}, 344 F_DoubleRepeated: []float64{}, 345 F_StringRepeated: []string{}, 346 F_BytesRepeated: [][]byte{}, 347 }, 348 }, 349 { 350 src: &pb.GoTest{}, 351 dst: &pb.GoTest{ 352 F_BoolRepeated: []bool{}, 353 F_Int32Repeated: []int32{}, 354 F_Int64Repeated: []int64{}, 355 F_Uint32Repeated: []uint32{}, 356 F_Uint64Repeated: []uint64{}, 357 F_FloatRepeated: []float32{}, 358 F_DoubleRepeated: []float64{}, 359 F_StringRepeated: []string{}, 360 F_BytesRepeated: [][]byte{}, 361 }, 362 want: &pb.GoTest{ 363 F_BoolRepeated: []bool{}, 364 F_Int32Repeated: []int32{}, 365 F_Int64Repeated: []int64{}, 366 F_Uint32Repeated: []uint32{}, 367 F_Uint64Repeated: []uint64{}, 368 F_FloatRepeated: []float32{}, 369 F_DoubleRepeated: []float64{}, 370 F_StringRepeated: []string{}, 371 F_BytesRepeated: [][]byte{}, 372 }, 373 }, 374 { 375 src: &pb.GoTest{ 376 F_BytesRepeated: [][]byte{nil, []byte{}, []byte{0}}, 377 }, 378 dst: &pb.GoTest{}, 379 want: &pb.GoTest{ 380 F_BytesRepeated: [][]byte{nil, []byte{}, []byte{0}}, 381 }, 382 }, 383 { 384 src: &pb.MyMessage{ 385 Others: []*pb.OtherMessage{}, 386 }, 387 dst: &pb.MyMessage{}, 388 want: &pb.MyMessage{ 389 Others: []*pb.OtherMessage{}, 390 }, 391 }, 392} 393 394func TestMerge(t *testing.T) { 395 for _, m := range mergeTests { 396 got := proto.Clone(m.dst) 397 if !proto.Equal(got, m.dst) { 398 t.Errorf("Clone()\ngot %v\nwant %v", got, m.dst) 399 continue 400 } 401 proto.Merge(got, m.src) 402 if !proto.Equal(got, m.want) { 403 t.Errorf("Merge(%v, %v)\ngot %v\nwant %v", m.dst, m.src, got, m.want) 404 } 405 } 406} 407