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  }