blob: d8aabd9c10e7c3cf8d3d384ddabf869c02eabd2c [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 Tsaie0daf312020-02-25 12:51:10 -080015
Joe Tsai74b14602020-01-06 15:44:09 -080016 "google.golang.org/protobuf/encoding/prototext"
Joe Tsaicd108d02020-02-14 18:08:02 -080017 "google.golang.org/protobuf/encoding/protowire"
Damien Neile89e6242019-05-13 23:55:40 -070018 "google.golang.org/protobuf/proto"
Damien Neil01c0e8d2019-11-12 12:33:12 -080019 pref "google.golang.org/protobuf/reflect/protoreflect"
Damien Neil3016b732019-04-07 12:43:10 -070020
Damien Neil01c0e8d2019-11-12 12:33:12 -080021 orderpb "google.golang.org/protobuf/internal/testprotos/order"
Joe Tsai9b22b932019-08-08 19:23:32 -070022 testpb "google.golang.org/protobuf/internal/testprotos/test"
Damien Neile89e6242019-05-13 23:55:40 -070023 test3pb "google.golang.org/protobuf/internal/testprotos/test3"
Damien Neil99f24c32019-03-13 17:06:42 -070024)
25
26func TestEncode(t *testing.T) {
Damien Neild0b07492019-12-16 12:59:13 -080027 for _, test := range testValidMessages {
Damien Neil99f24c32019-03-13 17:06:42 -070028 for _, want := range test.decodeTo {
29 t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
Damien Neil96c229a2019-04-03 12:17:24 -070030 opts := proto.MarshalOptions{
31 AllowPartial: test.partial,
32 }
33 wire, err := opts.Marshal(want)
Damien Neil99f24c32019-03-13 17:06:42 -070034 if err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080035 t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil61e93c72019-03-27 09:23:20 -070036 }
37
38 size := proto.Size(want)
39 if size != len(wire) {
Joe Tsai74b14602020-01-06 15:44:09 -080040 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 -070041 }
42
Joe Tsai378c1322019-04-25 23:48:08 -070043 got := want.ProtoReflect().New().Interface()
Damien Neil96c229a2019-04-03 12:17:24 -070044 uopts := proto.UnmarshalOptions{
45 AllowPartial: test.partial,
46 }
47 if err := uopts.Unmarshal(wire, got); err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080048 t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070049 return
50 }
Joe Tsai96a44732020-01-03 19:52:28 -080051 if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
Joe Tsai74b14602020-01-06 15:44:09 -080052 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 -070053 }
54 })
55 }
56 }
57}
58
59func TestEncodeDeterministic(t *testing.T) {
Damien Neild0b07492019-12-16 12:59:13 -080060 for _, test := range testValidMessages {
Damien Neil99f24c32019-03-13 17:06:42 -070061 for _, want := range test.decodeTo {
62 t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
Damien Neil96c229a2019-04-03 12:17:24 -070063 opts := proto.MarshalOptions{
64 Deterministic: true,
65 AllowPartial: test.partial,
66 }
67 wire, err := opts.Marshal(want)
Damien Neil99f24c32019-03-13 17:06:42 -070068 if err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080069 t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070070 }
Damien Neil96c229a2019-04-03 12:17:24 -070071 wire2, err := opts.Marshal(want)
Damien Neil99f24c32019-03-13 17:06:42 -070072 if err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080073 t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070074 }
Damien Neil99f24c32019-03-13 17:06:42 -070075 if !bytes.Equal(wire, wire2) {
76 t.Fatalf("deterministic marshal returned varying results:\n%v", cmp.Diff(wire, wire2))
77 }
78
Joe Tsai378c1322019-04-25 23:48:08 -070079 got := want.ProtoReflect().New().Interface()
Damien Neil96c229a2019-04-03 12:17:24 -070080 uopts := proto.UnmarshalOptions{
81 AllowPartial: test.partial,
82 }
83 if err := uopts.Unmarshal(wire, got); err != nil {
Joe Tsai74b14602020-01-06 15:44:09 -080084 t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
Damien Neil99f24c32019-03-13 17:06:42 -070085 return
86 }
Joe Tsai96a44732020-01-03 19:52:28 -080087 if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
Joe Tsai74b14602020-01-06 15:44:09 -080088 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 -070089 }
90 })
91 }
92 }
93}
Damien Neil96c229a2019-04-03 12:17:24 -070094
95func TestEncodeRequiredFieldChecks(t *testing.T) {
Damien Neild0b07492019-12-16 12:59:13 -080096 for _, test := range testValidMessages {
Damien Neil96c229a2019-04-03 12:17:24 -070097 if !test.partial {
98 continue
99 }
100 for _, m := range test.decodeTo {
101 t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
102 _, err := proto.Marshal(m)
103 if err == nil {
Joe Tsai74b14602020-01-06 15:44:09 -0800104 t.Fatalf("Marshal succeeded (want error)\nMessage:\n%v", prototext.Format(m))
Damien Neil96c229a2019-04-03 12:17:24 -0700105 }
106 })
107 }
108 }
109}
Damien Neil3016b732019-04-07 12:43:10 -0700110
Joe Tsai9b22b932019-08-08 19:23:32 -0700111func TestEncodeAppend(t *testing.T) {
Damien Neil3016b732019-04-07 12:43:10 -0700112 want := []byte("prefix")
113 got := append([]byte(nil), want...)
114 got, err := proto.MarshalOptions{}.MarshalAppend(got, &test3pb.TestAllTypes{
115 OptionalString: "value",
116 })
117 if err != nil {
118 t.Fatal(err)
119 }
120 if !bytes.HasPrefix(got, want) {
121 t.Fatalf("MarshalAppend modified prefix: got %v, want prefix %v", got, want)
122 }
123}
Joe Tsai9b22b932019-08-08 19:23:32 -0700124
Damien Neild0b07492019-12-16 12:59:13 -0800125func TestEncodeInvalidMessages(t *testing.T) {
126 for _, test := range testInvalidMessages {
127 for _, m := range test.decodeTo {
128 if !m.ProtoReflect().IsValid() {
129 continue
130 }
131 t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
Damien Neild0b07492019-12-16 12:59:13 -0800132 opts := proto.MarshalOptions{
133 AllowPartial: test.partial,
134 }
135 got, err := opts.Marshal(m)
136 if err == nil {
Joe Tsai74b14602020-01-06 15:44:09 -0800137 t.Fatalf("Marshal unexpectedly succeeded\noutput bytes: [%x]\nMessage:\n%v", got, prototext.Format(m))
Damien Neild0b07492019-12-16 12:59:13 -0800138 }
139 })
140 }
141 }
142}
143
Joe Tsai9b22b932019-08-08 19:23:32 -0700144func TestEncodeOneofNilWrapper(t *testing.T) {
145 m := &testpb.TestAllTypes{OneofField: (*testpb.TestAllTypes_OneofUint32)(nil)}
146 b, err := proto.Marshal(m)
147 if err != nil {
148 t.Fatal(err)
149 }
150 if len(b) > 0 {
151 t.Errorf("Marshal return non-empty, want empty")
152 }
153}
Damien Neil1e5516a2019-09-27 14:31:10 -0700154
155func TestMarshalAppendAllocations(t *testing.T) {
156 m := &test3pb.TestAllTypes{OptionalInt32: 1}
157 size := proto.Size(m)
158 const count = 1000
159 b := make([]byte, size)
160 // AllocsPerRun returns an integral value.
161 marshalAllocs := testing.AllocsPerRun(count, func() {
162 _, err := proto.MarshalOptions{}.MarshalAppend(b[:0], m)
163 if err != nil {
164 t.Fatal(err)
165 }
166 })
167 b = nil
168 marshalAppendAllocs := testing.AllocsPerRun(count, func() {
169 var err error
170 b, err = proto.MarshalOptions{}.MarshalAppend(b, m)
171 if err != nil {
172 t.Fatal(err)
173 }
174 })
175 if marshalAllocs != marshalAppendAllocs {
176 t.Errorf("%v allocs/op when writing to a preallocated buffer", marshalAllocs)
177 t.Errorf("%v allocs/op when repeatedly appending to a slice", marshalAppendAllocs)
178 t.Errorf("expect amortized allocs/op to be identical")
179 }
180}
Damien Neil01c0e8d2019-11-12 12:33:12 -0800181
182func TestEncodeOrder(t *testing.T) {
183 // We make no guarantees about the stability of wire marshal output.
184 // The order in which fields are marshaled may change over time.
185 // If deterministic marshaling is not enabled, it may change over
186 // successive calls to proto.Marshal in the same binary.
187 //
188 // Unfortunately, many users have come to rely on the specific current
189 // wire marshal output. Perhaps someday we will choose to deliberately
190 // change the marshal output; until that day comes, this test verifies
191 // that we don't unintentionally change it.
192 m := &orderpb.Message{
193 Field_1: proto.String("one"),
194 Field_2: proto.String("two"),
195 Field_20: proto.String("twenty"),
196 Oneof_1: &orderpb.Message_Field_10{"ten"},
197 }
198 proto.SetExtension(m, orderpb.E_Field_30, "thirty")
199 proto.SetExtension(m, orderpb.E_Field_31, "thirty-one")
200 proto.SetExtension(m, orderpb.E_Field_32, "thirty-two")
201 want := []pref.FieldNumber{
202 30, 31, 32, // extensions first, in number order
203 1, 2, 20, // non-extension, non-oneof in number order
204 10, // oneofs last, undefined order
205 }
206
207 // Test with deterministic serialization, since fields are not sorted without
208 // it when -tags=protoreflect.
209 b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
210 if err != nil {
211 t.Fatal(err)
212 }
213 var got []pref.FieldNumber
214 for len(b) > 0 {
Joe Tsaicd108d02020-02-14 18:08:02 -0800215 num, _, n := protowire.ConsumeField(b)
Damien Neil01c0e8d2019-11-12 12:33:12 -0800216 if n < 0 {
Joe Tsaicd108d02020-02-14 18:08:02 -0800217 t.Fatal(protowire.ParseError(n))
Damien Neil01c0e8d2019-11-12 12:33:12 -0800218 }
219 b = b[n:]
220 got = append(got, num)
221 }
222 if !reflect.DeepEqual(got, want) {
223 t.Errorf("unexpected field marshal order:\ngot: %v\nwant: %v\nmessage:\n%v", got, want, m)
224 }
225}
Damien Neil37cbbeb2020-02-24 11:10:10 -0800226
227func TestEncodeLarge(t *testing.T) {
228 // Encode/decode a message large enough to overflow a 32-bit size cache.
229 t.Skip("too slow and memory-hungry to run all the time")
230 size := math.MaxUint32 + 1
231 m := &testpb.TestAllTypes_NestedMessage{
232 Corecursive: &testpb.TestAllTypes{
233 OptionalBytes: make([]byte, size),
234 },
235 }
236 b, err := proto.Marshal(m)
237 if err != nil {
238 t.Fatalf("Marshal: %v", err)
239 }
240 if got, want := len(b), proto.Size(m); got != want {
241 t.Fatalf("Size(m) = %v, but len(Marshal(m)) = %v", got, want)
242 }
243 if err := proto.Unmarshal(b, m); err != nil {
244 t.Fatalf("Unmarshal: %v", err)
245 }
246 if got, want := len(m.Corecursive.OptionalBytes), size; got != want {
247 t.Errorf("after round-trip marshal, got len(m.OptionalBytes) = %v, want %v", got, want)
248 }
249}