github.com/jhump/protoreflect@v1.16.0/dynamic/equal_test.go (about) 1 package dynamic 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/golang/protobuf/proto" 8 9 "github.com/jhump/protoreflect/desc" 10 "github.com/jhump/protoreflect/internal/testprotos" 11 "github.com/jhump/protoreflect/internal/testutil" 12 ) 13 14 func eqm(a, b interface{}) bool { 15 return MessagesEqual(a.(proto.Message), b.(proto.Message)) 16 } 17 18 func eqdm(a, b interface{}) bool { 19 return Equal(a.(*Message), b.(*Message)) 20 } 21 22 func eqpm(a, b interface{}) bool { 23 return proto.Equal(a.(proto.Message), b.(proto.Message)) 24 } 25 26 func TestEquals(t *testing.T) { 27 mdProto3, err := desc.LoadMessageDescriptorForMessage((*testprotos.TestRequest)(nil)) 28 testutil.Ok(t, err) 29 30 dm1 := NewMessage(mdProto3) 31 dm2 := NewMessage(mdProto3) 32 checkEquals(t, dm1, dm2) // sanity check 33 34 dm1.SetFieldByName("foo", []testprotos.Proto3Enum{testprotos.Proto3Enum_VALUE1}) 35 dm1.SetFieldByName("bar", "barfbag") 36 dm1.SetFieldByName("flags", map[string]bool{"a": true, "b": false, "c": true}) 37 38 checkNotEquals(t, dm1, dm2) 39 40 dm2.SetFieldByName("foo", []testprotos.Proto3Enum{testprotos.Proto3Enum_VALUE1}) 41 dm2.SetFieldByName("bar", "barfbag") 42 dm2.SetFieldByName("flags", map[string]bool{"a": true, "b": false, "c": true}) 43 44 checkEquals(t, dm1, dm2) 45 46 // With proto3, setting fields to zero value is not distinguishable from absent fields 47 dm1.Reset() 48 dm2.Reset() 49 dm1.SetFieldByName("foo", []testprotos.Proto3Enum{}) 50 dm1.SetFieldByName("bar", "") 51 dm1.SetFieldByName("flags", map[string]bool{}) 52 53 checkEquals(t, dm1, dm2) 54 55 // Now check proto2 messages 56 mdProto2, err := desc.LoadMessageDescriptorForMessage((*testprotos.UnaryFields)(nil)) 57 testutil.Ok(t, err) 58 59 dm1 = NewMessage(mdProto2) 60 dm2 = NewMessage(mdProto2) 61 checkEquals(t, dm1, dm2) // sanity check 62 63 dm1.SetFieldByName("i", int32(123)) 64 dm1.SetFieldByName("v", "blueberry") 65 66 checkNotEquals(t, dm1, dm2) 67 68 dm2.SetFieldByName("i", int32(123)) 69 dm2.SetFieldByName("v", "blueberry") 70 71 checkEquals(t, dm1, dm2) 72 73 // In proto2, however, we can distinguish between present and zero/default values 74 dm1.Reset() 75 dm2.Reset() 76 dm1.SetFieldByName("i", int32(0)) 77 dm1.SetFieldByName("v", "") 78 79 checkNotEquals(t, dm1, dm2) 80 81 // But, even in proto2, empty repeated and map fields are indistinguishable from absent fields 82 mdProto2, err = desc.LoadMessageDescriptorForMessage((*testprotos.RepeatedFields)(nil)) 83 testutil.Ok(t, err) 84 85 dm1 = NewMessage(mdProto2) 86 dm2 = NewMessage(mdProto2) 87 checkEquals(t, dm1, dm2) // sanity check 88 89 dm1.SetFieldByName("i", []int32{}) 90 dm1.SetFieldByName("v", []string{}) 91 92 checkEquals(t, dm1, dm2) 93 94 mdProto2, err = desc.LoadMessageDescriptorForMessage((*testprotos.MapValFields)(nil)) 95 testutil.Ok(t, err) 96 97 dm1 = NewMessage(mdProto2) 98 dm2 = NewMessage(mdProto2) 99 checkEquals(t, dm1, dm2) // sanity check 100 101 dm1.SetFieldByName("i", map[string]int32{}) 102 dm1.SetFieldByName("v", map[string]string{}) 103 104 checkEquals(t, dm1, dm2) 105 } 106 107 func checkEquals(t *testing.T, a, b *Message) { 108 testutil.Ceq(t, a, b, eqdm) 109 testutil.Ceq(t, a, b, eqm) 110 testutil.Ceq(t, b, a, eqdm) 111 testutil.Ceq(t, b, a, eqm) 112 113 // and then compare generated message type to dynamic message 114 msgType := proto.MessageType(a.GetMessageDescriptor().GetFullyQualifiedName()) 115 msg := reflect.New(msgType.Elem()).Interface().(proto.Message) 116 err := a.ConvertTo(msg) 117 testutil.Ok(t, err) 118 testutil.Ceq(t, a, msg, eqm) 119 testutil.Ceq(t, msg, a, eqm) 120 } 121 122 func checkNotEquals(t *testing.T, a, b *Message) { 123 testutil.Cneq(t, a, b, eqdm) 124 testutil.Cneq(t, a, b, eqm) 125 testutil.Cneq(t, b, a, eqdm) 126 testutil.Cneq(t, b, a, eqm) 127 128 // and then compare generated message type to dynamic message 129 msgType := proto.MessageType(a.GetMessageDescriptor().GetFullyQualifiedName()) 130 msg := reflect.New(msgType.Elem()).Interface().(proto.Message) 131 err := a.ConvertTo(msg) 132 testutil.Ok(t, err) 133 testutil.Cneq(t, b, msg, eqm) 134 testutil.Cneq(t, msg, b, eqm) 135 }