@@ -13,8 +13,7 @@ import (
13
13
14
14
func TestWaitAll (t * testing.T ) {
15
15
t .Parallel ()
16
- ctx , cancelFunc := newTestContextWithTimeout (t , 2 * time .Second )
17
- defer cancelFunc ()
16
+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 2 * time .Second )
18
17
19
18
start := time .Now ()
20
19
countingTsk1 := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
@@ -26,13 +25,15 @@ func TestWaitAll(t *testing.T) {
26
25
err := asynctask .WaitAll (ctx , & asynctask.WaitAllOptions {FailFast : true }, countingTsk1 , countingTsk2 , countingTsk3 , completedTsk )
27
26
elapsed := time .Since (start )
28
27
assert .NoError (t , err )
28
+ cancelTaskExecution ()
29
+
29
30
// should only finish after longest task.
30
31
assert .True (t , elapsed > 10 * 40 * time .Millisecond , fmt .Sprintf ("actually elapsed: %v" , elapsed ))
31
32
}
32
33
33
34
func TestWaitAllFailFastCase (t * testing.T ) {
34
35
t .Parallel ()
35
- ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
36
+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 3 * time .Second )
36
37
37
38
start := time .Now ()
38
39
countingTsk := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
@@ -44,9 +45,10 @@ func TestWaitAllFailFastCase(t *testing.T) {
44
45
err := asynctask .WaitAll (ctx , & asynctask.WaitAllOptions {FailFast : true }, countingTsk , errorTsk , panicTsk , completedTsk )
45
46
countingTskState := countingTsk .State ()
46
47
panicTskState := countingTsk .State ()
48
+ errTskState := errorTsk .State ()
47
49
elapsed := time .Since (start )
48
50
49
- cancelFunc () // all assertion variable captured, cancel counting task
51
+ cancelTaskExecution () // all assertion variable captured, cancel counting task
50
52
51
53
assert .Error (t , err )
52
54
assert .Equal (t , "expected error" , err .Error ())
@@ -56,6 +58,7 @@ func TestWaitAllFailFastCase(t *testing.T) {
56
58
// since we pass FailFast, countingTsk and panicTsk should be still running
57
59
assert .Equal (t , asynctask .StateRunning , countingTskState )
58
60
assert .Equal (t , asynctask .StateRunning , panicTskState )
61
+ assert .Equal (t , asynctask .StateFailed , errTskState , "error task should the one failed the waitAll." )
59
62
60
63
// counting task do testing.Logf in another go routine
61
64
// while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
@@ -65,8 +68,7 @@ func TestWaitAllFailFastCase(t *testing.T) {
65
68
66
69
func TestWaitAllErrorCase (t * testing.T ) {
67
70
t .Parallel ()
68
- ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
69
- defer cancelFunc ()
71
+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 3 * time .Second )
70
72
71
73
start := time .Now ()
72
74
countingTsk := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
@@ -75,31 +77,34 @@ func TestWaitAllErrorCase(t *testing.T) {
75
77
result := "something"
76
78
completedTsk := asynctask .NewCompletedTask (& result )
77
79
78
- err := asynctask .WaitAll (ctx , & asynctask. WaitAllOptions { FailFast : false } , countingTsk , errorTsk , panicTsk , completedTsk )
80
+ err := asynctask .WaitAll (ctx , nil , countingTsk , errorTsk , panicTsk , completedTsk )
79
81
countingTskState := countingTsk .State ()
80
82
panicTskState := panicTsk .State ()
83
+ errTskState := errorTsk .State ()
84
+ completedTskState := completedTsk .State ()
81
85
elapsed := time .Since (start )
82
86
83
- cancelFunc () // all assertion variable captured, cancel counting task
87
+ cancelTaskExecution () // all assertion variable captured, cancel counting task
84
88
85
89
assert .Error (t , err )
86
- assert .Equal (t , "expected error" , err .Error ())
90
+ assert .Equal (t , "expected error" , err .Error (), "expecting first error" )
87
91
// should only finish after longest task.
88
92
assert .True (t , elapsed > 10 * 40 * time .Millisecond , fmt .Sprintf ("actually elapsed: %v" , elapsed ))
89
93
90
94
assert .Equal (t , asynctask .StateCompleted , countingTskState , "countingTask should finished" )
95
+ assert .Equal (t , asynctask .StateFailed , errTskState , "error task should failed" )
91
96
assert .Equal (t , asynctask .StateFailed , panicTskState , "panic task should failed" )
97
+ assert .Equal (t , asynctask .StateCompleted , completedTskState , "completed task should finished" )
92
98
93
99
// counting task do testing.Logf in another go routine
94
100
// while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
95
101
// wait minor time for the go routine to finish.
96
102
time .Sleep (1 * time .Millisecond )
97
103
}
98
104
99
- func TestWaitAllCanceled (t * testing.T ) {
105
+ func TestWaitAllFailFastCancelingWait (t * testing.T ) {
100
106
t .Parallel ()
101
- ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
102
- defer cancelFunc ()
107
+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 3 * time .Second )
103
108
104
109
start := time .Now ()
105
110
countingTsk1 := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
@@ -108,25 +113,68 @@ func TestWaitAllCanceled(t *testing.T) {
108
113
result := "something"
109
114
completedTsk := asynctask .NewCompletedTask (& result )
110
115
111
- waitCtx , cancelFunc1 := context .WithTimeout (ctx , 5 * time .Millisecond )
112
- defer cancelFunc1 ()
116
+ waitCtx , cancelWait := context .WithTimeout (ctx , 5 * time .Millisecond )
117
+ defer cancelWait ()
113
118
114
- elapsed := time .Since (start )
115
119
err := asynctask .WaitAll (waitCtx , & asynctask.WaitAllOptions {FailFast : true }, countingTsk1 , countingTsk2 , countingTsk3 , completedTsk )
116
-
117
- cancelFunc () // all assertion variable captured, cancel counting task
120
+ elapsed := time .Since (start )
121
+ countingTsk1State := countingTsk1 .State ()
122
+ countingTsk2State := countingTsk2 .State ()
123
+ countingTsk3State := countingTsk3 .State ()
124
+ completedTskState := completedTsk .State ()
125
+ cancelTaskExecution () // all assertion variable captured, cancel task execution
118
126
119
127
assert .Error (t , err )
120
128
assert .True (t , errors .Is (err , context .DeadlineExceeded ))
121
129
// should return before first task
122
130
assert .True (t , elapsed < 10 * 2 * time .Millisecond )
131
+ assert .Equal (t , countingTsk1State , asynctask .StateRunning )
132
+ assert .Equal (t , countingTsk2State , asynctask .StateRunning )
133
+ assert .Equal (t , countingTsk3State , asynctask .StateRunning )
134
+ assert .Equal (t , completedTskState , asynctask .StateCompleted )
123
135
124
136
// counting task do testing.Logf in another go routine
125
137
// while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
126
138
// wait minor time for the go routine to finish.
127
139
time .Sleep (1 * time .Millisecond )
128
140
}
129
141
142
+ func TestWaitAllCancelingWait (t * testing.T ) {
143
+ t .Parallel ()
144
+
145
+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 4 * time .Millisecond )
146
+
147
+ start := time .Now ()
148
+ rcCtx , rcCancel := context .WithCancel (context .Background ())
149
+ uncontrollableTask := asynctask .Start (ctx , getUncontrollableTask (rcCtx , t ))
150
+ countingTsk1 := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
151
+ countingTsk2 := asynctask .Start (ctx , getCountingTask (10 , "countingPer20ms" , 20 * time .Millisecond ))
152
+ countingTsk3 := asynctask .Start (ctx , getCountingTask (10 , "countingPer2ms" , 2 * time .Millisecond ))
153
+ result := "something"
154
+ completedTsk := asynctask .NewCompletedTask (& result )
155
+
156
+ waitCtx , cancelWait := context .WithTimeout (ctx , 5 * time .Millisecond )
157
+ defer cancelWait ()
158
+
159
+ err := asynctask .WaitAll (waitCtx , nil , countingTsk1 , countingTsk2 , countingTsk3 , completedTsk , uncontrollableTask )
160
+ elapsed := time .Since (start )
161
+ t .Logf ("WaitAll finished, elapsed: %v" , elapsed )
162
+ cancelTaskExecution () // all assertion variable captured, cancel counting task
163
+
164
+ assert .Error (t , err )
165
+ assert .True (t , errors .Is (err , context .DeadlineExceeded ))
166
+ // should return before first task
167
+ assert .True (t , elapsed < 10 * 2 * time .Millisecond )
168
+
169
+ // cancel the remote control context to stop the uncontrollable task, or goleak.VerifyNone will fail.
170
+ rcCancel ()
171
+
172
+ // counting task do testing.Logf in another go routine
173
+ // while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
174
+ // wait minor time for the go routine to finish.
175
+ time .Sleep (50 * time .Millisecond )
176
+ }
177
+
130
178
func TestWaitAllWithNoTasks (t * testing.T ) {
131
179
t .Parallel ()
132
180
ctx , cancelFunc := newTestContextWithTimeout (t , 1 * time .Millisecond )
@@ -135,3 +183,20 @@ func TestWaitAllWithNoTasks(t *testing.T) {
135
183
err := asynctask .WaitAll (ctx , nil )
136
184
assert .NoError (t , err )
137
185
}
186
+
187
+ // getUncontrollableTask return a task that is not honor context, it only hornor the remoteControl context.
188
+ func getUncontrollableTask (rcCtx context.Context , t * testing.T ) asynctask.AsyncFunc [int ] {
189
+ return func (ctx context.Context ) (* int , error ) {
190
+ for {
191
+ select {
192
+ case <- time .After (1 * time .Millisecond ):
193
+ if err := ctx .Err (); err != nil {
194
+ t .Logf ("[UncontrollableTask]: context %s, but not honoring it." , err )
195
+ }
196
+ case <- rcCtx .Done ():
197
+ t .Logf ("[UncontrollableTask]: cancelled by remote control" )
198
+ return nil , rcCtx .Err ()
199
+ }
200
+ }
201
+ }
202
+ }
0 commit comments