github.com/cosmos/cosmos-proto@v1.0.0-beta.3/testpb/proto_methods_test.go (about) 1 package testpb 2 3 import ( 4 "fmt" 5 "math" 6 "testing" 7 8 "github.com/stretchr/testify/require" 9 "google.golang.org/protobuf/proto" 10 "google.golang.org/protobuf/reflect/protoreflect" 11 "google.golang.org/protobuf/runtime/protoiface" 12 "google.golang.org/protobuf/runtime/protoimpl" 13 "google.golang.org/protobuf/types/dynamicpb" 14 "pgregory.net/rapid" 15 ) 16 17 func TestProtoMethods(t *testing.T) { 18 t.Run("testSize", rapid.MakeCheck(testSize)) 19 t.Run("testMarshal", rapid.MakeCheck(testMarshal)) 20 t.Run("testUnmarshal", rapid.MakeCheck(testUnmarshal)) 21 } 22 23 func testSize(t *rapid.T) { 24 slowMsg := getRapidMsg(t) 25 fastMsg := slowMsg.ProtoReflect() 26 dyn := dynamicpb.NewMessage(md_A) 27 populateDynamicMsg(dyn, fastMsg) 28 methods := fastMsg.ProtoMethods() 29 30 result := methods.Size(protoiface.SizeInput{Message: fastMsg}) 31 expected := proto.Size(dyn) 32 33 require.Equal(t, expected, result.Size) 34 } 35 36 func testMarshal(t *rapid.T) { 37 msg := getRapidMsg(t) 38 fastMsg := msg.ProtoReflect() 39 dyn := dynamicpb.NewMessage(md_A) 40 populateDynamicMsg(dyn, fastMsg) 41 42 result, err := proto.MarshalOptions{Deterministic: true}.Marshal(fastMsg.Interface()) 43 require.NoError(t, err) 44 45 canonical, err := proto.MarshalOptions{Deterministic: true}.Marshal(dyn) 46 require.NoError(t, err) 47 48 require.Equal(t, canonical, result) 49 } 50 51 func testUnmarshal(t *rapid.T) { 52 a := getRapidMsg(t) 53 fastMsg := a.ProtoReflect() 54 dyn := dynamicpb.NewMessage(md_A) 55 populateDynamicMsg(dyn, fastMsg) 56 bz, err := proto.MarshalOptions{Deterministic: true}.Marshal(dyn) 57 require.NoError(t, err) 58 59 aa := A{} 60 fastaa := aa.ProtoReflect() 61 err = proto.UnmarshalOptions{ 62 NoUnkeyedLiterals: struct{}{}, 63 Merge: false, 64 AllowPartial: false, 65 DiscardUnknown: false, 66 Resolver: nil, 67 }.Unmarshal(bz, fastaa.Interface()) 68 require.NoError(t, err) 69 70 require.True(t, proto.Equal(fastMsg.Interface(), fastaa.Interface()), fmt.Sprintf("left: %+v\nright:%+v", fastMsg, fastaa)) 71 } 72 73 func TestNegativeZero(t *testing.T) { 74 75 testCases := []struct { 76 name string 77 value float64 78 }{ 79 { 80 name: "negative 0", 81 value: math.Copysign(0, -1), 82 }, 83 { 84 name: "negative float", 85 value: -0.420, 86 }, 87 { 88 name: "regular zero", 89 value: 0, 90 }, 91 } 92 93 for _, tc := range testCases { 94 t.Run(tc.name, func(t *testing.T) { 95 a := A{} 96 a.DOUBLE = tc.value 97 98 dyn := dynamicpb.NewMessage(md_A) 99 dyn.Set(fd_A_DOUBLE, protoreflect.ValueOfFloat64(tc.value)) 100 101 bz, err := proto.MarshalOptions{Deterministic: true}.Marshal(dyn) 102 require.NoError(t, err) 103 104 bz2, err := proto.Marshal(a.ProtoReflect().Interface()) 105 require.NoError(t, err) 106 107 require.Equal(t, bz, bz2) 108 }) 109 } 110 } 111 112 func populateDynamicMsg(dyn *dynamicpb.Message, msg protoreflect.Message) { 113 msg.Range(func(descriptor protoreflect.FieldDescriptor, value protoreflect.Value) bool { 114 if descriptor.IsMap() { 115 dynMap := dyn.Mutable(descriptor).Map() 116 underlying := value.Map() 117 underlying.Range(func(key protoreflect.MapKey, value protoreflect.Value) bool { 118 dynMap.Set(key, value) 119 return true 120 }) 121 dyn.Set(fd_A_MAP, protoreflect.ValueOfMap(dynMap)) 122 } else if descriptor.IsList() { 123 dynList := dyn.Mutable(descriptor).List() 124 underlying := value.List() 125 for i := 0; i < underlying.Len(); i++ { 126 dynList.Append(underlying.Get(i)) 127 } 128 dyn.Set(descriptor, protoreflect.ValueOfList(dynList)) 129 } else { 130 dyn.Set(descriptor, value) 131 } 132 return true 133 }) 134 } 135 136 func getRapidMsg(t *rapid.T) A { 137 return A{ 138 Enum: Enumeration(rapid.IntRange(0, 1).Draw(t, "enum")), 139 SomeBoolean: rapid.Bool().Draw(t, "SomeBool"), 140 INT32: rapid.Int32().Draw(t, "INT32"), 141 SINT32: rapid.Int32().Draw(t, "SINT32"), 142 UINT32: rapid.Uint32().Draw(t, "UINT32"), 143 INT64: rapid.Int64().Draw(t, "INT64"), 144 SING64: rapid.Int64().Draw(t, "SING64"), 145 UINT64: rapid.Uint64().Draw(t, "UINT64"), 146 SFIXED32: rapid.Int32().Draw(t, "SFIXED32"), 147 FIXED32: rapid.Uint32().Draw(t, "FIXED32"), 148 FLOAT: rapid.Float32().Draw(t, "FLOAT"), 149 SFIXED64: rapid.Int64().Draw(t, "SFIXED64"), 150 FIXED64: rapid.Uint64().Draw(t, "FIXED64"), 151 DOUBLE: rapid.Float64().Draw(t, "DOUBLE"), 152 STRING: rapid.String().Draw(t, "STRING"), 153 BYTES: rapid.SliceOf(rapid.Byte()).Draw(t, "byte slice"), 154 MESSAGE: genMessageB.Draw(t, "MESSAGE"), 155 LIST: rapid.SliceOf(genMessageB).Draw(t, "LIST"), 156 ONEOF: genOneOf.Draw(t, "one of"), 157 MAP: rapid.MapOf(rapid.String(), genMessageB).Draw(t, "map[string]*B"), 158 LIST_ENUM: rapid.SliceOf(genEnumSlice).Draw(t, "slice enum"), 159 } 160 } 161 162 var genEnumSlice = rapid.Custom(func(t *rapid.T) Enumeration { 163 n := rapid.Int32Range(0, 1).Draw(t, "int32") 164 return Enumeration(n) 165 }) 166 167 var genOneOf = rapid.Custom(func(t *rapid.T) isA_ONEOF { 168 oneof := rapid.OneOf(genOneOfB, genOneOfString).Draw(t, "oneof") 169 return oneof 170 }) 171 172 var genOneOfB = rapid.Custom(func(t *rapid.T) isA_ONEOF { 173 return &A_ONEOF_B{ONEOF_B: genMessageB.Draw(t, "message B in one of")} 174 }) 175 176 var genOneOfString = rapid.Custom(func(t *rapid.T) isA_ONEOF { 177 return &A_ONEOF_STRING{ONEOF_STRING: rapid.StringN(1, -1, -1).Draw(t, "string in one of")} 178 }) 179 180 var genMessageB = rapid.Custom(func(t *rapid.T) *B { 181 msg := B{ 182 state: protoimpl.MessageState{}, 183 sizeCache: 0, 184 unknownFields: nil, 185 X: rapid.String().Draw(t, "X"), 186 } 187 return &msg 188 })