@@ -13,8 +13,7 @@ import (
1313
1414func TestWaitAll (t * testing.T ) {
1515 t .Parallel ()
16- ctx , cancelFunc := newTestContextWithTimeout (t , 2 * time .Second )
17- defer cancelFunc ()
16+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 2 * time .Second )
1817
1918 start := time .Now ()
2019 countingTsk1 := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
@@ -26,13 +25,15 @@ func TestWaitAll(t *testing.T) {
2625 err := asynctask .WaitAll (ctx , & asynctask.WaitAllOptions {FailFast : true }, countingTsk1 , countingTsk2 , countingTsk3 , completedTsk )
2726 elapsed := time .Since (start )
2827 assert .NoError (t , err )
28+ cancelTaskExecution ()
29+
2930 // should only finish after longest task.
3031 assert .True (t , elapsed > 10 * 40 * time .Millisecond , fmt .Sprintf ("actually elapsed: %v" , elapsed ))
3132}
3233
3334func TestWaitAllFailFastCase (t * testing.T ) {
3435 t .Parallel ()
35- ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
36+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 3 * time .Second )
3637
3738 start := time .Now ()
3839 countingTsk := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
@@ -44,9 +45,10 @@ func TestWaitAllFailFastCase(t *testing.T) {
4445 err := asynctask .WaitAll (ctx , & asynctask.WaitAllOptions {FailFast : true }, countingTsk , errorTsk , panicTsk , completedTsk )
4546 countingTskState := countingTsk .State ()
4647 panicTskState := countingTsk .State ()
48+ errTskState := errorTsk .State ()
4749 elapsed := time .Since (start )
4850
49- cancelFunc () // all assertion variable captured, cancel counting task
51+ cancelTaskExecution () // all assertion variable captured, cancel counting task
5052
5153 assert .Error (t , err )
5254 assert .Equal (t , "expected error" , err .Error ())
@@ -56,6 +58,7 @@ func TestWaitAllFailFastCase(t *testing.T) {
5658 // since we pass FailFast, countingTsk and panicTsk should be still running
5759 assert .Equal (t , asynctask .StateRunning , countingTskState )
5860 assert .Equal (t , asynctask .StateRunning , panicTskState )
61+ assert .Equal (t , asynctask .StateFailed , errTskState , "error task should the one failed the waitAll." )
5962
6063 // counting task do testing.Logf in another go routine
6164 // 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) {
6568
6669func TestWaitAllErrorCase (t * testing.T ) {
6770 t .Parallel ()
68- ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
69- defer cancelFunc ()
71+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 3 * time .Second )
7072
7173 start := time .Now ()
7274 countingTsk := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
@@ -75,31 +77,34 @@ func TestWaitAllErrorCase(t *testing.T) {
7577 result := "something"
7678 completedTsk := asynctask .NewCompletedTask (& result )
7779
78- err := asynctask .WaitAll (ctx , & asynctask. WaitAllOptions { FailFast : false } , countingTsk , errorTsk , panicTsk , completedTsk )
80+ err := asynctask .WaitAll (ctx , nil , countingTsk , errorTsk , panicTsk , completedTsk )
7981 countingTskState := countingTsk .State ()
8082 panicTskState := panicTsk .State ()
83+ errTskState := errorTsk .State ()
84+ completedTskState := completedTsk .State ()
8185 elapsed := time .Since (start )
8286
83- cancelFunc () // all assertion variable captured, cancel counting task
87+ cancelTaskExecution () // all assertion variable captured, cancel counting task
8488
8589 assert .Error (t , err )
86- assert .Equal (t , "expected error" , err .Error ())
90+ assert .Equal (t , "expected error" , err .Error (), "expecting first error" )
8791 // should only finish after longest task.
8892 assert .True (t , elapsed > 10 * 40 * time .Millisecond , fmt .Sprintf ("actually elapsed: %v" , elapsed ))
8993
9094 assert .Equal (t , asynctask .StateCompleted , countingTskState , "countingTask should finished" )
95+ assert .Equal (t , asynctask .StateFailed , errTskState , "error task should failed" )
9196 assert .Equal (t , asynctask .StateFailed , panicTskState , "panic task should failed" )
97+ assert .Equal (t , asynctask .StateCompleted , completedTskState , "completed task should finished" )
9298
9399 // counting task do testing.Logf in another go routine
94100 // while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
95101 // wait minor time for the go routine to finish.
96102 time .Sleep (1 * time .Millisecond )
97103}
98104
99- func TestWaitAllCanceled (t * testing.T ) {
105+ func TestWaitAllFailFastCancelingWait (t * testing.T ) {
100106 t .Parallel ()
101- ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
102- defer cancelFunc ()
107+ ctx , cancelTaskExecution := newTestContextWithTimeout (t , 3 * time .Second )
103108
104109 start := time .Now ()
105110 countingTsk1 := asynctask .Start (ctx , getCountingTask (10 , "countingPer40ms" , 40 * time .Millisecond ))
@@ -108,25 +113,68 @@ func TestWaitAllCanceled(t *testing.T) {
108113 result := "something"
109114 completedTsk := asynctask .NewCompletedTask (& result )
110115
111- waitCtx , cancelFunc1 := context .WithTimeout (ctx , 5 * time .Millisecond )
112- defer cancelFunc1 ()
116+ waitCtx , cancelWait := context .WithTimeout (ctx , 5 * time .Millisecond )
117+ defer cancelWait ()
113118
114- elapsed := time .Since (start )
115119 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
118126
119127 assert .Error (t , err )
120128 assert .True (t , errors .Is (err , context .DeadlineExceeded ))
121129 // should return before first task
122130 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 )
123135
124136 // counting task do testing.Logf in another go routine
125137 // while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
126138 // wait minor time for the go routine to finish.
127139 time .Sleep (1 * time .Millisecond )
128140}
129141
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+
130178func TestWaitAllWithNoTasks (t * testing.T ) {
131179 t .Parallel ()
132180 ctx , cancelFunc := newTestContextWithTimeout (t , 1 * time .Millisecond )
@@ -135,3 +183,20 @@ func TestWaitAllWithNoTasks(t *testing.T) {
135183 err := asynctask .WaitAll (ctx , nil )
136184 assert .NoError (t , err )
137185}
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