blob: edef13952b385b2008172b20c0925f93b347f633 [file] [log] [blame]
Damien Neilc37adef2019-04-01 13:49:56 -07001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Damien Neil99f24c32019-03-13 17:06:42 -07005package proto_test
6
7import (
8 "bytes"
9 "fmt"
Damien Neil37cbbeb2020-02-24 11:10:10 -080010 "math"
Damien Neil01c0e8d2019-11-12 12:33:12 -080011 "reflect"
Damien Neil99f24c32019-03-13 17:06:42 -070012 "testing"
13
Damien Neil99f24c32019-03-13 17:06:42 -070014 "github.com/google/go-cmp/cmp"
Joe Tsai74b14602020-01-06 15:44:09 -080015 "google.golang.org/protobuf/encoding/prototext"
Damien Neil01c0e8d2019-11-12 12:33:12 -080016 "google.golang.org/protobuf/internal/encoding/wire"
Damien Neile89e6242019-05-13 23:55:40 -070017 "google.golang.org/protobuf/proto"
Damien Neil01c0e8d2019-11-12 12:33:12 -080018 pref "google.golang.org/protobuf/reflect/protoreflect"
Damien Neil3016b732019-04-07 12:43:10 -070019
Damien Neil01c0e8d2019-11-12 12:33:12 -080020 orderpb "google.golang.org/protobuf/internal/testprotos/order"
Joe Tsai9b22b932019-08-08 19:23:32 -070021 testpb "google.golang.org/protobuf/internal/testprotos/test"
Damien Neile89e6242019-05-13 23:55:40 -070022 test3pb "google.golang.org/protobuf/internal/testprotos/test3"
Damien Neil99f24c32019-03-13 17:06:42 -070023)
24
25func TestEncode(t *testing.T) {
Damien Neild0b07492019-12-16 12:59:13 -080026 for _, test := range testValidMessages {
Damien Neil99f24c32019-03-13 17:06:42 -070027 for _, want := range test.decodeTo {
28 t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
Damien Neil96c229a2019-04-03 12:17:24 -070029 opts := proto.MarshalOptions{
30 AllowPartial: test.partial,
31 }
32 wire, err := opts.Marshal(want)
Damien Neil99f24c32019-03-13 17:06:42 -070033 if err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080034 t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil61e93c72019-03-27 09:23:20 -070035 }
36
37 size := proto.Size(want)
38 if size != len(wire) {
Joe Tsai74b14602020-01-06 15:44:09 -080039 t.Errorf("Size and marshal disagree: Size(m)=%v; len(Marshal(m))=%v\nMessage:\n%v", size, len(wire), prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070040 }
41
Joe Tsai378c1322019-04-25 23:48:08 -070042 got := want.ProtoReflect().New().Interface()
Damien Neil96c229a2019-04-03 12:17:24 -070043 uopts := proto.UnmarshalOptions{
44 AllowPartial: test.partial,
45 }
46 if err := uopts.Unmarshal(wire, got); err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080047 t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070048 return
49 }
Joe Tsai96a44732020-01-03 19:52:28 -080050 if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
Joe Tsai74b14602020-01-06 15:44:09 -080051 t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070052 }
53 })
54 }
55 }
56}
57
58func TestEncodeDeterministic(t *testing.T) {
Damien Neild0b07492019-12-16 12:59:13 -080059 for _, test := range testValidMessages {
Damien Neil99f24c32019-03-13 17:06:42 -070060 for _, want := range test.decodeTo {
61 t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
Damien Neil96c229a2019-04-03 12:17:24 -070062 opts := proto.MarshalOptions{
63 Deterministic: true,
64 AllowPartial: test.partial,
65 }
66 wire, err := opts.Marshal(want)
Damien Neil99f24c32019-03-13 17:06:42 -070067 if err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080068 t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070069 }
Damien Neil96c229a2019-04-03 12:17:24 -070070 wire2, err := opts.Marshal(want)
Damien Neil99f24c32019-03-13 17:06:42 -070071 if err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080072 t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070073 }
Damien Neil99f24c32019-03-13 17:06:42 -070074 if !bytes.Equal(wire, wire2) {
75 t.Fatalf("deterministic marshal returned varying results:\n%v", cmp.Diff(wire, wire2))
76 }
77
Joe Tsai378c1322019-04-25 23:48:08 -070078 got := want.ProtoReflect().New().Interface()
Damien Neil96c229a2019-04-03 12:17:24 -070079 uopts := proto.UnmarshalOptions{
80 AllowPartial: test.partial,
81 }
82 if err := uopts.Unmarshal(wire, got); err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080083 t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070084 return
85 }
Joe Tsai96a44732020-01-03 19:52:28 -080086 if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
Joe Tsai74b14602020-01-06 15:44:09 -080087 t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070088 }
89 })
90 }
91 }
92}
Damien Neil96c229a2019-04-03 12:17:24 -070093
94func TestEncodeRequiredFieldChecks(t *testing.T) {
Damien Neild0b07492019-12-16 12:59:13 -080095 for _, test := range testValidMessages {
Damien Neil96c229a2019-04-03 12:17:24 -070096 if !test.partial {
97 continue
98 }
99 for _, m := range test.decodeTo {
100 t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
101 _, err := proto.Marshal(m)
102 if err == nil {
Joe Tsai74b14602020-01-06 15:44:09 -0800103 t.Fatalf("Marshal succeeded (want error)\nMessage:\n%v", prototext.Format(m))
Damien Neil96c229a2019-04-03 12:17:24 -0700104 }
105 })
106 }
107 }
108}
Damien Neil3016b732019-04-07 12:43:10 -0700109
Joe Tsai9b22b932019-08-08 19:23:32 -0700110func TestEncodeAppend(t *testing.T) {
Damien Neil3016b732019-04-07 12:43:10 -0700111 want := []byte("prefix")
112 got := append([]byte(nil), want...)
113 got, err := proto.MarshalOptions{}.MarshalAppend(got, &test3pb.TestAllTypes{
114 OptionalString: "value",
115 })
116 if err != nil {
117 t.Fatal(err)
118 }
119 if !bytes.HasPrefix(got, want) {
120 t.Fatalf("MarshalAppend modified prefix: got %v, want prefix %v", got, want)
121 }
122}
Joe Tsai9b22b932019-08-08 19:23:32 -0700123
Damien Neild0b07492019-12-16 12:59:13 -0800124func TestEncodeInvalidMessages(t *testing.T) {
125 for _, test := range testInvalidMessages {
126 for _, m := range test.decodeTo {
127 if !m.ProtoReflect().IsValid() {
128 continue
129 }
130 t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
Damien Neild0b07492019-12-16 12:59:13 -0800131 opts := proto.MarshalOptions{
132 AllowPartial: test.partial,
133 }
134 got, err := opts.Marshal(m)
135 if err == nil {
Joe Tsai74b14602020-01-06 15:44:09 -0800136 t.Fatalf("Marshal unexpectedly succeeded\noutput bytes: [%x]\nMessage:\n%v", got, prototext.Format(m))
Damien Neild0b07492019-12-16 12:59:13 -0800137 }
138 })
139 }
140 }
141}
142
Joe Tsai9b22b932019-08-08 19:23:32 -0700143func TestEncodeOneofNilWrapper(t *testing.T) {
144 m := &testpb.TestAllTypes{OneofField: (*testpb.TestAllTypes_OneofUint32)(nil)}
145 b, err := proto.Marshal(m)
146 if err != nil {
147 t.Fatal(err)
148 }
149 if len(b) > 0 {
150 t.Errorf("Marshal return non-empty, want empty")
151 }
152}
Damien Neil1e5516a2019-09-27 14:31:10 -0700153
154func TestMarshalAppendAllocations(t *testing.T) {
155 m := &test3pb.TestAllTypes{OptionalInt32: 1}
156 size := proto.Size(m)
157 const count = 1000
158 b := make([]byte, size)
159 // AllocsPerRun returns an integral value.
160 marshalAllocs := testing.AllocsPerRun(count, func() {
161 _, err := proto.MarshalOptions{}.MarshalAppend(b[:0], m)
162 if err != nil {
163 t.Fatal(err)
164 }
165 })
166 b = nil
167 marshalAppendAllocs := testing.AllocsPerRun(count, func() {
168 var err error
169 b, err = proto.MarshalOptions{}.MarshalAppend(b, m)
170 if err != nil {
171 t.Fatal(err)
172 }
173 })
174 if marshalAllocs != marshalAppendAllocs {
175 t.Errorf("%v allocs/op when writing to a preallocated buffer", marshalAllocs)
176 t.Errorf("%v allocs/op when repeatedly appending to a slice", marshalAppendAllocs)
177 t.Errorf("expect amortized allocs/op to be identical")
178 }
179}
Damien Neil01c0e8d2019-11-12 12:33:12 -0800180
181func TestEncodeOrder(t *testing.T) {
182 // We make no guarantees about the stability of wire marshal output.
183 // The order in which fields are marshaled may change over time.
184 // If deterministic marshaling is not enabled, it may change over
185 // successive calls to proto.Marshal in the same binary.
186 //
187 // Unfortunately, many users have come to rely on the specific current
188 // wire marshal output. Perhaps someday we will choose to deliberately
189 // change the marshal output; until that day comes, this test verifies
190 // that we don't unintentionally change it.
191 m := &orderpb.Message{
192 Field_1: proto.String("one"),
193 Field_2: proto.String("two"),
194 Field_20: proto.String("twenty"),
195 Oneof_1: &orderpb.Message_Field_10{"ten"},
196 }
197 proto.SetExtension(m, orderpb.E_Field_30, "thirty")
198 proto.SetExtension(m, orderpb.E_Field_31, "thirty-one")
199 proto.SetExtension(m, orderpb.E_Field_32, "thirty-two")
200 want := []pref.FieldNumber{
201 30, 31, 32, // extensions first, in number order
202 1, 2, 20, // non-extension, non-oneof in number order
203 10, // oneofs last, undefined order
204 }
205
206 // Test with deterministic serialization, since fields are not sorted without
207 // it when -tags=protoreflect.
208 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
209 if err != nil {
210 t.Fatal(err)
211 }
212 var got []pref.FieldNumber
213 for len(b) > 0 {
214 num, _, n := wire.ConsumeField(b)
215 if n < 0 {
216 t.Fatal(wire.ParseError(n))
217 }
218 b = b[n:]
219 got = append(got, num)
220 }
221 if !reflect.DeepEqual(got, want) {
222 t.Errorf("unexpected field marshal order:\ngot: %v\nwant: %v\nmessage:\n%v", got, want, m)
223 }
224}
Damien Neil37cbbeb2020-02-24 11:10:10 -0800225
226func TestEncodeLarge(t *testing.T) {
227 // Encode/decode a message large enough to overflow a 32-bit size cache.
228 t.Skip("too slow and memory-hungry to run all the time")
229 size := math.MaxUint32 + 1
230 m := &testpb.TestAllTypes_NestedMessage{
231 Corecursive: &testpb.TestAllTypes{
232 OptionalBytes: make([]byte, size),
233 },
234 }
235 b, err := proto.Marshal(m)
236 if err != nil {
237 t.Fatalf("Marshal: %v", err)
238 }
239 if got, want := len(b), proto.Size(m); got != want {
240 t.Fatalf("Size(m) = %v, but len(Marshal(m)) = %v", got, want)
241 }
242 if err := proto.Unmarshal(b, m); err != nil {
243 t.Fatalf("Unmarshal: %v", err)
244 }
245 if got, want := len(m.Corecursive.OptionalBytes), size; got != want {
246 t.Errorf("after round-trip marshal, got len(m.OptionalBytes) = %v, want %v", got, want)
247 }
248}