vitess.io/vitess@v0.16.2/go/vt/vtctl/grpcvtctldserver/testutil/proto_compare.go (about) 1 /* 2 Copyright 2021 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package testutil 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "sort" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26 "google.golang.org/protobuf/proto" 27 28 "vitess.io/vitess/go/test/utils" 29 30 logutilpb "vitess.io/vitess/go/vt/proto/logutil" 31 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 32 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 33 ) 34 35 // AssertEmergencyReparentShardResponsesEqual asserts that two 36 // vtctldatapb.EmergencyReparentShardResponse objects are equal, ignoring their 37 // respective Events field in the comparison. 38 func AssertEmergencyReparentShardResponsesEqual(t *testing.T, expected *vtctldatapb.EmergencyReparentShardResponse, actual *vtctldatapb.EmergencyReparentShardResponse, msgAndArgs ...any) { 39 t.Helper() 40 41 expected = proto.Clone(expected).(*vtctldatapb.EmergencyReparentShardResponse) 42 expected.Events = nil 43 44 actual = proto.Clone(actual).(*vtctldatapb.EmergencyReparentShardResponse) 45 actual.Events = nil 46 47 utils.MustMatch(t, expected, actual) 48 } 49 50 // AssertLogutilEventsMatch asserts that two slices of Events match, by their 51 // .Value fields. In the expected slice, .Value is treated as a regular 52 // expression; that is, it is passed as a regexp-like string to assert.Regexp. 53 // 54 // NOTE: To match events independent of ordering, callers should run both their 55 // expected and actual event slices through the EventValueSorter before calling 56 // this function. This will mutate the slices, so make a copy first if that is 57 // an issue for your use case. 58 func AssertLogutilEventsMatch(t testing.TB, expected []*logutilpb.Event, actual []*logutilpb.Event) { 59 t.Helper() 60 61 f := func(e *logutilpb.Event) *logutilpb.Event { 62 return &logutilpb.Event{ 63 Value: e.Value, 64 } 65 } 66 expected = clearEvents(expected, f) 67 actual = clearEvents(actual, f) 68 69 expectedBytes, err := json.Marshal(expected) 70 if !assert.NoError(t, err, "could not marshal expected events as json, assertion messages will be impacted") { 71 expectedBytes = nil 72 } 73 74 actualBytes, err := json.Marshal(actual) 75 if !assert.NoError(t, err, "could not marshal actual events as json, assertion messages will be impacted") { 76 actualBytes = nil 77 } 78 79 if !assert.Equal(t, len(expected), len(actual), "differing number of events; expected %d, have %d\nexpected bytes: %s\nactual bytes: %s\n", len(expected), len(actual), expectedBytes, actualBytes) { 80 return 81 } 82 83 for i, expectedEvent := range expected { 84 actualEvent := actual[i] 85 assert.Regexp(t, expectedEvent.Value, actualEvent.Value, "event %d mismatch", i) 86 } 87 } 88 89 func clearEvents(events []*logutilpb.Event, f func(*logutilpb.Event) *logutilpb.Event) []*logutilpb.Event { 90 if events == nil { 91 return nil 92 } 93 94 result := make([]*logutilpb.Event, len(events)) 95 for i, event := range events { 96 result[i] = f(event) 97 } 98 99 return result 100 } 101 102 // AssertPlannedReparentShardResponsesEqual asserts that two 103 // vtctldatapb.PlannedReparentShardResponse objects are equal, ignoring their 104 // respective Events field in the comparison. 105 func AssertPlannedReparentShardResponsesEqual(t *testing.T, expected *vtctldatapb.PlannedReparentShardResponse, actual *vtctldatapb.PlannedReparentShardResponse) { 106 t.Helper() 107 108 expected = proto.Clone(expected).(*vtctldatapb.PlannedReparentShardResponse) 109 expected.Events = nil 110 111 actual = proto.Clone(actual).(*vtctldatapb.PlannedReparentShardResponse) 112 actual.Events = nil 113 114 utils.MustMatch(t, expected, actual) 115 } 116 117 func AssertSameTablets(t *testing.T, expected, actual []*topodatapb.Tablet) { 118 sort.Slice(expected, func(i, j int) bool { 119 return fmt.Sprintf("%v", expected[i]) < fmt.Sprintf("%v", expected[j]) 120 }) 121 sort.Slice(actual, func(i, j int) bool { 122 return fmt.Sprintf("%v", actual[i]) < fmt.Sprintf("%v", actual[j]) 123 }) 124 utils.MustMatch(t, expected, actual) 125 } 126 127 // AssertKeyspacesEqual is a convenience function to assert that two 128 // vtctldatapb.Keyspace objects are equal, after clearing out any reserved 129 // proto XXX_ fields. 130 func AssertKeyspacesEqual(t *testing.T, expected *vtctldatapb.Keyspace, actual *vtctldatapb.Keyspace, msgAndArgs ...any) { 131 t.Helper() 132 utils.MustMatch(t, expected, actual) 133 } 134 135 // AssertLogutilEventsOccurred asserts that for something containing a slice of 136 // logutilpb.Event, that the container is non-nil, and the event slice is 137 // non-zero. 138 // 139 // This test function is generalized with an anonymous interface that any 140 // protobuf type containing a slice of logutilpb.Event elements called Events, 141 // which is the convention in protobuf types in the Vitess codebase, already 142 // implements. 143 func AssertLogutilEventsOccurred(t *testing.T, container interface{ GetEvents() []*logutilpb.Event }, msgAndArgs ...any) { 144 t.Helper() 145 146 if container == nil { 147 assert.Fail(t, "Events container must not be nil", msgAndArgs...) 148 149 return 150 } 151 152 assert.Greater(t, len(container.GetEvents()), 0, msgAndArgs...) 153 } 154 155 // AssertNoLogutilEventsOccurred asserts that for something containing a slice 156 // of logutilpb.Event, that the container is either nil, or that the event slice 157 // is exactly zero length. 158 // 159 // This test function is generalized with an anonymous interface that any 160 // protobuf type containing a slice of logutilpb.Event elements called Events, 161 // which is the convention in protobuf types in the Vitess codebase, already 162 // implements. 163 func AssertNoLogutilEventsOccurred(t *testing.T, container interface{ GetEvents() []*logutilpb.Event }, msgAndArgs ...any) { 164 t.Helper() 165 166 if container == nil { 167 return 168 } 169 170 assert.Equal(t, len(container.GetEvents()), 0, msgAndArgs...) 171 } 172 173 // EventValueSorter implements sort.Interface for slices of logutil.Event, 174 // ordering by .Value lexicographically. 175 type EventValueSorter []*logutilpb.Event 176 177 func (events EventValueSorter) Len() int { return len(events) } 178 func (events EventValueSorter) Swap(i, j int) { events[i], events[j] = events[j], events[i] } 179 func (events EventValueSorter) Less(i, j int) bool { return events[i].Value < events[j].Value }