1// 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
5package proto_test
6
7import (
8	"bytes"
9	"fmt"
10	"math"
11	"reflect"
12	"testing"
13
14	"github.com/google/go-cmp/cmp"
15
16	"google.golang.org/protobuf/encoding/prototext"
17	"google.golang.org/protobuf/encoding/protowire"
18	"google.golang.org/protobuf/proto"
19	pref "google.golang.org/protobuf/reflect/protoreflect"
20
21	"google.golang.org/protobuf/internal/errors"
22	orderpb "google.golang.org/protobuf/internal/testprotos/order"
23	testpb "google.golang.org/protobuf/internal/testprotos/test"
24	test3pb "google.golang.org/protobuf/internal/testprotos/test3"
25)
26
27func TestEncode(t *testing.T) {
28	for _, test := range testValidMessages {
29		for _, want := range test.decodeTo {
30			t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
31				opts := proto.MarshalOptions{
32					AllowPartial: test.partial,
33				}
34				wire, err := opts.Marshal(want)
35				if err != nil {
36					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
37				}
38
39				size := proto.Size(want)
40				if size != len(wire) {
41					t.Errorf("Size and marshal disagree: Size(m)=%v; len(Marshal(m))=%v\nMessage:\n%v", size, len(wire), prototext.Format(want))
42				}
43
44				got := want.ProtoReflect().New().Interface()
45				uopts := proto.UnmarshalOptions{
46					AllowPartial: test.partial,
47				}
48				if err := uopts.Unmarshal(wire, got); err != nil {
49					t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
50					return
51				}
52				if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
53					t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
54				}
55			})
56		}
57	}
58}
59
60func TestEncodeDeterministic(t *testing.T) {
61	for _, test := range testValidMessages {
62		for _, want := range test.decodeTo {
63			t.Run(fmt.Sprintf("%s (%T)", test.desc, want), func(t *testing.T) {
64				opts := proto.MarshalOptions{
65					Deterministic: true,
66					AllowPartial:  test.partial,
67				}
68				wire, err := opts.Marshal(want)
69				if err != nil {
70					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
71				}
72				wire2, err := opts.Marshal(want)
73				if err != nil {
74					t.Fatalf("Marshal error: %v\nMessage:\n%v", err, prototext.Format(want))
75				}
76				if !bytes.Equal(wire, wire2) {
77					t.Fatalf("deterministic marshal returned varying results:\n%v", cmp.Diff(wire, wire2))
78				}
79
80				got := want.ProtoReflect().New().Interface()
81				uopts := proto.UnmarshalOptions{
82					AllowPartial: test.partial,
83				}
84				if err := uopts.Unmarshal(wire, got); err != nil {
85					t.Errorf("Unmarshal error: %v\nMessage:\n%v", err, prototext.Format(want))
86					return
87				}
88				if !proto.Equal(got, want) && got.ProtoReflect().IsValid() && want.ProtoReflect().IsValid() {
89					t.Errorf("Unmarshal returned unexpected result; got:\n%v\nwant:\n%v", prototext.Format(got), prototext.Format(want))
90				}
91			})
92		}
93	}
94}
95
96func TestEncodeRequiredFieldChecks(t *testing.T) {
97	for _, test := range testValidMessages {
98		if !test.partial {
99			continue
100		}
101		for _, m := range test.decodeTo {
102			t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
103				_, err := proto.Marshal(m)
104				if err == nil {
105					t.Fatalf("Marshal succeeded (want error)\nMessage:\n%v", prototext.Format(m))
106				}
107			})
108		}
109	}
110}
111
112func TestEncodeAppend(t *testing.T) {
113	want := []byte("prefix")
114	got := append([]byte(nil), want...)
115	got, err := proto.MarshalOptions{}.MarshalAppend(got, &test3pb.TestAllTypes{
116		SingularString: "value",
117	})
118	if err != nil {
119		t.Fatal(err)
120	}
121	if !bytes.HasPrefix(got, want) {
122		t.Fatalf("MarshalAppend modified prefix: got %v, want prefix %v", got, want)
123	}
124}
125
126func TestEncodeInvalidMessages(t *testing.T) {
127	for _, test := range testInvalidMessages {
128		for _, m := range test.decodeTo {
129			if !m.ProtoReflect().IsValid() {
130				continue
131			}
132			t.Run(fmt.Sprintf("%s (%T)", test.desc, m), func(t *testing.T) {
133				opts := proto.MarshalOptions{
134					AllowPartial: test.partial,
135				}
136				got, err := opts.Marshal(m)
137				if err == nil {
138					t.Fatalf("Marshal unexpectedly succeeded\noutput bytes: [%x]\nMessage:\n%v", got, prototext.Format(m))
139				}
140				if !errors.Is(err, proto.Error) {
141					t.Fatalf("Marshal error is not a proto.Error: %v", err)
142				}
143			})
144		}
145	}
146}
147
148func TestEncodeOneofNilWrapper(t *testing.T) {
149	m := &testpb.TestAllTypes{OneofField: (*testpb.TestAllTypes_OneofUint32)(nil)}
150	b, err := proto.Marshal(m)
151	if err != nil {
152		t.Fatal(err)
153	}
154	if len(b) > 0 {
155		t.Errorf("Marshal return non-empty, want empty")
156	}
157}
158
159func TestMarshalAppendAllocations(t *testing.T) {
160	m := &test3pb.TestAllTypes{SingularInt32: 1}
161	size := proto.Size(m)
162	const count = 1000
163	b := make([]byte, size)
164	// AllocsPerRun returns an integral value.
165	marshalAllocs := testing.AllocsPerRun(count, func() {
166		_, err := proto.MarshalOptions{}.MarshalAppend(b[:0], m)
167		if err != nil {
168			t.Fatal(err)
169		}
170	})
171	b = nil
172	marshalAppendAllocs := testing.AllocsPerRun(count, func() {
173		var err error
174		b, err = proto.MarshalOptions{}.MarshalAppend(b, m)
175		if err != nil {
176			t.Fatal(err)
177		}
178	})
179	if marshalAllocs != marshalAppendAllocs {
180		t.Errorf("%v allocs/op when writing to a preallocated buffer", marshalAllocs)
181		t.Errorf("%v allocs/op when repeatedly appending to a slice", marshalAppendAllocs)
182		t.Errorf("expect amortized allocs/op to be identical")
183	}
184}
185
186func TestEncodeOrder(t *testing.T) {
187	// We make no guarantees about the stability of wire marshal output.
188	// The order in which fields are marshaled may change over time.
189	// If deterministic marshaling is not enabled, it may change over
190	// successive calls to proto.Marshal in the same binary.
191	//
192	// Unfortunately, many users have come to rely on the specific current
193	// wire marshal output. Perhaps someday we will choose to deliberately
194	// change the marshal output; until that day comes, this test verifies
195	// that we don't unintentionally change it.
196	m := &orderpb.Message{
197		Field_1:  proto.String("one"),
198		Field_2:  proto.String("two"),
199		Field_20: proto.String("twenty"),
200		Oneof_1:  &orderpb.Message_Field_10{"ten"},
201	}
202	proto.SetExtension(m, orderpb.E_Field_30, "thirty")
203	proto.SetExtension(m, orderpb.E_Field_31, "thirty-one")
204	proto.SetExtension(m, orderpb.E_Field_32, "thirty-two")
205	want := []pref.FieldNumber{
206		30, 31, 32, // extensions first, in number order
207		1, 2, 20, // non-extension, non-oneof in number order
208		10, // oneofs last, undefined order
209	}
210
211	// Test with deterministic serialization, since fields are not sorted without
212	// it when -tags=protoreflect.
213	b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
214	if err != nil {
215		t.Fatal(err)
216	}
217	var got []pref.FieldNumber
218	for len(b) > 0 {
219		num, _, n := protowire.ConsumeField(b)
220		if n < 0 {
221			t.Fatal(protowire.ParseError(n))
222		}
223		b = b[n:]
224		got = append(got, num)
225	}
226	if !reflect.DeepEqual(got, want) {
227		t.Errorf("unexpected field marshal order:\ngot:  %v\nwant: %v\nmessage:\n%v", got, want, m)
228	}
229}
230
231func TestEncodeLarge(t *testing.T) {
232	// Encode/decode a message large enough to overflow a 32-bit size cache.
233	t.Skip("too slow and memory-hungry to run all the time")
234	size := int64(math.MaxUint32 + 1)
235	m := &testpb.TestAllTypes_NestedMessage{
236		Corecursive: &testpb.TestAllTypes{
237			OptionalBytes: make([]byte, size),
238		},
239	}
240	b, err := proto.Marshal(m)
241	if err != nil {
242		t.Fatalf("Marshal: %v", err)
243	}
244	if got, want := len(b), proto.Size(m); got != want {
245		t.Fatalf("Size(m) = %v, but len(Marshal(m)) = %v", got, want)
246	}
247	if err := proto.Unmarshal(b, m); err != nil {
248		t.Fatalf("Unmarshal: %v", err)
249	}
250	if got, want := int64(len(m.Corecursive.OptionalBytes)), size; got != want {
251		t.Errorf("after round-trip marshal, got len(m.OptionalBytes) = %v, want %v", got, want)
252	}
253}
254
255// TestEncodeEmpty tests for boundary conditions when producing an empty output.
256// These tests are not necessarily a statement of proper behavior,
257// but exist to detect accidental changes in behavior.
258func TestEncodeEmpty(t *testing.T) {
259	for _, m := range []proto.Message{nil, (*testpb.TestAllTypes)(nil), &testpb.TestAllTypes{}} {
260		isValid := m != nil && m.ProtoReflect().IsValid()
261
262		b, err := proto.Marshal(m)
263		if err != nil {
264			t.Errorf("proto.Marshal() = %v", err)
265		}
266		if isNil := b == nil; isNil == isValid {
267			t.Errorf("proto.Marshal() == nil: %v, want %v", isNil, !isValid)
268		}
269
270		b, err = proto.MarshalOptions{}.Marshal(m)
271		if err != nil {
272			t.Errorf("proto.MarshalOptions{}.Marshal() = %v", err)
273		}
274		if isNil := b == nil; isNil == isValid {
275			t.Errorf("proto.MarshalOptions{}.Marshal() = %v, want %v", isNil, !isValid)
276		}
277	}
278}
279