返回 DeepSeek-Reasonix
session_destroy_wait_test.go
根目录 / desktop / session_destroy_wait_test.go
1 package main
2
3 import (
4 "testing"
5 "time"
6
7 "reasonix/internal/control"
8 "reasonix/internal/jobs"
9 )
10
11 func TestWaitDestroyHandlesPrefersBoundedWait(t *testing.T) {
12 requested := make(chan time.Duration, 1)
13 unboundedCalled := make(chan struct{}, 1)
14 timedOut := waitDestroyHandles([]control.SessionDestroyHandle{{
15 Wait: func() jobs.TeardownResult {
16 unboundedCalled <- struct{}{}
17 return jobs.TeardownResult{}
18 },
19 WaitFor: func(grace time.Duration) jobs.TeardownResult {
20 requested <- grace
21 return jobs.TeardownResult{}
22 },
23 }})
24 if timedOut {
25 t.Fatal("bounded wait reported an unexpected timeout")
26 }
27 select {
28 case grace := <-requested:
29 if grace != desktopSessionRemovalGrace {
30 t.Fatalf("bounded wait grace = %s, want %s", grace, desktopSessionRemovalGrace)
31 }
32 default:
33 t.Fatal("bounded wait was not called")
34 }
35 select {
36 case <-unboundedCalled:
37 t.Fatal("waitDestroyHandles used the unbounded wait when WaitFor was available")
38 default:
39 }
40 }
41
42 func TestWaitDestroyHandlesBoundsLegacyWait(t *testing.T) {
43 release := make(chan struct{})
44 returned := make(chan struct{})
45 defer func() {
46 close(release)
47 select {
48 case <-returned:
49 case <-time.After(time.Second):
50 t.Error("legacy wait goroutine did not finish after release")
51 }
52 }()
53 started := time.Now()
54 timedOut := waitDestroyHandles([]control.SessionDestroyHandle{{
55 Wait: func() jobs.TeardownResult {
56 <-release
57 close(returned)
58 return jobs.TeardownResult{}
59 },
60 }})
61 elapsed := time.Since(started)
62 if !timedOut {
63 t.Fatal("non-cooperative legacy wait did not report a timeout")
64 }
65 if elapsed < desktopSessionRemovalGrace || elapsed > 2*time.Second {
66 t.Fatalf("legacy wait returned after %s, want a bounded wait near %s", elapsed, desktopSessionRemovalGrace)
67 }
68 }
69
70 func TestWaitDestroyHandleBatchesWaitsAcrossSessionsConcurrently(t *testing.T) {
71 started := make(chan int, 2)
72 release := make(chan struct{})
73 batches := make([][]control.SessionDestroyHandle, 2)
74 for i := range batches {
75 index := i
76 batches[i] = []control.SessionDestroyHandle{{
77 WaitFor: func(time.Duration) jobs.TeardownResult {
78 started <- index
79 <-release
80 return jobs.TeardownResult{}
81 },
82 }}
83 }
84 done := make(chan []bool, 1)
85 go func() { done <- waitDestroyHandleBatches(batches) }()
86 seen := map[int]bool{}
87 for range batches {
88 select {
89 case index := <-started:
90 seen[index] = true
91 case <-time.After(time.Second):
92 t.Fatal("session destroy batches did not start concurrently")
93 }
94 }
95 close(release)
96 if !seen[0] || !seen[1] {
97 t.Fatalf("started batches = %v, want both sessions", seen)
98 }
99 select {
100 case timedOut := <-done:
101 if timedOut[0] || timedOut[1] {
102 t.Fatalf("completed destroy batches timed out: %v", timedOut)
103 }
104 case <-time.After(time.Second):
105 t.Fatal("concurrent destroy batches did not finish after release")
106 }
107 }
108
108 lines GO