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