|  | // Copyright 2021 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 nullable | 
|  |  | 
|  | import ( | 
|  | "reflect" | 
|  | "testing" | 
|  |  | 
|  | "github.com/google/go-cmp/cmp" | 
|  | "google.golang.org/protobuf/proto" | 
|  | "google.golang.org/protobuf/reflect/protoreflect" | 
|  | "google.golang.org/protobuf/runtime/protoimpl" | 
|  | "google.golang.org/protobuf/testing/protocmp" | 
|  | ) | 
|  |  | 
|  | func Test(t *testing.T) { | 
|  | for _, mt := range []protoreflect.MessageType{ | 
|  | protoimpl.X.ProtoMessageV2Of((*Proto2)(nil)).ProtoReflect().Type(), | 
|  | protoimpl.X.ProtoMessageV2Of((*Proto3)(nil)).ProtoReflect().Type(), | 
|  | } { | 
|  | t.Run(string(mt.Descriptor().FullName()), func(t *testing.T) { | 
|  | testEmptyMessage(t, mt.Zero(), false) | 
|  | testEmptyMessage(t, mt.New(), true) | 
|  | //testMethods(t, mt) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | var methodTestProtos = []protoreflect.MessageType{ | 
|  | protoimpl.X.ProtoMessageV2Of((*Methods)(nil)).ProtoReflect().Type(), | 
|  | } | 
|  |  | 
|  | func TestMethods(t *testing.T) { | 
|  | for _, mt := range methodTestProtos { | 
|  | t.Run(string(mt.Descriptor().FullName()), func(t *testing.T) { | 
|  | testMethods(t, mt) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func testMethods(t *testing.T, mt protoreflect.MessageType) { | 
|  | m1 := mt.New() | 
|  | populated := testPopulateMessage(t, m1, 2) | 
|  | b, err := proto.Marshal(m1.Interface()) | 
|  | if err != nil { | 
|  | t.Errorf("proto.Marshal error: %v", err) | 
|  | } | 
|  | if populated && len(b) == 0 { | 
|  | t.Errorf("len(proto.Marshal) = 0, want >0") | 
|  | } | 
|  | m2 := mt.New() | 
|  | if err := proto.Unmarshal(b, m2.Interface()); err != nil { | 
|  | t.Errorf("proto.Unmarshal error: %v", err) | 
|  | } | 
|  | if diff := cmp.Diff(m1.Interface(), m2.Interface(), protocmp.Transform()); diff != "" { | 
|  | t.Errorf("message mismatch:\n%v", diff) | 
|  | } | 
|  | proto.Reset(m2.Interface()) | 
|  | testEmptyMessage(t, m2, true) | 
|  | proto.Merge(m2.Interface(), m1.Interface()) | 
|  | if diff := cmp.Diff(m1.Interface(), m2.Interface(), protocmp.Transform()); diff != "" { | 
|  | t.Errorf("message mismatch:\n%v", diff) | 
|  | } | 
|  | proto.Merge(mt.New().Interface(), mt.Zero().Interface()) | 
|  | } | 
|  |  | 
|  | func testEmptyMessage(t *testing.T, m protoreflect.Message, wantValid bool) { | 
|  | numFields := func(m protoreflect.Message) (n int) { | 
|  | m.Range(func(protoreflect.FieldDescriptor, protoreflect.Value) bool { | 
|  | n++ | 
|  | return true | 
|  | }) | 
|  | return n | 
|  | } | 
|  |  | 
|  | md := m.Descriptor() | 
|  | if gotValid := m.IsValid(); gotValid != wantValid { | 
|  | t.Errorf("%v.IsValid = %v, want %v", md.FullName(), gotValid, wantValid) | 
|  | } | 
|  | m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { | 
|  | t.Errorf("%v.Range iterated over field %v, want no iteration", md.FullName(), fd.Name()) | 
|  | return true | 
|  | }) | 
|  | fds := md.Fields() | 
|  | for i := 0; i < fds.Len(); i++ { | 
|  | fd := fds.Get(i) | 
|  | if m.Has(fd) { | 
|  | t.Errorf("%v.Has(%v) = true, want false", md.FullName(), fd.Name()) | 
|  | } | 
|  | v := m.Get(fd) | 
|  | switch { | 
|  | case fd.IsList(): | 
|  | if n := v.List().Len(); n > 0 { | 
|  | t.Errorf("%v.Get(%v).List().Len() = %v, want 0", md.FullName(), fd.Name(), n) | 
|  | } | 
|  | ls := m.NewField(fd).List() | 
|  | if fd.Message() != nil { | 
|  | if n := numFields(ls.NewElement().Message()); n > 0 { | 
|  | t.Errorf("%v.NewField(%v).List().NewElement().Message().Len() = %v, want 0", md.FullName(), fd.Name(), n) | 
|  | } | 
|  | } | 
|  | case fd.IsMap(): | 
|  | if n := v.Map().Len(); n > 0 { | 
|  | t.Errorf("%v.Get(%v).Map().Len() = %v, want 0", md.FullName(), fd.Name(), n) | 
|  | } | 
|  | ms := m.NewField(fd).Map() | 
|  | if fd.MapValue().Message() != nil { | 
|  | if n := numFields(ms.NewValue().Message()); n > 0 { | 
|  | t.Errorf("%v.NewField(%v).Map().NewValue().Message().Len() = %v, want 0", md.FullName(), fd.Name(), n) | 
|  | } | 
|  | } | 
|  | case fd.Message() != nil: | 
|  | if n := numFields(v.Message()); n > 0 { | 
|  | t.Errorf("%v.Get(%v).Message().Len() = %v, want 0", md.FullName(), fd.Name(), n) | 
|  | } | 
|  | if n := numFields(m.NewField(fd).Message()); n > 0 { | 
|  | t.Errorf("%v.NewField(%v).Message().Len() = %v, want 0", md.FullName(), fd.Name(), n) | 
|  | } | 
|  | default: | 
|  | if !reflect.DeepEqual(v.Interface(), fd.Default().Interface()) { | 
|  | t.Errorf("%v.Get(%v) = %v, want %v", md.FullName(), fd.Name(), v, fd.Default()) | 
|  | } | 
|  | m.NewField(fd) // should not panic | 
|  | } | 
|  | } | 
|  | ods := md.Oneofs() | 
|  | for i := 0; i < ods.Len(); i++ { | 
|  | od := ods.Get(i) | 
|  | if fd := m.WhichOneof(od); fd != nil { | 
|  | t.Errorf("%v.WhichOneof(%v) = %v, want nil", md.FullName(), od.Name(), fd.Name()) | 
|  | } | 
|  | } | 
|  | if b := m.GetUnknown(); b != nil { | 
|  | t.Errorf("%v.GetUnknown() = %v, want nil", md.FullName(), b) | 
|  | } | 
|  | } | 
|  |  | 
|  | func testPopulateMessage(t *testing.T, m protoreflect.Message, depth int) bool { | 
|  | if depth == 0 { | 
|  | return false | 
|  | } | 
|  | md := m.Descriptor() | 
|  | fds := md.Fields() | 
|  | var populatedMessage bool | 
|  | for i := 0; i < fds.Len(); i++ { | 
|  | populatedField := true | 
|  | fd := fds.Get(i) | 
|  | m.Clear(fd) // should not panic | 
|  | switch { | 
|  | case fd.IsList(): | 
|  | ls := m.Mutable(fd).List() | 
|  | if fd.Message() == nil { | 
|  | ls.Append(scalarValue(fd.Kind())) | 
|  | } else { | 
|  | populatedField = testPopulateMessage(t, ls.AppendMutable().Message(), depth-1) | 
|  | } | 
|  | case fd.IsMap(): | 
|  | ms := m.Mutable(fd).Map() | 
|  | if fd.MapValue().Message() == nil { | 
|  | ms.Set( | 
|  | scalarValue(fd.MapKey().Kind()).MapKey(), | 
|  | scalarValue(fd.MapValue().Kind()), | 
|  | ) | 
|  | } else { | 
|  | // NOTE: Map.Mutable does not work with non-nullable fields. | 
|  | m2 := ms.NewValue().Message() | 
|  | populatedField = testPopulateMessage(t, m2, depth-1) | 
|  | ms.Set( | 
|  | scalarValue(fd.MapKey().Kind()).MapKey(), | 
|  | protoreflect.ValueOfMessage(m2), | 
|  | ) | 
|  | } | 
|  | case fd.Message() != nil: | 
|  | populatedField = testPopulateMessage(t, m.Mutable(fd).Message(), depth-1) | 
|  | default: | 
|  | m.Set(fd, scalarValue(fd.Kind())) | 
|  | } | 
|  | if populatedField && !m.Has(fd) { | 
|  | t.Errorf("%v.Has(%v) = false, want true", md.FullName(), fd.Name()) | 
|  | } | 
|  | populatedMessage = populatedMessage || populatedField | 
|  | } | 
|  | m.SetUnknown(m.GetUnknown()) // should not panic | 
|  | return populatedMessage | 
|  | } | 
|  |  | 
|  | func scalarValue(k protoreflect.Kind) protoreflect.Value { | 
|  | switch k { | 
|  | case protoreflect.BoolKind: | 
|  | return protoreflect.ValueOfBool(true) | 
|  | case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: | 
|  | return protoreflect.ValueOfInt32(-32) | 
|  | case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: | 
|  | return protoreflect.ValueOfInt64(-64) | 
|  | case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: | 
|  | return protoreflect.ValueOfUint32(32) | 
|  | case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: | 
|  | return protoreflect.ValueOfUint64(64) | 
|  | case protoreflect.FloatKind: | 
|  | return protoreflect.ValueOfFloat32(32.32) | 
|  | case protoreflect.DoubleKind: | 
|  | return protoreflect.ValueOfFloat64(64.64) | 
|  | case protoreflect.StringKind: | 
|  | return protoreflect.ValueOfString(string("string")) | 
|  | case protoreflect.BytesKind: | 
|  | return protoreflect.ValueOfBytes([]byte("bytes")) | 
|  | case protoreflect.EnumKind: | 
|  | return protoreflect.ValueOfEnum(1) | 
|  | default: | 
|  | panic("unknown kind: " + k.String()) | 
|  | } | 
|  | } |