Skip to content

Commit f124e7b

Browse files
committed
chore: Fix event sorting for testing
Previous sorting method was not stable, and only worked coincidentally for the two use cases that were using it. This new method works on more event types and only sorts contiguous events. This should make the sort usable when we add parallel apply and watch instead of poll.
1 parent 5d06234 commit f124e7b

File tree

3 files changed

+46
-32
lines changed

3 files changed

+46
-32
lines changed

pkg/apply/applier_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package apply
55

66
import (
77
"context"
8-
"sort"
98
"sync"
109
"testing"
1110
"time"
@@ -1511,7 +1510,7 @@ func TestApplier(t *testing.T) {
15111510
}
15121511

15131512
// sort to allow comparison of multiple apply/prune tasks in the same task group
1514-
sort.Sort(testutil.GroupedEventsByID(receivedEvents))
1513+
testutil.SortExpEvents(receivedEvents)
15151514

15161515
// Validate the rest of the events
15171516
testutil.AssertEqual(t, tc.expectedEvents, receivedEvents,

pkg/testutil/events.go

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package testutil
55

66
import (
77
"fmt"
8+
"sort"
89

910
"github.com/google/go-cmp/cmp"
1011
"github.com/google/go-cmp/cmp/cmpopts"
@@ -414,6 +415,21 @@ func RemoveEqualEvents(in []ExpEvent, expected ExpEvent) ([]ExpEvent, int) {
414415
return in, matches
415416
}
416417

418+
// SortExpEvents sorts a list of ExpEvents so they can be compared for equality.
419+
//
420+
// This is a stable sort which only sorts nearly identical contiguous events by
421+
// object identifier, to make the full list easier to validate.
422+
//
423+
// You may need to remove StatusEvents from the list before comparing, because
424+
// these events are fully asynchronous and non-contiguous.
425+
//
426+
// Comparison Options:
427+
// A) Expect(received).To(testutil.Equal(expected))
428+
// B) testutil.assertEqual(t, expected, received)
429+
func SortExpEvents(events []ExpEvent) {
430+
sort.SliceStable(events, GroupedEventsByID(events).Less)
431+
}
432+
417433
// GroupedEventsByID implements sort.Interface for []ExpEvent based on
418434
// the serialized ObjMetadata of Apply, Prune, and Delete events within the same
419435
// task group.
@@ -428,48 +444,48 @@ func (ape GroupedEventsByID) Swap(i, j int) { ape[i], ape[j] = ape[j], ape[i] }
428444
func (ape GroupedEventsByID) Less(i, j int) bool {
429445
if ape[i].EventType != ape[j].EventType {
430446
// don't change order if not the same type
431-
return false
447+
return i < j
432448
}
433449
switch ape[i].EventType {
434450
case event.ApplyType:
435-
if ape[i].ApplyEvent.GroupName != ape[j].ApplyEvent.GroupName {
436-
// don't change order if not the same task group
437-
return false
451+
if ape[i].ApplyEvent.GroupName == ape[j].ApplyEvent.GroupName &&
452+
ape[i].ApplyEvent.Status == ape[j].ApplyEvent.Status &&
453+
ape[i].ApplyEvent.Identifier.GroupKind == ape[j].ApplyEvent.Identifier.GroupKind {
454+
// apply events are predictably ordered by GroupKind, due to
455+
// ordering.SortableMetas.
456+
// So we only need to sort by namespace & name.
457+
return ape[i].ApplyEvent.Identifier.String() < ape[j].ApplyEvent.Identifier.String()
438458
}
439-
return ape[i].ApplyEvent.Identifier.String() < ape[j].ApplyEvent.Identifier.String()
440459
case event.PruneType:
441-
if ape[i].PruneEvent.GroupName != ape[j].PruneEvent.GroupName {
442-
// don't change order if not the same task group
443-
return false
460+
if ape[i].PruneEvent.GroupName == ape[j].PruneEvent.GroupName &&
461+
ape[i].PruneEvent.Status == ape[j].PruneEvent.Status &&
462+
ape[i].PruneEvent.Identifier.GroupKind == ape[j].PruneEvent.Identifier.GroupKind {
463+
// prune events are predictably ordered by GroupKind, due to
464+
// ordering.SortableMetas (reversed).
465+
// So we only need to sort by namespace & name.
466+
return ape[i].PruneEvent.Identifier.String() < ape[j].PruneEvent.Identifier.String()
444467
}
445-
return ape[i].PruneEvent.Identifier.String() < ape[j].PruneEvent.Identifier.String()
446468
case event.DeleteType:
447-
if ape[i].DeleteEvent.GroupName != ape[j].DeleteEvent.GroupName {
448-
// don't change order if not the same task group
449-
return false
469+
if ape[i].DeleteEvent.GroupName == ape[j].DeleteEvent.GroupName &&
470+
ape[i].DeleteEvent.Status == ape[j].DeleteEvent.Status &&
471+
ape[i].DeleteEvent.Identifier.GroupKind == ape[j].DeleteEvent.Identifier.GroupKind {
472+
// delete events are predictably ordered by GroupKind, due to
473+
// ordering.SortableMetas (reversed).
474+
// So we only need to sort by namespace & name.
475+
return ape[i].DeleteEvent.Identifier.String() < ape[j].DeleteEvent.Identifier.String()
450476
}
451-
return ape[i].DeleteEvent.Identifier.String() < ape[j].DeleteEvent.Identifier.String()
452477
case event.WaitType:
453-
if ape[i].WaitEvent.GroupName != ape[j].WaitEvent.GroupName {
454-
// don't change order if not the same task group
455-
return false
456-
}
457-
if ape[i].WaitEvent.Status != ape[j].WaitEvent.Status {
458-
// don't change order if not the same status
459-
return false
460-
}
461-
if ape[i].WaitEvent.Status != event.ReconcileSuccessful {
462-
// pending, skipped, and timeout status are predictably ordered
478+
if ape[i].WaitEvent.GroupName == ape[j].WaitEvent.GroupName &&
479+
ape[i].WaitEvent.Status == ape[j].WaitEvent.Status &&
480+
ape[i].WaitEvent.Status == event.ReconcileSuccessful {
481+
// pending, skipped, and timeout operations are predictably ordered
463482
// using the order in WaitTask.Ids.
464483
// So we only need to sort Reconciled events, which occur in the
465484
// order the Waitask receives StatusEvents with Current/NotFound.
466-
return false
485+
return ape[i].WaitEvent.Identifier.String() < ape[j].WaitEvent.Identifier.String()
467486
}
468-
return ape[i].WaitEvent.Identifier.String() < ape[j].WaitEvent.Identifier.String()
469487
case event.ValidationType:
470488
return ape[i].ValidationEvent.Identifiers.Hash() < ape[j].ValidationEvent.Identifiers.Hash()
471-
default:
472-
// don't change order if not ApplyType, PruneType, or DeleteType
473-
return false
474489
}
490+
return i < j
475491
}

test/e2e/continue_on_error_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package e2e
66
import (
77
"context"
88
"errors"
9-
"sort"
109

1110
. "github.com/onsi/ginkgo"
1211
. "github.com/onsi/gomega"
@@ -165,7 +164,7 @@ func continueOnErrorTest(ctx context.Context, c client.Client, invConfig invconf
165164
}
166165
receivedEvents := testutil.EventsToExpEvents(applierEvents)
167166
// sort to allow comparison of multiple ApplyTasks in the same task group
168-
sort.Sort(testutil.GroupedEventsByID(receivedEvents))
167+
testutil.SortExpEvents(receivedEvents)
169168
Expect(receivedEvents).To(testutil.Equal(expEvents))
170169

171170
By("Verify pod1 created")

0 commit comments

Comments
 (0)