зеркало из https://github.com/Azure/go-asynctask.git
203 строки
8.1 KiB
Go
203 строки
8.1 KiB
Go
package asynctask_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/Azure/go-asynctask"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestWaitAll(t *testing.T) {
|
|
t.Parallel()
|
|
ctx, cancelTaskExecution := newTestContextWithTimeout(t, 2*time.Second)
|
|
|
|
start := time.Now()
|
|
countingTsk1 := asynctask.Start(ctx, getCountingTask(10, "countingPer40ms", 40*time.Millisecond))
|
|
countingTsk2 := asynctask.Start(ctx, getCountingTask(10, "countingPer20ms", 20*time.Millisecond))
|
|
countingTsk3 := asynctask.Start(ctx, getCountingTask(10, "countingPer2ms", 2*time.Millisecond))
|
|
result := "something"
|
|
completedTsk := asynctask.NewCompletedTask(&result)
|
|
|
|
err := asynctask.WaitAll(ctx, &asynctask.WaitAllOptions{FailFast: true}, countingTsk1, countingTsk2, countingTsk3, completedTsk)
|
|
elapsed := time.Since(start)
|
|
assert.NoError(t, err)
|
|
cancelTaskExecution()
|
|
|
|
// should only finish after longest task.
|
|
assert.True(t, elapsed > 10*40*time.Millisecond, fmt.Sprintf("actually elapsed: %v", elapsed))
|
|
}
|
|
|
|
func TestWaitAllFailFastCase(t *testing.T) {
|
|
t.Parallel()
|
|
ctx, cancelTaskExecution := newTestContextWithTimeout(t, 3*time.Second)
|
|
|
|
start := time.Now()
|
|
countingTsk := asynctask.Start(ctx, getCountingTask(10, "countingPer40ms", 40*time.Millisecond))
|
|
errorTsk := asynctask.Start(ctx, getErrorTask("expected error", 10*time.Millisecond))
|
|
panicTsk := asynctask.Start(ctx, getPanicTask(20*time.Millisecond))
|
|
result := "something"
|
|
completedTsk := asynctask.NewCompletedTask(&result)
|
|
|
|
err := asynctask.WaitAll(ctx, &asynctask.WaitAllOptions{FailFast: true}, countingTsk, errorTsk, panicTsk, completedTsk)
|
|
countingTskState := countingTsk.State()
|
|
panicTskState := countingTsk.State()
|
|
errTskState := errorTsk.State()
|
|
elapsed := time.Since(start)
|
|
|
|
cancelTaskExecution() // all assertion variable captured, cancel counting task
|
|
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "expected error", err.Error())
|
|
// should fail before we finish panic task
|
|
assert.True(t, elapsed.Milliseconds() < 20)
|
|
|
|
// since we pass FailFast, countingTsk and panicTsk should be still running
|
|
assert.Equal(t, asynctask.StateRunning, countingTskState)
|
|
assert.Equal(t, asynctask.StateRunning, panicTskState)
|
|
assert.Equal(t, asynctask.StateFailed, errTskState, "error task should the one failed the waitAll.")
|
|
|
|
// counting task do testing.Logf in another go routine
|
|
// while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
|
|
// wait minor time for the go routine to finish.
|
|
time.Sleep(1 * time.Millisecond)
|
|
}
|
|
|
|
func TestWaitAllErrorCase(t *testing.T) {
|
|
t.Parallel()
|
|
ctx, cancelTaskExecution := newTestContextWithTimeout(t, 3*time.Second)
|
|
|
|
start := time.Now()
|
|
countingTsk := asynctask.Start(ctx, getCountingTask(10, "countingPer40ms", 40*time.Millisecond))
|
|
errorTsk := asynctask.Start(ctx, getErrorTask("expected error", 10*time.Millisecond))
|
|
panicTsk := asynctask.Start(ctx, getPanicTask(20*time.Millisecond))
|
|
result := "something"
|
|
completedTsk := asynctask.NewCompletedTask(&result)
|
|
|
|
err := asynctask.WaitAll(ctx, nil, countingTsk, errorTsk, panicTsk, completedTsk)
|
|
countingTskState := countingTsk.State()
|
|
panicTskState := panicTsk.State()
|
|
errTskState := errorTsk.State()
|
|
completedTskState := completedTsk.State()
|
|
elapsed := time.Since(start)
|
|
|
|
cancelTaskExecution() // all assertion variable captured, cancel counting task
|
|
|
|
assert.Error(t, err)
|
|
assert.Equal(t, "expected error", err.Error(), "expecting first error")
|
|
// should only finish after longest task.
|
|
assert.True(t, elapsed > 10*40*time.Millisecond, fmt.Sprintf("actually elapsed: %v", elapsed))
|
|
|
|
assert.Equal(t, asynctask.StateCompleted, countingTskState, "countingTask should finished")
|
|
assert.Equal(t, asynctask.StateFailed, errTskState, "error task should failed")
|
|
assert.Equal(t, asynctask.StateFailed, panicTskState, "panic task should failed")
|
|
assert.Equal(t, asynctask.StateCompleted, completedTskState, "completed task should finished")
|
|
|
|
// counting task do testing.Logf in another go routine
|
|
// while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
|
|
// wait minor time for the go routine to finish.
|
|
time.Sleep(1 * time.Millisecond)
|
|
}
|
|
|
|
func TestWaitAllFailFastCancelingWait(t *testing.T) {
|
|
t.Parallel()
|
|
ctx, cancelTaskExecution := newTestContextWithTimeout(t, 3*time.Second)
|
|
|
|
start := time.Now()
|
|
countingTsk1 := asynctask.Start(ctx, getCountingTask(10, "countingPer40ms", 40*time.Millisecond))
|
|
countingTsk2 := asynctask.Start(ctx, getCountingTask(10, "countingPer20ms", 20*time.Millisecond))
|
|
countingTsk3 := asynctask.Start(ctx, getCountingTask(10, "countingPer2ms", 2*time.Millisecond))
|
|
result := "something"
|
|
completedTsk := asynctask.NewCompletedTask(&result)
|
|
|
|
waitCtx, cancelWait := context.WithTimeout(ctx, 5*time.Millisecond)
|
|
defer cancelWait()
|
|
|
|
err := asynctask.WaitAll(waitCtx, &asynctask.WaitAllOptions{FailFast: true}, countingTsk1, countingTsk2, countingTsk3, completedTsk)
|
|
elapsed := time.Since(start)
|
|
countingTsk1State := countingTsk1.State()
|
|
countingTsk2State := countingTsk2.State()
|
|
countingTsk3State := countingTsk3.State()
|
|
completedTskState := completedTsk.State()
|
|
cancelTaskExecution() // all assertion variable captured, cancel task execution
|
|
|
|
assert.Error(t, err)
|
|
assert.True(t, errors.Is(err, context.DeadlineExceeded))
|
|
// should return before first task
|
|
assert.True(t, elapsed < 10*2*time.Millisecond)
|
|
assert.Equal(t, countingTsk1State, asynctask.StateRunning)
|
|
assert.Equal(t, countingTsk2State, asynctask.StateRunning)
|
|
assert.Equal(t, countingTsk3State, asynctask.StateRunning)
|
|
assert.Equal(t, completedTskState, asynctask.StateCompleted)
|
|
|
|
// counting task do testing.Logf in another go routine
|
|
// while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
|
|
// wait minor time for the go routine to finish.
|
|
time.Sleep(1 * time.Millisecond)
|
|
}
|
|
|
|
func TestWaitAllCancelingWait(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, cancelTaskExecution := newTestContextWithTimeout(t, 4*time.Millisecond)
|
|
|
|
start := time.Now()
|
|
rcCtx, rcCancel := context.WithCancel(context.Background())
|
|
uncontrollableTask := asynctask.Start(ctx, getUncontrollableTask(rcCtx, t))
|
|
countingTsk1 := asynctask.Start(ctx, getCountingTask(10, "countingPer40ms", 40*time.Millisecond))
|
|
countingTsk2 := asynctask.Start(ctx, getCountingTask(10, "countingPer20ms", 20*time.Millisecond))
|
|
countingTsk3 := asynctask.Start(ctx, getCountingTask(10, "countingPer2ms", 2*time.Millisecond))
|
|
result := "something"
|
|
completedTsk := asynctask.NewCompletedTask(&result)
|
|
|
|
waitCtx, cancelWait := context.WithTimeout(ctx, 5*time.Millisecond)
|
|
defer cancelWait()
|
|
|
|
err := asynctask.WaitAll(waitCtx, nil, countingTsk1, countingTsk2, countingTsk3, completedTsk, uncontrollableTask)
|
|
elapsed := time.Since(start)
|
|
t.Logf("WaitAll finished, elapsed: %v", elapsed)
|
|
cancelTaskExecution() // all assertion variable captured, cancel counting task
|
|
|
|
assert.Error(t, err)
|
|
assert.True(t, errors.Is(err, context.DeadlineExceeded))
|
|
// should return before first task
|
|
assert.True(t, elapsed < 10*2*time.Millisecond)
|
|
|
|
// cancel the remote control context to stop the uncontrollable task, or goleak.VerifyNone will fail.
|
|
rcCancel()
|
|
|
|
// counting task do testing.Logf in another go routine
|
|
// while testing.Logf would cause DataRace error when test is already finished: https://github.com/golang/go/issues/40343
|
|
// wait minor time for the go routine to finish.
|
|
time.Sleep(50 * time.Millisecond)
|
|
}
|
|
|
|
func TestWaitAllWithNoTasks(t *testing.T) {
|
|
t.Parallel()
|
|
ctx, cancelFunc := newTestContextWithTimeout(t, 1*time.Millisecond)
|
|
defer cancelFunc()
|
|
|
|
err := asynctask.WaitAll(ctx, nil)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// getUncontrollableTask return a task that is not honor context, it only hornor the remoteControl context.
|
|
func getUncontrollableTask(rcCtx context.Context, t *testing.T) asynctask.AsyncFunc[int] {
|
|
return func(ctx context.Context) (int, error) {
|
|
for {
|
|
select {
|
|
case <-time.After(1 * time.Millisecond):
|
|
if err := ctx.Err(); err != nil {
|
|
t.Logf("[UncontrollableTask]: context %s, but not honoring it.", err)
|
|
}
|
|
case <-rcCtx.Done():
|
|
t.Logf("[UncontrollableTask]: cancelled by remote control")
|
|
return 0, rcCtx.Err()
|
|
}
|
|
}
|
|
}
|
|
}
|