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