mediocre-go-lib/mrun/mrun_test.go

189 lines
4.4 KiB
Go

package mrun
import (
"errors"
. "testing"
"time"
"github.com/mediocregopher/mediocre-go-lib/mctx"
"github.com/mediocregopher/mediocre-go-lib/mtest/massert"
)
func TestThreadWait(t *T) {
testErr := errors.New("test error")
cancelCh := func(t time.Duration) <-chan struct{} {
tCtx, _ := mctx.WithTimeout(mctx.New(), t*2)
return tCtx.Done()
}
wait := func(ctx mctx.Context, shouldTake time.Duration) error {
start := time.Now()
err := Wait(ctx, cancelCh(shouldTake*2))
if took := time.Since(start); took < shouldTake || took > shouldTake*4/3 {
t.Fatalf("wait took %v, should have taken %v", took, shouldTake)
}
return err
}
t.Run("noChildren", func(t *T) {
t.Run("noBlock", func(t *T) {
t.Run("noErr", func(t *T) {
ctx := mctx.New()
Thread(ctx, func(mctx.Context) error { return nil })
if err := Wait(ctx, nil); err != nil {
t.Fatal(err)
}
})
t.Run("err", func(t *T) {
ctx := mctx.New()
Thread(ctx, func(mctx.Context) error { return testErr })
if err := Wait(ctx, nil); err != testErr {
t.Fatalf("should have got test error, got: %v", err)
}
})
})
t.Run("block", func(t *T) {
t.Run("noErr", func(t *T) {
ctx := mctx.New()
Thread(ctx, func(mctx.Context) error {
time.Sleep(1 * time.Second)
return nil
})
if err := wait(ctx, 1*time.Second); err != nil {
t.Fatal(err)
}
})
t.Run("err", func(t *T) {
ctx := mctx.New()
Thread(ctx, func(mctx.Context) error {
time.Sleep(1 * time.Second)
return testErr
})
if err := wait(ctx, 1*time.Second); err != testErr {
t.Fatalf("should have got test error, got: %v", err)
}
})
t.Run("canceled", func(t *T) {
ctx := mctx.New()
Thread(ctx, func(mctx.Context) error {
time.Sleep(5 * time.Second)
return testErr
})
if err := Wait(ctx, cancelCh(500*time.Millisecond)); err != ErrDone {
t.Fatalf("should have got ErrDone, got: %v", err)
}
})
})
})
ctxWithChild := func() (mctx.Context, mctx.Context) {
ctx := mctx.New()
return ctx, mctx.ChildOf(ctx, "child")
}
t.Run("children", func(t *T) {
t.Run("noBlock", func(t *T) {
t.Run("noErr", func(t *T) {
ctx, childCtx := ctxWithChild()
Thread(childCtx, func(mctx.Context) error { return nil })
if err := Wait(ctx, nil); err != nil {
t.Fatal(err)
}
})
t.Run("err", func(t *T) {
ctx, childCtx := ctxWithChild()
Thread(childCtx, func(mctx.Context) error { return testErr })
if err := Wait(ctx, nil); err != testErr {
t.Fatalf("should have got test error, got: %v", err)
}
})
})
t.Run("block", func(t *T) {
t.Run("noErr", func(t *T) {
ctx, childCtx := ctxWithChild()
Thread(childCtx, func(mctx.Context) error {
time.Sleep(1 * time.Second)
return nil
})
if err := wait(ctx, 1*time.Second); err != nil {
t.Fatal(err)
}
})
t.Run("err", func(t *T) {
ctx, childCtx := ctxWithChild()
Thread(childCtx, func(mctx.Context) error {
time.Sleep(1 * time.Second)
return testErr
})
if err := wait(ctx, 1*time.Second); err != testErr {
t.Fatalf("should have got test error, got: %v", err)
}
})
t.Run("canceled", func(t *T) {
ctx, childCtx := ctxWithChild()
Thread(childCtx, func(mctx.Context) error {
time.Sleep(5 * time.Second)
return testErr
})
if err := Wait(ctx, cancelCh(500*time.Millisecond)); err != ErrDone {
t.Fatalf("should have got ErrDone, got: %v", err)
}
})
})
})
}
func TestHooks(t *T) {
ch := make(chan int, 10)
ctx := mctx.New()
ctxChild := mctx.ChildOf(ctx, "child")
mkHook := func(i int) Hook {
return func(mctx.Context) error {
ch <- i
return nil
}
}
RegisterHook(ctx, 0, mkHook(0))
RegisterHook(ctxChild, 0, mkHook(1))
RegisterHook(ctx, 0, mkHook(2))
bogusErr := errors.New("bogus error")
RegisterHook(ctxChild, 0, func(mctx.Context) error { return bogusErr })
RegisterHook(ctx, 0, mkHook(3))
RegisterHook(ctx, 0, mkHook(4))
massert.Fatal(t, massert.All(
massert.Equal(bogusErr, TriggerHooks(ctx, 0)),
massert.Equal(0, <-ch),
massert.Equal(1, <-ch),
massert.Equal(2, <-ch),
))
// after the error the 3 and 4 Hooks should still be registered, but not
// called yet.
select {
case <-ch:
t.Fatal("Hooks should not have been called yet")
default:
}
massert.Fatal(t, massert.All(
massert.Nil(TriggerHooks(ctx, 0)),
massert.Equal(3, <-ch),
massert.Equal(4, <-ch),
))
}