| 1 | package control |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "sync" |
| 6 | "testing" |
| 7 | "time" |
| 8 | ) |
| 9 | |
| 10 | func TestCancelSessionSignalsAndAcknowledgesWhileSamplingBlocked(t *testing.T) { |
| 11 | isolateControlConfigHome(t) |
| 12 | c := newOwnedTestController(t, Options{SessionDir: t.TempDir()}) |
| 13 | defer c.Close() |
| 14 | started, cancelled, releaseBody := make(chan struct{}), make(chan struct{}), make(chan struct{}) |
| 15 | defer close(releaseBody) |
| 16 | c.runGuarded(func(ctx context.Context) error { |
| 17 | close(started) |
| 18 | <-ctx.Done() |
| 19 | close(cancelled) |
| 20 | <-releaseBody |
| 21 | return ctx.Err() |
| 22 | }) |
| 23 | <-started |
| 24 | c.runtimeState.mu.Lock() |
| 25 | unlock := sync.OnceFunc(c.runtimeState.mu.Unlock) |
| 26 | defer unlock() |
| 27 | returned := make(chan CancelReceipt, 1) |
| 28 | go func() { returned <- c.CancelSessionFrom("test") }() |
| 29 | select { |
| 30 | case <-cancelled: |
| 31 | case <-time.After(5 * time.Second): |
| 32 | t.Fatal("Stop waited for state observation before signalling execution") |
| 33 | } |
| 34 | select { |
| 35 | case receipt := <-returned: |
| 36 | if !receipt.Accepted || receipt.AlreadyIdle || receipt.RuntimeEpoch == "" { |
| 37 | t.Fatalf("incorrect cancellation receipt: %+v", receipt) |
| 38 | } |
| 39 | case <-time.After(5 * time.Second): |
| 40 | t.Fatal("Stop receipt waited for state observation") |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | func TestMaintenanceCancelAcknowledgesWhilePublicationBlocked(t *testing.T) { |
| 45 | isolateControlConfigHome(t) |
| 46 | c := newOwnedTestController(t, Options{SessionDir: t.TempDir()}) |
| 47 | defer c.Close() |
| 48 | ctx, cancel := context.WithCancel(context.Background()) |
| 49 | op := &controllerMaintenance{maintenanceIdentity: maintenanceIdentity{id: "cancel-test"}, activity: "running", cancel: cancel, done: make(chan struct{})} |
| 50 | c.mu.Lock() |
| 51 | c.maintenance = op |
| 52 | c.mu.Unlock() |
| 53 | defer func() { |
| 54 | c.mu.Lock() |
| 55 | c.maintenance = nil |
| 56 | close(op.done) |
| 57 | c.mu.Unlock() |
| 58 | }() |
| 59 | op.publishMu.Lock() |
| 60 | defer op.publishMu.Unlock() |
| 61 | returned := make(chan CancelReceipt, 1) |
| 62 | go func() { returned <- c.CancelSessionFrom("test") }() |
| 63 | select { |
| 64 | case <-ctx.Done(): |
| 65 | case <-time.After(5 * time.Second): |
| 66 | t.Fatal("maintenance cancellation did not signal its owner") |
| 67 | } |
| 68 | select { |
| 69 | case receipt := <-returned: |
| 70 | if !receipt.Accepted || receipt.AlreadyIdle { |
| 71 | t.Fatalf("maintenance receipt = %+v", receipt) |
| 72 | } |
| 73 | case <-time.After(5 * time.Second): |
| 74 | t.Fatal("maintenance receipt waited for publication") |
| 75 | } |
| 76 | } |
| 77 |