github.com/gogo/protobuf@v1.3.2/proto/clone_test.go (about) 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 32 package proto_test 33 34 import ( 35 "testing" 36 37 "github.com/gogo/protobuf/proto" 38 39 proto3pb "github.com/gogo/protobuf/proto/proto3_proto" 40 pb "github.com/gogo/protobuf/proto/test_proto" 41 ) 42 43 var 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 63 func 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 72 func 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 101 func 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 108 var 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: {F: proto.Float64(2.0)}, 198 0x4002: { 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: { 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: {F: proto.Float64(2.0)}, 223 0x4002: { 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 { 249 src: &pb.Communique{ 250 Union: &pb.Communique_Number{Number: 41}, 251 }, 252 dst: &pb.Communique{ 253 Union: &pb.Communique_Name{Name: "Bobby Tables"}, 254 }, 255 want: &pb.Communique{ 256 Union: &pb.Communique_Number{Number: 41}, 257 }, 258 }, 259 { // Oneof nil is the same as not set. 260 src: &pb.Communique{}, 261 dst: &pb.Communique{Union: &pb.Communique_Name{Name: "Bobby Tables"}}, 262 want: &pb.Communique{Union: &pb.Communique_Name{Name: "Bobby Tables"}}, 263 }, 264 { 265 src: &pb.Communique{Union: &pb.Communique_Number{Number: 1337}}, 266 dst: &pb.Communique{}, 267 want: &pb.Communique{Union: &pb.Communique_Number{Number: 1337}}, 268 }, 269 { 270 src: &pb.Communique{Union: &pb.Communique_Col{Col: pb.MyMessage_RED}}, 271 dst: &pb.Communique{}, 272 want: &pb.Communique{Union: &pb.Communique_Col{Col: pb.MyMessage_RED}}, 273 }, 274 { 275 src: &pb.Communique{Union: &pb.Communique_Data{Data: []byte("hello")}}, 276 dst: &pb.Communique{}, 277 want: &pb.Communique{Union: &pb.Communique_Data{Data: []byte("hello")}}, 278 }, 279 { 280 src: &pb.Communique{Union: &pb.Communique_Msg{Msg: &pb.Strings{BytesField: []byte{1, 2, 3}}}}, 281 dst: &pb.Communique{}, 282 want: &pb.Communique{Union: &pb.Communique_Msg{Msg: &pb.Strings{BytesField: []byte{1, 2, 3}}}}, 283 }, 284 { 285 src: &pb.Communique{Union: &pb.Communique_Msg{}}, 286 dst: &pb.Communique{}, 287 want: &pb.Communique{Union: &pb.Communique_Msg{}}, 288 }, 289 { 290 src: &pb.Communique{Union: &pb.Communique_Msg{Msg: &pb.Strings{StringField: proto.String("123")}}}, 291 dst: &pb.Communique{Union: &pb.Communique_Msg{Msg: &pb.Strings{BytesField: []byte{1, 2, 3}}}}, 292 want: &pb.Communique{Union: &pb.Communique_Msg{Msg: &pb.Strings{StringField: proto.String("123"), BytesField: []byte{1, 2, 3}}}}, 293 }, 294 { 295 src: &proto3pb.Message{ 296 Terrain: map[string]*proto3pb.Nested{ 297 "kay_a": {Cute: true}, // replace 298 "kay_b": {Bunny: "rabbit"}, // insert 299 }, 300 }, 301 dst: &proto3pb.Message{ 302 Terrain: map[string]*proto3pb.Nested{ 303 "kay_a": {Bunny: "lost"}, // replaced 304 "kay_c": {Bunny: "bunny"}, // keep 305 }, 306 }, 307 want: &proto3pb.Message{ 308 Terrain: map[string]*proto3pb.Nested{ 309 "kay_a": {Cute: true}, 310 "kay_b": {Bunny: "rabbit"}, 311 "kay_c": {Bunny: "bunny"}, 312 }, 313 }, 314 }, 315 { 316 src: &pb.GoTest{ 317 F_BoolRepeated: []bool{}, 318 F_Int32Repeated: []int32{}, 319 F_Int64Repeated: []int64{}, 320 F_Uint32Repeated: []uint32{}, 321 F_Uint64Repeated: []uint64{}, 322 F_FloatRepeated: []float32{}, 323 F_DoubleRepeated: []float64{}, 324 F_StringRepeated: []string{}, 325 F_BytesRepeated: [][]byte{}, 326 }, 327 dst: &pb.GoTest{}, 328 want: &pb.GoTest{ 329 F_BoolRepeated: []bool{}, 330 F_Int32Repeated: []int32{}, 331 F_Int64Repeated: []int64{}, 332 F_Uint32Repeated: []uint32{}, 333 F_Uint64Repeated: []uint64{}, 334 F_FloatRepeated: []float32{}, 335 F_DoubleRepeated: []float64{}, 336 F_StringRepeated: []string{}, 337 F_BytesRepeated: [][]byte{}, 338 }, 339 }, 340 { 341 src: &pb.GoTest{}, 342 dst: &pb.GoTest{ 343 F_BoolRepeated: []bool{}, 344 F_Int32Repeated: []int32{}, 345 F_Int64Repeated: []int64{}, 346 F_Uint32Repeated: []uint32{}, 347 F_Uint64Repeated: []uint64{}, 348 F_FloatRepeated: []float32{}, 349 F_DoubleRepeated: []float64{}, 350 F_StringRepeated: []string{}, 351 F_BytesRepeated: [][]byte{}, 352 }, 353 want: &pb.GoTest{ 354 F_BoolRepeated: []bool{}, 355 F_Int32Repeated: []int32{}, 356 F_Int64Repeated: []int64{}, 357 F_Uint32Repeated: []uint32{}, 358 F_Uint64Repeated: []uint64{}, 359 F_FloatRepeated: []float32{}, 360 F_DoubleRepeated: []float64{}, 361 F_StringRepeated: []string{}, 362 F_BytesRepeated: [][]byte{}, 363 }, 364 }, 365 { 366 src: &pb.GoTest{ 367 F_BytesRepeated: [][]byte{nil, {}, {0}}, 368 }, 369 dst: &pb.GoTest{}, 370 want: &pb.GoTest{ 371 F_BytesRepeated: [][]byte{nil, {}, {0}}, 372 }, 373 }, 374 { 375 src: &pb.MyMessage{ 376 Others: []*pb.OtherMessage{}, 377 }, 378 dst: &pb.MyMessage{}, 379 want: &pb.MyMessage{ 380 Others: []*pb.OtherMessage{}, 381 }, 382 }, 383 } 384 385 func TestMerge(t *testing.T) { 386 for _, m := range mergeTests { 387 got := proto.Clone(m.dst) 388 if !proto.Equal(got, m.dst) { 389 t.Errorf("Clone()\ngot %v\nwant %v", got, m.dst) 390 continue 391 } 392 proto.Merge(got, m.src) 393 if !proto.Equal(got, m.want) { 394 t.Errorf("Merge(%v, %v)\ngot %v\nwant %v", m.dst, m.src, got, m.want) 395 } 396 } 397 }