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