zrepl/util/optionaldeadline/optionaldeadline_test.go

108 lines
3.1 KiB
Go
Raw Normal View History

package optionaldeadline
2018-07-15 17:36:53 +02:00
import (
"context"
2018-08-25 21:30:25 +02:00
"testing"
"time"
2019-03-22 19:41:12 +01:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zrepl/zrepl/util/chainlock"
"github.com/zrepl/zrepl/util/zreplcircleci"
2018-07-15 17:36:53 +02:00
)
func TestContextWithOptionalDeadline(t *testing.T) {
zreplcircleci.SkipOnCircleCI(t, "test relies on predictably low scheduler latency")
2018-07-15 17:36:53 +02:00
ctx := context.Background()
cctx, enforceDeadline := ContextWithOptionalDeadline(ctx)
begin := time.Now()
var checker struct {
receivedCancellation time.Time
cancellationError error
timeout bool
mtx chainlock.L
}
2018-07-15 17:36:53 +02:00
go func() {
select {
2018-08-25 21:30:25 +02:00
case <-cctx.Done():
defer checker.mtx.Lock().Unlock()
checker.receivedCancellation = time.Now()
checker.cancellationError = cctx.Err()
2018-08-25 21:30:25 +02:00
case <-time.After(600 * time.Millisecond):
defer checker.mtx.Lock().Unlock()
checker.timeout = true
2018-07-15 17:36:53 +02:00
}
}()
defer checker.mtx.Lock().Unlock()
checker.mtx.DropWhile(func() {
time.Sleep(100 * time.Millisecond)
})
if !checker.receivedCancellation.IsZero() {
2018-07-15 17:36:53 +02:00
t.Fatalf("no enforcement means no cancellation")
}
require.Nil(t, cctx.Err(), "no error while not cancelled")
dl, ok := cctx.Deadline()
require.False(t, ok)
require.Zero(t, dl)
2018-08-25 21:30:25 +02:00
enforceDeadline(begin.Add(200 * time.Millisecond))
2018-07-15 17:36:53 +02:00
// second call must be ignored, i.e. we expect the deadline to be at begin+200ms, not begin+400ms
2018-08-25 21:30:25 +02:00
enforceDeadline(begin.Add(400 * time.Millisecond))
2018-07-15 17:36:53 +02:00
checker.mtx.DropWhile(func() {
time.Sleep(300 * time.Millisecond) // 100ms margin for scheduler
})
assert.False(t, checker.timeout, "test timeout")
receivedCancellationAfter := checker.receivedCancellation.Sub(begin)
if receivedCancellationAfter > 250*time.Millisecond {
t.Fatalf("cancellation is beyond acceptable scheduler latency: %s", receivedCancellationAfter)
2018-07-15 17:36:53 +02:00
}
require.Equal(t, context.DeadlineExceeded, checker.cancellationError)
2018-07-15 17:36:53 +02:00
}
func TestContextWithOptionalDeadlineNegativeDeadline(t *testing.T) {
ctx := context.Background()
cctx, enforceDeadline := ContextWithOptionalDeadline(ctx)
2018-08-25 21:30:25 +02:00
enforceDeadline(time.Now().Add(-10 * time.Second))
2018-07-15 17:36:53 +02:00
select {
case <-cctx.Done():
default:
t.FailNow()
}
}
func TestContextWithOptionalDeadlineParentCancellation(t *testing.T) {
zreplcircleci.SkipOnCircleCI(t, "test relies on predictably low scheduler latency")
2018-07-15 17:36:53 +02:00
pctx, cancel := context.WithCancel(context.Background())
cctx, enforceDeadline := ContextWithOptionalDeadline(pctx)
// 0 ms
start := time.Now()
2018-08-25 21:30:25 +02:00
enforceDeadline(start.Add(400 * time.Millisecond))
time.Sleep(100 * time.Millisecond)
cancel() // cancel @ ~100ms
time.Sleep(100 * time.Millisecond) // give 100ms time to propagate cancel
2018-07-15 17:36:53 +02:00
// @ ~200ms
select {
case <-cctx.Done():
assert.True(t, time.Now().Before(start.Add(300*time.Millisecond)))
assert.Equal(t, context.Canceled, cctx.Err())
default:
t.FailNow()
}
}
type testContextKey string
const testContextKeyKey testContextKey = "key"
2018-07-15 17:36:53 +02:00
func TestContextWithOptionalDeadlineValue(t *testing.T) {
pctx := context.WithValue(context.Background(), testContextKeyKey, "value")
2018-07-15 17:36:53 +02:00
cctx, _ := ContextWithOptionalDeadline(pctx)
assert.Equal(t, "value", cctx.Value(testContextKeyKey))
2018-08-25 21:30:25 +02:00
}