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 }