mirror of https://github.com/coder/coder.git
204 lines
5.6 KiB
Go
204 lines
5.6 KiB
Go
package harness_test
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"sort"
|
|
"strconv"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/coder/coder/v2/scaletest/harness"
|
|
)
|
|
|
|
//nolint:paralleltest // this tests uses timings to determine if it's working
|
|
func Test_LinearExecutionStrategy(t *testing.T) {
|
|
var (
|
|
lastSeenI int64 = -1
|
|
count int64
|
|
)
|
|
runs, fns := strategyTestData(100, func(_ context.Context, i int, _ io.Writer) error {
|
|
atomic.AddInt64(&count, 1)
|
|
swapped := atomic.CompareAndSwapInt64(&lastSeenI, int64(i-1), int64(i))
|
|
assert.True(t, swapped)
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
if i%2 == 0 {
|
|
return xerrors.New("error")
|
|
}
|
|
return nil
|
|
})
|
|
|
|
strategy := harness.LinearExecutionStrategy{}
|
|
runErrs, err := strategy.Run(context.Background(), fns)
|
|
require.NoError(t, err)
|
|
require.Len(t, runErrs, 50)
|
|
require.EqualValues(t, 100, atomic.LoadInt64(&count))
|
|
|
|
lastStartTime := time.Time{}
|
|
for _, run := range runs {
|
|
startTime := run.Result().StartedAt
|
|
require.True(t, startTime.After(lastStartTime))
|
|
lastStartTime = startTime
|
|
}
|
|
}
|
|
|
|
//nolint:paralleltest // this tests uses timings to determine if it's working
|
|
func Test_ConcurrentExecutionStrategy(t *testing.T) {
|
|
runs, fns := strategyTestData(10, func(_ context.Context, i int, _ io.Writer) error {
|
|
time.Sleep(1 * time.Second)
|
|
if i%2 == 0 {
|
|
return xerrors.New("error")
|
|
}
|
|
return nil
|
|
})
|
|
strategy := harness.ConcurrentExecutionStrategy{}
|
|
|
|
startTime := time.Now()
|
|
runErrs, err := strategy.Run(context.Background(), fns)
|
|
require.NoError(t, err)
|
|
require.Len(t, runErrs, 5)
|
|
|
|
// Should've taken at least 900ms to run but less than 5 seconds.
|
|
require.True(t, time.Since(startTime) > 900*time.Millisecond)
|
|
require.True(t, time.Since(startTime) < 5*time.Second)
|
|
|
|
// All tests should've started within 500 ms of the start time.
|
|
endTime := startTime.Add(500 * time.Millisecond)
|
|
for _, run := range runs {
|
|
runStartTime := run.Result().StartedAt
|
|
require.WithinRange(t, runStartTime, startTime, endTime)
|
|
}
|
|
}
|
|
|
|
//nolint:paralleltest // this tests uses timings to determine if it's working
|
|
func Test_ParallelExecutionStrategy(t *testing.T) {
|
|
runs, fns := strategyTestData(10, func(_ context.Context, i int, _ io.Writer) error {
|
|
time.Sleep(1 * time.Second)
|
|
if i%2 == 0 {
|
|
return xerrors.New("error")
|
|
}
|
|
return nil
|
|
})
|
|
strategy := harness.ParallelExecutionStrategy{
|
|
Limit: 5,
|
|
}
|
|
|
|
startTime := time.Now()
|
|
time.Sleep(time.Millisecond)
|
|
runErrs, err := strategy.Run(context.Background(), fns)
|
|
require.NoError(t, err)
|
|
require.Len(t, runErrs, 5)
|
|
|
|
// Should've taken at least 1900ms to run but less than 8 seconds.
|
|
require.True(t, time.Since(startTime) > 1900*time.Millisecond)
|
|
require.True(t, time.Since(startTime) < 8*time.Second)
|
|
|
|
// Any five of the tests should've started within 500 ms of the start time.
|
|
endTime := startTime.Add(500 * time.Millisecond)
|
|
withinRange := 0
|
|
for _, run := range runs {
|
|
runStartTime := run.Result().StartedAt
|
|
if runStartTime.After(startTime) && runStartTime.Before(endTime) {
|
|
withinRange++
|
|
}
|
|
}
|
|
require.Equal(t, 5, withinRange)
|
|
|
|
// The other 5 tests should've started between 900ms and 1.5s after the
|
|
// start time.
|
|
startTime = startTime.Add(900 * time.Millisecond)
|
|
endTime = startTime.Add(600 * time.Millisecond)
|
|
withinRange = 0
|
|
for _, run := range runs {
|
|
runStartTime := run.Result().StartedAt
|
|
if runStartTime.After(startTime) && runStartTime.Before(endTime) {
|
|
withinRange++
|
|
}
|
|
}
|
|
require.Equal(t, 5, withinRange)
|
|
}
|
|
|
|
//nolint:paralleltest // this tests uses timings to determine if it's working
|
|
func Test_TimeoutExecutionStrategy(t *testing.T) {
|
|
runs, fns := strategyTestData(1, func(ctx context.Context, _ int, _ io.Writer) error {
|
|
ticker := time.NewTicker(500 * time.Millisecond)
|
|
defer ticker.Stop()
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil
|
|
case <-ticker.C:
|
|
return xerrors.New("context wasn't canceled")
|
|
}
|
|
})
|
|
strategy := harness.TimeoutExecutionStrategyWrapper{
|
|
Timeout: 100 * time.Millisecond,
|
|
Inner: harness.LinearExecutionStrategy{},
|
|
}
|
|
|
|
runErrs, err := strategy.Run(context.Background(), fns)
|
|
require.NoError(t, err)
|
|
require.Len(t, runErrs, 0)
|
|
|
|
for _, run := range runs {
|
|
require.NoError(t, run.Result().Error)
|
|
}
|
|
}
|
|
|
|
//nolint:paralleltest // this tests uses timings to determine if it's working
|
|
func Test_ShuffleExecutionStrategyWrapper(t *testing.T) {
|
|
runs, fns := strategyTestData(100000, func(_ context.Context, i int, _ io.Writer) error {
|
|
// t.Logf("run %d", i)
|
|
return nil
|
|
})
|
|
strategy := harness.ShuffleExecutionStrategyWrapper{
|
|
Inner: harness.LinearExecutionStrategy{},
|
|
}
|
|
|
|
runErrs, err := strategy.Run(context.Background(), fns)
|
|
require.NoError(t, err)
|
|
require.Len(t, runErrs, 0)
|
|
|
|
// Ensure not in order by sorting the start time of each run.
|
|
unsortedTimes := make([]time.Time, len(runs))
|
|
for i, run := range runs {
|
|
unsortedTimes[i] = run.Result().StartedAt
|
|
}
|
|
|
|
sortedTimes := make([]time.Time, len(runs))
|
|
copy(sortedTimes, unsortedTimes)
|
|
sort.Slice(sortedTimes, func(i, j int) bool {
|
|
return sortedTimes[i].Before(sortedTimes[j])
|
|
})
|
|
|
|
require.NotEqual(t, unsortedTimes, sortedTimes)
|
|
}
|
|
|
|
func strategyTestData(count int, runFn func(ctx context.Context, i int, logs io.Writer) error) ([]*harness.TestRun, []harness.TestFn) {
|
|
var (
|
|
runs = make([]*harness.TestRun, count)
|
|
fns = make([]harness.TestFn, count)
|
|
)
|
|
for i := 0; i < count; i++ {
|
|
i := i
|
|
|
|
runs[i] = harness.NewTestRun("test", strconv.Itoa(i), testFns{
|
|
RunFn: func(ctx context.Context, id string, logs io.Writer) error {
|
|
if runFn != nil {
|
|
return runFn(ctx, i, logs)
|
|
}
|
|
return nil
|
|
},
|
|
})
|
|
fns[i] = runs[i].Run
|
|
}
|
|
|
|
return runs, fns
|
|
}
|