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  })