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