File: lazy_roundtrip_test.go

package info (click to toggle)
golang-google-protobuf 1.36.7-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid
  • size: 14,996 kB
  • sloc: sh: 94; makefile: 4
file content (125 lines) | stat: -rw-r--r-- 4,017 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package proto_test

import (
	"reflect"
	"testing"
	"unsafe"

	"github.com/google/go-cmp/cmp"
	"google.golang.org/protobuf/internal/impl"
	testopaquepb "google.golang.org/protobuf/internal/testprotos/testeditions/testeditions_opaque"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/testing/protocmp"
)

func fillLazyRequiredMessage() *testopaquepb.TestRequiredLazy {
	return testopaquepb.TestRequiredLazy_builder{
		OptionalLazyMessage: testopaquepb.TestRequired_builder{
			RequiredField: proto.Int32(12),
		}.Build(),
	}.Build()
}

func expandedLazy(m *testopaquepb.TestRequiredLazy) bool {
	v := reflect.ValueOf(m).Elem()
	rf := v.FieldByName("xxx_hidden_OptionalLazyMessage")
	rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem()
	return rf.Pointer() != 0
}

// This test ensures that a lazy field keeps being lazy when marshalling
// even if it has required fields (as they have already been checked on
// unmarshal)
func TestLazyRequiredRoundtrip(t *testing.T) {
	if !impl.LazyEnabled() {
		t.Skipf("this test requires lazy decoding to be enabled")
	}
	m := fillLazyRequiredMessage()
	b, _ := proto.MarshalOptions{}.Marshal(m)
	ml := &testopaquepb.TestRequiredLazy{}
	err := proto.UnmarshalOptions{}.Unmarshal(b, ml)
	if err != nil {
		t.Fatalf("Error while unmarshaling: %v", err)
	}
	// Sanity check, we should have all unexpanded fields in the proto
	if expandedLazy(ml) {
		t.Fatalf("Proto is not lazy: %#v", ml)
	}
	// Now we marshal the lazy field. It should still be unexpanded
	_, _ = proto.MarshalOptions{}.Marshal(ml)

	if expandedLazy(ml) {
		t.Errorf("Proto got expanded by marshal: %#v", ml)
	}

	// The following tests the current behavior for cases where we
	// cannot guarantee the integrity of the lazy unmarshalled buffer
	// because of required fields. This would have to be updated if
	// we find another way to check required fields than simply
	// unmarshalling everything that has them when we're not sure.

	ml = &testopaquepb.TestRequiredLazy{}
	err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(b, ml)
	if err != nil {
		t.Fatalf("Error while unmarshaling: %v", err)
	}
	// Sanity check, we should have all unexpanded fields in the proto.
	if expandedLazy(ml) {
		t.Fatalf("Proto is not lazy: %#v", ml)
	}
	// Now we marshal the proto. The lazy fields will be expanded to
	// check required fields.
	_, _ = proto.MarshalOptions{}.Marshal(ml)

	if !expandedLazy(ml) {
		t.Errorf("Proto did not get expanded by marshal: %#v", ml)
	}

	// Finally, we test to see that the fields to not get expanded
	// if we are consistently using AllowPartial both for marshal
	// and unmarshal.
	ml = &testopaquepb.TestRequiredLazy{}
	err = proto.UnmarshalOptions{AllowPartial: true}.Unmarshal(b, ml)
	if err != nil {
		t.Fatalf("Error while unmarshaling: %v", err)
	}
	// Sanity check, we should have all unexpanded fields in the proto.
	if expandedLazy(ml) {
		t.Fatalf("Proto is not lazy: %#v", ml)
	}
	// Now we marshal the proto. The lazy fields will be expanded to
	// check required fields.
	_, _ = proto.MarshalOptions{AllowPartial: true}.Marshal(ml)

	if expandedLazy(ml) {
		t.Errorf("Proto did not get expanded by marshal: %#v", ml)
	}

}

func TestRoundtripMap(t *testing.T) {
	m := testopaquepb.TestAllTypes_builder{
		OptionalLazyNestedMessage: testopaquepb.TestAllTypes_NestedMessage_builder{
			Corecursive: testopaquepb.TestAllTypes_builder{
				MapStringString: map[string]string{
					"a": "b",
				},
			}.Build(),
		}.Build(),
	}.Build()
	b, err := proto.Marshal(m)
	if err != nil {
		t.Fatalf("proto.Marshal: %v", err)
	}
	got := &testopaquepb.TestAllTypes{}
	if err := proto.Unmarshal(b, got); err != nil {
		t.Fatalf("proto.Unmarshal: %v", err)
	}
	if diff := cmp.Diff(m, got, protocmp.Transform()); diff != "" {
		t.Errorf("not the same: diff (-want +got):\n%s", diff)
	}
}