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