@@ -2,6 +2,7 @@ package asynctask_test
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"testing"
6
7
"time"
7
8
@@ -10,6 +11,7 @@ import (
10
11
)
11
12
12
13
const testContextKey string = "testing"
14
+ const countingTaskDefaultStepLatency time.Duration = 20 * time .Millisecond
13
15
14
16
func newTestContext (t * testing.T ) context.Context {
15
17
return context .WithValue (context .TODO (), testContextKey , t )
@@ -19,18 +21,20 @@ func newTestContextWithTimeout(t *testing.T, timeout time.Duration) (context.Con
19
21
return context .WithTimeout (context .WithValue (context .TODO (), testContextKey , t ), timeout )
20
22
}
21
23
22
- func getCountingTask (countTo int , sleepInterval time.Duration ) asynctask.AsyncFunc [int ] {
24
+ func getCountingTask (countTo int , taskId string , sleepInterval time.Duration ) asynctask.AsyncFunc [int ] {
23
25
return func (ctx context.Context ) (* int , error ) {
24
26
t := ctx .Value (testContextKey ).(* testing.T )
25
27
26
28
result := 0
27
29
for i := 0 ; i < countTo ; i ++ {
28
30
select {
29
31
case <- time .After (sleepInterval ):
30
- t .Logf (" working %d" , i )
32
+ t .Logf ("[%s]: counting %d" , taskId , i )
31
33
result = i
32
34
case <- ctx .Done ():
33
- t .Log ("work canceled" )
35
+ // testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
36
+ // leave minor time buffer before exit test to finish this last logging at least.
37
+ t .Logf ("[%s]: work canceled" , taskId )
34
38
return & result , nil
35
39
}
36
40
}
@@ -43,7 +47,7 @@ func TestEasyGenericCase(t *testing.T) {
43
47
ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
44
48
defer cancelFunc ()
45
49
46
- t1 := asynctask .Start (ctx , getCountingTask (10 , 200 * time . Millisecond ))
50
+ t1 := asynctask .Start (ctx , getCountingTask (10 , "easyTask" , countingTaskDefaultStepLatency ))
47
51
assert .Equal (t , asynctask .StateRunning , t1 .State (), "Task should queued to Running" )
48
52
49
53
rawResult , err := t1 .Result (ctx )
@@ -62,26 +66,27 @@ func TestEasyGenericCase(t *testing.T) {
62
66
assert .NotNil (t , rawResult )
63
67
assert .Equal (t , * rawResult , 9 )
64
68
65
- assert .True (t , elapsed .Microseconds () < 3 , "Second wait should return immediately" )
69
+ // Result should be returned immediately
70
+ assert .True (t , elapsed .Milliseconds () < 1 , fmt .Sprintf ("Second wait should have return immediately: %s" , elapsed ))
66
71
}
67
72
68
73
func TestCancelFuncOnGeneric (t * testing.T ) {
69
74
t .Parallel ()
70
75
ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
71
76
defer cancelFunc ()
72
77
73
- t1 := asynctask .Start (ctx , getCountingTask (10 , 200 * time . Millisecond ))
78
+ t1 := asynctask .Start (ctx , getCountingTask (10 , "cancelTask" , countingTaskDefaultStepLatency ))
74
79
assert .Equal (t , asynctask .StateRunning , t1 .State (), "Task should queued to Running" )
75
80
76
- time .Sleep (time . Second * 1 )
81
+ time .Sleep (countingTaskDefaultStepLatency )
77
82
t1 .Cancel ()
78
83
79
84
_ , err := t1 .Result (ctx )
80
85
assert .Equal (t , asynctask .ErrCanceled , err , "should return reason of error" )
81
86
assert .Equal (t , asynctask .StateCanceled , t1 .State (), "Task should remain in cancel state" )
82
87
83
88
// I can cancel again, and nothing changes
84
- time .Sleep (time . Second * 1 )
89
+ time .Sleep (countingTaskDefaultStepLatency )
85
90
t1 .Cancel ()
86
91
_ , err = t1 .Result (ctx )
87
92
assert .Equal (t , asynctask .ErrCanceled , err , "should return reason of error" )
@@ -101,11 +106,11 @@ func TestConsistentResultAfterCancelGenericTask(t *testing.T) {
101
106
ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
102
107
defer cancelFunc ()
103
108
104
- t1 := asynctask .Start (ctx , getCountingTask (10 , 200 * time . Millisecond ))
105
- t2 := asynctask .Start (ctx , getCountingTask (10 , 200 * time . Millisecond ))
109
+ t1 := asynctask .Start (ctx , getCountingTask (10 , "consistentTask1" , countingTaskDefaultStepLatency ))
110
+ t2 := asynctask .Start (ctx , getCountingTask (10 , "consistentTask2" , countingTaskDefaultStepLatency ))
106
111
assert .Equal (t , asynctask .StateRunning , t1 .State (), "Task should queued to Running" )
107
112
108
- time .Sleep (time . Second * 1 )
113
+ time .Sleep (countingTaskDefaultStepLatency )
109
114
start := time .Now ()
110
115
t1 .Cancel ()
111
116
duration := time .Since (start )
@@ -150,22 +155,24 @@ func TestCrazyCaseGeneric(t *testing.T) {
150
155
ctx , cancelFunc := newTestContextWithTimeout (t , 3 * time .Second )
151
156
defer cancelFunc ()
152
157
153
- numOfTasks := 8000 // if you have --race switch on: limit on 8128 simultaneously alive goroutines is exceeded, dying
158
+ numOfTasks := 100 // if you have --race switch on: limit on 8128 simultaneously alive goroutines is exceeded, dying
154
159
tasks := map [int ]* asynctask.Task [int ]{}
155
160
for i := 0 ; i < numOfTasks ; i ++ {
156
- tasks [i ] = asynctask .Start (ctx , getCountingTask (10 , 200 * time . Millisecond ))
161
+ tasks [i ] = asynctask .Start (ctx , getCountingTask (10 , fmt . Sprintf ( "CrazyTask%d" , i ), countingTaskDefaultStepLatency ))
157
162
}
158
163
159
- time .Sleep (200 * time .Millisecond )
164
+ // sleep 1 step, then cancel task with even number
165
+ time .Sleep (countingTaskDefaultStepLatency )
160
166
for i := 0 ; i < numOfTasks ; i += 2 {
161
167
tasks [i ].Cancel ()
162
168
}
163
169
170
+ time .Sleep (time .Duration (numOfTasks ) * 2 * time .Microsecond )
164
171
for i := 0 ; i < numOfTasks ; i += 1 {
165
172
rawResult , err := tasks [i ].Result (ctx )
166
173
167
174
if i % 2 == 0 {
168
- assert .Equal (t , asynctask .ErrCanceled , err , " should be canceled" )
175
+ assert .Equal (t , asynctask .ErrCanceled , err , fmt . Sprintf ( "task %s should be canceled, but it finished with %+v" , fmt . Sprintf ( "CrazyTask%d" , i ), rawResult ) )
169
176
assert .Equal (t , * rawResult , 0 )
170
177
} else {
171
178
assert .NoError (t , err )
0 commit comments