| 16 | } |
| 17 | |
| 18 | func TestSet_Replace_Race(t *testing.T) { |
| 19 | // CAUTION: This is a race condition test. It should not be flaky. |
| 20 | // If you see failures in CI, run this test with --count=1000 to induce failures. |
| 21 | |
| 22 | // Create three sets. The first set is just a starter. We'll be replacing it |
| 23 | // with the two latter sets at the same time. |
| 24 | namespaceGVK := corev1.SchemeGroupVersion.WithKind("Namespace") |
| 25 | roleGVK := rbacv1.SchemeGroupVersion.WithKind("Role") |
| 26 | clusterRoleGVK := rbacv1.SchemeGroupVersion.WithKind("ClusterRole") |
| 27 | |
| 28 | s := NewSet() |
| 29 | s.Add(namespaceGVK) |
| 30 | |
| 31 | other1 := NewSet() |
| 32 | other1.Add(roleGVK) |
| 33 | other2 := NewSet() |
| 34 | other2.Add(clusterRoleGVK) |
| 35 | |
| 36 | // Create two channels which are written to after each Replace succeeds, but |
| 37 | // before releasing locks. We expect identical data to be written to both |
| 38 | // channels. This simulates other work being done. |
| 39 | replaceA := make(chan schema.GroupVersionKind) |
| 40 | replaceB := make(chan schema.GroupVersionKind) |
| 41 | |
| 42 | wait := make(chan struct{}) |
| 43 | wg := sync.WaitGroup{} |
| 44 | wg.Add(4) |
| 45 | |
| 46 | go func() { |
| 47 | <-wait |
| 48 | s.Replace(other1, write(replaceA, roleGVK), write(replaceB, roleGVK)) |
| 49 | wg.Done() |
| 50 | }() |
| 51 | |
| 52 | go func() { |
| 53 | <-wait |
| 54 | s.Replace(other2, write(replaceA, clusterRoleGVK), write(replaceB, clusterRoleGVK)) |
| 55 | wg.Done() |
| 56 | }() |
| 57 | |
| 58 | // Start all goroutines simultaneously. |
| 59 | // It doesn't matter which Replace finishes first or second - we actually |
| 60 | // want it to be possible for either to win in order to ensure that deadlocks |
| 61 | // don't happen and data is written consistently. |
| 62 | close(wait) |
| 63 | |
| 64 | var a1, a2, b1, b2 schema.GroupVersionKind |
| 65 | wg2 := sync.WaitGroup{} |
| 66 | wg2.Add(2) |
| 67 | |
| 68 | // Read from the channels asynchronously to prevent blocking and inducing |
| 69 | // successes. |
| 70 | go func() { |
| 71 | a1 = <-replaceA |
| 72 | a2 = <-replaceA |
| 73 | wg.Done() |
| 74 | }() |
| 75 | go func() { |