1// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2017 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/gogo/protobuf/proto"
38	proto3pb "github.com/gogo/protobuf/proto/proto3_proto"
39	pb "github.com/gogo/protobuf/proto/test_proto"
40)
41
42func TestDiscardUnknown(t *testing.T) {
43	tests := []struct {
44		desc     string
45		in, want proto.Message
46	}{{
47		desc: "Nil",
48		in:   nil, want: nil, // Should not panic
49	}, {
50		desc: "NilPtr",
51		in:   (*proto3pb.Message)(nil), want: (*proto3pb.Message)(nil), // Should not panic
52	}, {
53		desc: "Nested",
54		in: &proto3pb.Message{
55			Name:             "Aaron",
56			Nested:           &proto3pb.Nested{Cute: true, XXX_unrecognized: []byte("blah")},
57			XXX_unrecognized: []byte("blah"),
58		},
59		want: &proto3pb.Message{
60			Name:   "Aaron",
61			Nested: &proto3pb.Nested{Cute: true},
62		},
63	}, {
64		desc: "Slice",
65		in: &proto3pb.Message{
66			Name: "Aaron",
67			Children: []*proto3pb.Message{
68				{Name: "Sarah", XXX_unrecognized: []byte("blah")},
69				{Name: "Abraham", XXX_unrecognized: []byte("blah")},
70			},
71			XXX_unrecognized: []byte("blah"),
72		},
73		want: &proto3pb.Message{
74			Name: "Aaron",
75			Children: []*proto3pb.Message{
76				{Name: "Sarah"},
77				{Name: "Abraham"},
78			},
79		},
80	}, {
81		desc: "OneOf",
82		in: &pb.Communique{
83			Union: &pb.Communique_Msg{Msg: &pb.Strings{
84				StringField:      proto.String("123"),
85				XXX_unrecognized: []byte("blah"),
86			}},
87			XXX_unrecognized: []byte("blah"),
88		},
89		want: &pb.Communique{
90			Union: &pb.Communique_Msg{Msg: &pb.Strings{StringField: proto.String("123")}},
91		},
92	}, {
93		desc: "Map",
94		in: &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{
95			0x4002: {
96				Exact:            proto.Bool(true),
97				XXX_unrecognized: []byte("blah"),
98			},
99		}},
100		want: &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{
101			0x4002: {Exact: proto.Bool(true)},
102		}},
103	}, {
104		desc: "Extension",
105		in: func() proto.Message {
106			m := &pb.MyMessage{
107				Count: proto.Int32(42),
108				Somegroup: &pb.MyMessage_SomeGroup{
109					GroupField:       proto.Int32(6),
110					XXX_unrecognized: []byte("blah"),
111				},
112				XXX_unrecognized: []byte("blah"),
113			}
114			proto.SetExtension(m, pb.E_Ext_More, &pb.Ext{
115				Data:             proto.String("extension"),
116				XXX_unrecognized: []byte("blah"),
117			})
118			return m
119		}(),
120		want: func() proto.Message {
121			m := &pb.MyMessage{
122				Count:     proto.Int32(42),
123				Somegroup: &pb.MyMessage_SomeGroup{GroupField: proto.Int32(6)},
124			}
125			proto.SetExtension(m, pb.E_Ext_More, &pb.Ext{Data: proto.String("extension")})
126			return m
127		}(),
128	}}
129
130	// Test the legacy code path.
131	for _, tt := range tests {
132		// Clone the input so that we don't alter the original.
133		in := tt.in
134		if in != nil {
135			in = proto.Clone(tt.in)
136		}
137
138		var m LegacyMessage
139		m.Message, _ = in.(*proto3pb.Message)
140		m.Communique, _ = in.(*pb.Communique)
141		m.MessageWithMap, _ = in.(*pb.MessageWithMap)
142		m.MyMessage, _ = in.(*pb.MyMessage)
143		proto.DiscardUnknown(&m)
144		if !proto.Equal(in, tt.want) {
145			t.Errorf("test %s/Legacy, expected unknown fields to be discarded\ngot  %v\nwant %v", tt.desc, in, tt.want)
146		}
147	}
148
149	for _, tt := range tests {
150		proto.DiscardUnknown(tt.in)
151		if !proto.Equal(tt.in, tt.want) {
152			t.Errorf("test %s, expected unknown fields to be discarded\ngot  %v\nwant %v", tt.desc, tt.in, tt.want)
153		}
154	}
155}
156
157// LegacyMessage is a proto.Message that has several nested messages.
158// This does not have the XXX_DiscardUnknown method and so forces DiscardUnknown
159// to use the legacy fallback logic.
160type LegacyMessage struct {
161	Message        *proto3pb.Message
162	Communique     *pb.Communique
163	MessageWithMap *pb.MessageWithMap
164	MyMessage      *pb.MyMessage
165}
166
167func (m *LegacyMessage) Reset()         { *m = LegacyMessage{} }
168func (m *LegacyMessage) String() string { return proto.CompactTextString(m) }
169func (*LegacyMessage) ProtoMessage()    {}
170