shad-go/dupcall/dupcall_test.go

166 lines
2.9 KiB
Go
Raw Normal View History

2021-03-18 14:39:31 +00:00
package dupcall
import (
"context"
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
)
func TestCall_Simple(t *testing.T) {
defer goleak.VerifyNone(t)
called := 0
var call Call
result, err := call.Do(context.Background(), func(ctx context.Context) (interface{}, error) {
called++
return "ok", nil
})
require.NoError(t, err)
require.Equal(t, result, "ok")
require.Equal(t, called, 1)
errFailed := errors.New("failed")
result, err = call.Do(context.Background(), func(ctx context.Context) (interface{}, error) {
called++
return nil, errFailed
})
require.Equal(t, err, errFailed)
require.Nil(t, result)
require.Equal(t, called, 2)
}
func TestCall_Dedup(t *testing.T) {
defer goleak.VerifyNone(t)
called := 0
cb := func(ctx context.Context) (interface{}, error) {
time.Sleep(time.Millisecond * 100)
called++
return "ok", nil
}
var call Call
for i := 0; i < 10; i++ {
2021-03-18 14:59:22 +00:00
go func() {
_, _ = call.Do(context.Background(), cb)
}()
2021-03-18 14:39:31 +00:00
}
result, err := call.Do(context.Background(), cb)
require.NoError(t, err)
require.Equal(t, result, "ok")
require.Equal(t, called, 1)
}
func TestCall_HalfCancel(t *testing.T) {
defer goleak.VerifyNone(t)
called := 0
cb := func(ctx context.Context) (interface{}, error) {
time.Sleep(time.Millisecond * 100)
called++
return "ok", nil
}
var call Call
for i := 0; i < 10; i++ {
2021-03-18 14:59:22 +00:00
go func() {
_, _ = call.Do(context.Background(), cb)
}()
2021-03-18 14:39:31 +00:00
}
for i := 0; i < 10; i++ {
ctx, cancel := context.WithCancel(context.Background())
2021-03-18 14:59:22 +00:00
go func() {
_, _ = call.Do(ctx, cb)
}()
2021-03-18 14:39:31 +00:00
time.Sleep(time.Millisecond)
cancel()
}
result, err := call.Do(context.Background(), cb)
require.NoError(t, err)
require.Equal(t, result, "ok")
require.Equal(t, called, 1)
}
func TestCall_FullCancel(t *testing.T) {
defer goleak.VerifyNone(t)
2021-03-18 15:04:38 +00:00
cancelled := make(chan struct{}, 1)
2021-03-18 14:39:31 +00:00
cb := func(ctx context.Context) (interface{}, error) {
<-ctx.Done()
2021-03-18 15:04:38 +00:00
select {
case cancelled <- struct{}{}:
default:
}
2021-03-18 14:39:31 +00:00
return nil, nil
}
var call Call
for i := 0; i < 10; i++ {
ctx, cancel := context.WithCancel(context.Background())
2021-03-18 14:59:22 +00:00
go func() {
_, _ = call.Do(ctx, cb)
}()
2021-03-18 14:39:31 +00:00
time.Sleep(time.Millisecond)
cancel()
}
select {
case <-cancelled:
return
case <-time.After(time.Millisecond * 100):
t.Errorf("duplicate call not cancelled after 100ms")
}
}
func TestCall_NonBlockingCancel(t *testing.T) {
defer goleak.VerifyNone(t)
var call Call
cb := func(ctx context.Context) (interface{}, error) {
time.Sleep(time.Millisecond * 100)
return nil, nil
}
cancelled := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
go func() {
_, err := call.Do(ctx, cb)
assert.Error(t, err)
close(cancelled)
}()
time.Sleep(10 * time.Millisecond)
cancel()
select {
case <-cancelled:
return
case <-time.After(50 * time.Millisecond):
t.Errorf("cancelled call blocked for more that 50ms")
}
}