xref: /aosp_15_r20/external/golang-protobuf/reflect/protorange/range_test.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1*1c12ee1eSDan Willemsen// Copyright 2020 The Go Authors. All rights reserved.
2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style
3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file.
4*1c12ee1eSDan Willemsen
5*1c12ee1eSDan Willemsenpackage protorange
6*1c12ee1eSDan Willemsen
7*1c12ee1eSDan Willemsenimport (
8*1c12ee1eSDan Willemsen	"testing"
9*1c12ee1eSDan Willemsen	"time"
10*1c12ee1eSDan Willemsen
11*1c12ee1eSDan Willemsen	"github.com/google/go-cmp/cmp"
12*1c12ee1eSDan Willemsen	"github.com/google/go-cmp/cmp/cmpopts"
13*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/proto"
14*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protopath"
15*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protoreflect"
16*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protoregistry"
17*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/testing/protocmp"
18*1c12ee1eSDan Willemsen
19*1c12ee1eSDan Willemsen	newspb "google.golang.org/protobuf/internal/testprotos/news"
20*1c12ee1eSDan Willemsen	anypb "google.golang.org/protobuf/types/known/anypb"
21*1c12ee1eSDan Willemsen	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
22*1c12ee1eSDan Willemsen)
23*1c12ee1eSDan Willemsen
24*1c12ee1eSDan Willemsenfunc mustMarshal(m proto.Message) []byte {
25*1c12ee1eSDan Willemsen	b, err := proto.MarshalOptions{Deterministic: true}.Marshal(m)
26*1c12ee1eSDan Willemsen	if err != nil {
27*1c12ee1eSDan Willemsen		panic(err)
28*1c12ee1eSDan Willemsen	}
29*1c12ee1eSDan Willemsen	return b
30*1c12ee1eSDan Willemsen}
31*1c12ee1eSDan Willemsen
32*1c12ee1eSDan Willemsenvar transformReflectValue = cmp.Transformer("", func(v protoreflect.Value) interface{} {
33*1c12ee1eSDan Willemsen	switch v := v.Interface().(type) {
34*1c12ee1eSDan Willemsen	case protoreflect.Message:
35*1c12ee1eSDan Willemsen		return v.Interface()
36*1c12ee1eSDan Willemsen	case protoreflect.Map:
37*1c12ee1eSDan Willemsen		ms := map[interface{}]protoreflect.Value{}
38*1c12ee1eSDan Willemsen		v.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
39*1c12ee1eSDan Willemsen			ms[k.Interface()] = v
40*1c12ee1eSDan Willemsen			return true
41*1c12ee1eSDan Willemsen		})
42*1c12ee1eSDan Willemsen		return ms
43*1c12ee1eSDan Willemsen	case protoreflect.List:
44*1c12ee1eSDan Willemsen		ls := []protoreflect.Value{}
45*1c12ee1eSDan Willemsen		for i := 0; i < v.Len(); i++ {
46*1c12ee1eSDan Willemsen			ls = append(ls, v.Get(i))
47*1c12ee1eSDan Willemsen		}
48*1c12ee1eSDan Willemsen		return ls
49*1c12ee1eSDan Willemsen	default:
50*1c12ee1eSDan Willemsen		return v
51*1c12ee1eSDan Willemsen	}
52*1c12ee1eSDan Willemsen})
53*1c12ee1eSDan Willemsen
54*1c12ee1eSDan Willemsenfunc TestRange(t *testing.T) {
55*1c12ee1eSDan Willemsen	m2 := (&newspb.KeyValueAttachment{
56*1c12ee1eSDan Willemsen		Name: "checksums.txt",
57*1c12ee1eSDan Willemsen		Data: map[string]string{
58*1c12ee1eSDan Willemsen			"go1.10.src.tar.gz":         "07cbb9d0091b846c6aea40bf5bc0cea7",
59*1c12ee1eSDan Willemsen			"go1.10.darwin-amd64.pkg":   "cbb38bb6ff6ea86279e01745984445bf",
60*1c12ee1eSDan Willemsen			"go1.10.linux-amd64.tar.gz": "6b3d0e4a5c77352cf4275573817f7566",
61*1c12ee1eSDan Willemsen			"go1.10.windows-amd64.msi":  "57bda02030f58f5d2bf71943e1390123",
62*1c12ee1eSDan Willemsen		},
63*1c12ee1eSDan Willemsen	}).ProtoReflect()
64*1c12ee1eSDan Willemsen	m := (&newspb.Article{
65*1c12ee1eSDan Willemsen		Author:  "Brad Fitzpatrick",
66*1c12ee1eSDan Willemsen		Date:    timestamppb.New(time.Date(2018, time.February, 16, 0, 0, 0, 0, time.UTC)),
67*1c12ee1eSDan Willemsen		Title:   "Go 1.10 is released",
68*1c12ee1eSDan Willemsen		Content: "Happy Friday, happy weekend! Today the Go team is happy to announce the release of Go 1.10...",
69*1c12ee1eSDan Willemsen		Status:  newspb.Article_PUBLISHED,
70*1c12ee1eSDan Willemsen		Tags:    []string{"go1.10", "release"},
71*1c12ee1eSDan Willemsen		Attachments: []*anypb.Any{{
72*1c12ee1eSDan Willemsen			TypeUrl: "google.golang.org.KeyValueAttachment",
73*1c12ee1eSDan Willemsen			Value:   mustMarshal(m2.Interface()),
74*1c12ee1eSDan Willemsen		}},
75*1c12ee1eSDan Willemsen	}).ProtoReflect()
76*1c12ee1eSDan Willemsen
77*1c12ee1eSDan Willemsen	// Nil push and pop functions should not panic.
78*1c12ee1eSDan Willemsen	noop := func(protopath.Values) error { return nil }
79*1c12ee1eSDan Willemsen	Options{}.Range(m, nil, nil)
80*1c12ee1eSDan Willemsen	Options{}.Range(m, noop, nil)
81*1c12ee1eSDan Willemsen	Options{}.Range(m, nil, noop)
82*1c12ee1eSDan Willemsen
83*1c12ee1eSDan Willemsen	getByName := func(m protoreflect.Message, s protoreflect.Name) protoreflect.Value {
84*1c12ee1eSDan Willemsen		fds := m.Descriptor().Fields()
85*1c12ee1eSDan Willemsen		return m.Get(fds.ByName(s))
86*1c12ee1eSDan Willemsen	}
87*1c12ee1eSDan Willemsen
88*1c12ee1eSDan Willemsen	wantPaths := []string{
89*1c12ee1eSDan Willemsen		``,
90*1c12ee1eSDan Willemsen		`.author`,
91*1c12ee1eSDan Willemsen		`.date`,
92*1c12ee1eSDan Willemsen		`.date.seconds`,
93*1c12ee1eSDan Willemsen		`.title`,
94*1c12ee1eSDan Willemsen		`.content`,
95*1c12ee1eSDan Willemsen		`.attachments`,
96*1c12ee1eSDan Willemsen		`.attachments[0]`,
97*1c12ee1eSDan Willemsen		`.attachments[0].(google.golang.org.KeyValueAttachment)`,
98*1c12ee1eSDan Willemsen		`.attachments[0].(google.golang.org.KeyValueAttachment).name`,
99*1c12ee1eSDan Willemsen		`.attachments[0].(google.golang.org.KeyValueAttachment).data`,
100*1c12ee1eSDan Willemsen		`.attachments[0].(google.golang.org.KeyValueAttachment).data["go1.10.darwin-amd64.pkg"]`,
101*1c12ee1eSDan Willemsen		`.attachments[0].(google.golang.org.KeyValueAttachment).data["go1.10.linux-amd64.tar.gz"]`,
102*1c12ee1eSDan Willemsen		`.attachments[0].(google.golang.org.KeyValueAttachment).data["go1.10.src.tar.gz"]`,
103*1c12ee1eSDan Willemsen		`.attachments[0].(google.golang.org.KeyValueAttachment).data["go1.10.windows-amd64.msi"]`,
104*1c12ee1eSDan Willemsen		`.tags`,
105*1c12ee1eSDan Willemsen		`.tags[0]`,
106*1c12ee1eSDan Willemsen		`.tags[1]`,
107*1c12ee1eSDan Willemsen		`.status`,
108*1c12ee1eSDan Willemsen	}
109*1c12ee1eSDan Willemsen	wantValues := []protoreflect.Value{
110*1c12ee1eSDan Willemsen		protoreflect.ValueOfMessage(m),
111*1c12ee1eSDan Willemsen		getByName(m, "author"),
112*1c12ee1eSDan Willemsen		getByName(m, "date"),
113*1c12ee1eSDan Willemsen		getByName(getByName(m, "date").Message(), "seconds"),
114*1c12ee1eSDan Willemsen		getByName(m, `title`),
115*1c12ee1eSDan Willemsen		getByName(m, `content`),
116*1c12ee1eSDan Willemsen		getByName(m, `attachments`),
117*1c12ee1eSDan Willemsen		getByName(m, `attachments`).List().Get(0),
118*1c12ee1eSDan Willemsen		protoreflect.ValueOfMessage(m2),
119*1c12ee1eSDan Willemsen		getByName(m2, `name`),
120*1c12ee1eSDan Willemsen		getByName(m2, `data`),
121*1c12ee1eSDan Willemsen		getByName(m2, `data`).Map().Get(protoreflect.ValueOfString("go1.10.darwin-amd64.pkg").MapKey()),
122*1c12ee1eSDan Willemsen		getByName(m2, `data`).Map().Get(protoreflect.ValueOfString("go1.10.linux-amd64.tar.gz").MapKey()),
123*1c12ee1eSDan Willemsen		getByName(m2, `data`).Map().Get(protoreflect.ValueOfString("go1.10.src.tar.gz").MapKey()),
124*1c12ee1eSDan Willemsen		getByName(m2, `data`).Map().Get(protoreflect.ValueOfString("go1.10.windows-amd64.msi").MapKey()),
125*1c12ee1eSDan Willemsen		getByName(m, `tags`),
126*1c12ee1eSDan Willemsen		getByName(m, `tags`).List().Get(0),
127*1c12ee1eSDan Willemsen		getByName(m, `tags`).List().Get(1),
128*1c12ee1eSDan Willemsen		getByName(m, `status`),
129*1c12ee1eSDan Willemsen	}
130*1c12ee1eSDan Willemsen
131*1c12ee1eSDan Willemsen	tests := []struct {
132*1c12ee1eSDan Willemsen		resolver interface {
133*1c12ee1eSDan Willemsen			protoregistry.ExtensionTypeResolver
134*1c12ee1eSDan Willemsen			protoregistry.MessageTypeResolver
135*1c12ee1eSDan Willemsen		}
136*1c12ee1eSDan Willemsen
137*1c12ee1eSDan Willemsen		errorAt     int
138*1c12ee1eSDan Willemsen		breakAt     int
139*1c12ee1eSDan Willemsen		terminateAt int
140*1c12ee1eSDan Willemsen
141*1c12ee1eSDan Willemsen		wantPaths  []string
142*1c12ee1eSDan Willemsen		wantValues []protoreflect.Value
143*1c12ee1eSDan Willemsen		wantError  error
144*1c12ee1eSDan Willemsen	}{{
145*1c12ee1eSDan Willemsen		wantPaths:  wantPaths,
146*1c12ee1eSDan Willemsen		wantValues: wantValues,
147*1c12ee1eSDan Willemsen	}, {
148*1c12ee1eSDan Willemsen		resolver: (*protoregistry.Types)(nil),
149*1c12ee1eSDan Willemsen		wantPaths: append(append(wantPaths[:8:8],
150*1c12ee1eSDan Willemsen			`.attachments[0].type_url`,
151*1c12ee1eSDan Willemsen			`.attachments[0].value`,
152*1c12ee1eSDan Willemsen		), wantPaths[15:]...),
153*1c12ee1eSDan Willemsen		wantValues: append(append(wantValues[:8:8],
154*1c12ee1eSDan Willemsen			protoreflect.ValueOfString("google.golang.org.KeyValueAttachment"),
155*1c12ee1eSDan Willemsen			protoreflect.ValueOfBytes(mustMarshal(m2.Interface())),
156*1c12ee1eSDan Willemsen		), wantValues[15:]...),
157*1c12ee1eSDan Willemsen	}, {
158*1c12ee1eSDan Willemsen		errorAt:    5, // return error within newspb.Article
159*1c12ee1eSDan Willemsen		wantPaths:  wantPaths[:5],
160*1c12ee1eSDan Willemsen		wantValues: wantValues[:5],
161*1c12ee1eSDan Willemsen		wantError:  cmpopts.AnyError,
162*1c12ee1eSDan Willemsen	}, {
163*1c12ee1eSDan Willemsen		terminateAt: 11, // terminate within newspb.KeyValueAttachment
164*1c12ee1eSDan Willemsen		wantPaths:   wantPaths[:11],
165*1c12ee1eSDan Willemsen		wantValues:  wantValues[:11],
166*1c12ee1eSDan Willemsen	}, {
167*1c12ee1eSDan Willemsen		breakAt:    11, // break within newspb.KeyValueAttachment
168*1c12ee1eSDan Willemsen		wantPaths:  append(wantPaths[:11:11], wantPaths[15:]...),
169*1c12ee1eSDan Willemsen		wantValues: append(wantValues[:11:11], wantValues[15:]...),
170*1c12ee1eSDan Willemsen	}, {
171*1c12ee1eSDan Willemsen		errorAt:    17, // return error within newspb.Article.Tags
172*1c12ee1eSDan Willemsen		wantPaths:  wantPaths[:17],
173*1c12ee1eSDan Willemsen		wantValues: wantValues[:17],
174*1c12ee1eSDan Willemsen		wantError:  cmpopts.AnyError,
175*1c12ee1eSDan Willemsen	}, {
176*1c12ee1eSDan Willemsen		breakAt:    17, // break within newspb.Article.Tags
177*1c12ee1eSDan Willemsen		wantPaths:  append(wantPaths[:17:17], wantPaths[18:]...),
178*1c12ee1eSDan Willemsen		wantValues: append(wantValues[:17:17], wantValues[18:]...),
179*1c12ee1eSDan Willemsen	}, {
180*1c12ee1eSDan Willemsen		terminateAt: 17, // terminate within newspb.Article.Tags
181*1c12ee1eSDan Willemsen		wantPaths:   wantPaths[:17],
182*1c12ee1eSDan Willemsen		wantValues:  wantValues[:17],
183*1c12ee1eSDan Willemsen	}, {
184*1c12ee1eSDan Willemsen		errorAt:    13, // return error within newspb.KeyValueAttachment.Data
185*1c12ee1eSDan Willemsen		wantPaths:  wantPaths[:13],
186*1c12ee1eSDan Willemsen		wantValues: wantValues[:13],
187*1c12ee1eSDan Willemsen		wantError:  cmpopts.AnyError,
188*1c12ee1eSDan Willemsen	}, {
189*1c12ee1eSDan Willemsen		breakAt:    13, // break within newspb.KeyValueAttachment.Data
190*1c12ee1eSDan Willemsen		wantPaths:  append(wantPaths[:13:13], wantPaths[15:]...),
191*1c12ee1eSDan Willemsen		wantValues: append(wantValues[:13:13], wantValues[15:]...),
192*1c12ee1eSDan Willemsen	}, {
193*1c12ee1eSDan Willemsen		terminateAt: 13, // terminate within newspb.KeyValueAttachment.Data
194*1c12ee1eSDan Willemsen		wantPaths:   wantPaths[:13],
195*1c12ee1eSDan Willemsen		wantValues:  wantValues[:13],
196*1c12ee1eSDan Willemsen	}}
197*1c12ee1eSDan Willemsen	for _, tt := range tests {
198*1c12ee1eSDan Willemsen		t.Run("", func(t *testing.T) {
199*1c12ee1eSDan Willemsen			var gotPaths []string
200*1c12ee1eSDan Willemsen			var gotValues []protoreflect.Value
201*1c12ee1eSDan Willemsen			var stackPaths []string
202*1c12ee1eSDan Willemsen			var stackValues []protoreflect.Value
203*1c12ee1eSDan Willemsen			gotError := Options{
204*1c12ee1eSDan Willemsen				Stable:   true,
205*1c12ee1eSDan Willemsen				Resolver: tt.resolver,
206*1c12ee1eSDan Willemsen			}.Range(m,
207*1c12ee1eSDan Willemsen				func(p protopath.Values) error {
208*1c12ee1eSDan Willemsen					gotPaths = append(gotPaths, p.Path[1:].String())
209*1c12ee1eSDan Willemsen					stackPaths = append(stackPaths, p.Path[1:].String())
210*1c12ee1eSDan Willemsen					gotValues = append(gotValues, p.Index(-1).Value)
211*1c12ee1eSDan Willemsen					stackValues = append(stackValues, p.Index(-1).Value)
212*1c12ee1eSDan Willemsen					switch {
213*1c12ee1eSDan Willemsen					case tt.errorAt > 0 && tt.errorAt == len(gotPaths):
214*1c12ee1eSDan Willemsen						return cmpopts.AnyError
215*1c12ee1eSDan Willemsen					case tt.breakAt > 0 && tt.breakAt == len(gotPaths):
216*1c12ee1eSDan Willemsen						return Break
217*1c12ee1eSDan Willemsen					case tt.terminateAt > 0 && tt.terminateAt == len(gotPaths):
218*1c12ee1eSDan Willemsen						return Terminate
219*1c12ee1eSDan Willemsen					default:
220*1c12ee1eSDan Willemsen						return nil
221*1c12ee1eSDan Willemsen					}
222*1c12ee1eSDan Willemsen				},
223*1c12ee1eSDan Willemsen				func(p protopath.Values) error {
224*1c12ee1eSDan Willemsen					gotPath := p.Path[1:].String()
225*1c12ee1eSDan Willemsen					wantPath := stackPaths[len(stackPaths)-1]
226*1c12ee1eSDan Willemsen					if wantPath != gotPath {
227*1c12ee1eSDan Willemsen						t.Errorf("%d: pop path mismatch: got %v, want %v", len(gotPaths), gotPath, wantPath)
228*1c12ee1eSDan Willemsen					}
229*1c12ee1eSDan Willemsen					gotValue := p.Index(-1).Value
230*1c12ee1eSDan Willemsen					wantValue := stackValues[len(stackValues)-1]
231*1c12ee1eSDan Willemsen					if diff := cmp.Diff(wantValue, gotValue, transformReflectValue, protocmp.Transform()); diff != "" {
232*1c12ee1eSDan Willemsen						t.Errorf("%d: pop value mismatch (-want +got):\n%v", len(gotValues), diff)
233*1c12ee1eSDan Willemsen					}
234*1c12ee1eSDan Willemsen					stackPaths = stackPaths[:len(stackPaths)-1]
235*1c12ee1eSDan Willemsen					stackValues = stackValues[:len(stackValues)-1]
236*1c12ee1eSDan Willemsen					return nil
237*1c12ee1eSDan Willemsen				},
238*1c12ee1eSDan Willemsen			)
239*1c12ee1eSDan Willemsen			if n := len(stackPaths) + len(stackValues); n > 0 {
240*1c12ee1eSDan Willemsen				t.Errorf("stack mismatch: got %d unpopped items", n)
241*1c12ee1eSDan Willemsen			}
242*1c12ee1eSDan Willemsen			if diff := cmp.Diff(tt.wantPaths, gotPaths); diff != "" {
243*1c12ee1eSDan Willemsen				t.Errorf("paths mismatch (-want +got):\n%s", diff)
244*1c12ee1eSDan Willemsen			}
245*1c12ee1eSDan Willemsen			if diff := cmp.Diff(tt.wantValues, gotValues, transformReflectValue, protocmp.Transform()); diff != "" {
246*1c12ee1eSDan Willemsen				t.Errorf("values mismatch (-want +got):\n%s", diff)
247*1c12ee1eSDan Willemsen			}
248*1c12ee1eSDan Willemsen			if !cmp.Equal(gotError, tt.wantError, cmpopts.EquateErrors()) {
249*1c12ee1eSDan Willemsen				t.Errorf("error mismatch: got %v, want %v", gotError, tt.wantError)
250*1c12ee1eSDan Willemsen			}
251*1c12ee1eSDan Willemsen		})
252*1c12ee1eSDan Willemsen	}
253*1c12ee1eSDan Willemsen}
254