package ctxext import ( "math/rand" "testing" "time" context "golang.org/x/net/context" ) func TestWithParentsSingle(t *testing.T) { ctx1, cancel := context.WithCancel(context.Background()) ctx2 := WithParents(ctx1) select { case <-ctx2.Done(): t.Fatal("ended too early") case <-time.After(time.Millisecond): } cancel() select { case <-ctx2.Done(): case <-time.After(time.Millisecond): t.Error("should've cancelled it") } if ctx2.Err() != ctx1.Err() { t.Error("errors should match") } } func TestWithParentsDeadline(t *testing.T) { ctx1, _ := context.WithCancel(context.Background()) ctx2, _ := context.WithTimeout(context.Background(), time.Second) ctx3, _ := context.WithTimeout(context.Background(), time.Second*2) ctx := WithParents(ctx1) d, ok := ctx.Deadline() if ok { t.Error("ctx should have no deadline") } ctx = WithParents(ctx1, ctx2, ctx3) d, ok = ctx.Deadline() d2, ok2 := ctx2.Deadline() if !ok { t.Error("ctx should have deadline") } else if !ok2 { t.Error("ctx2 should have deadline") } else if !d.Equal(d2) { t.Error("ctx should have ctx2 deadline") } } func SubtestWithParentsMany(t *testing.T, n int) { ctxs := make([]context.Context, n) cancels := make([]context.CancelFunc, n) for i := 0; i < n; i++ { if i == 0 { // first must be new. ctxs[i], cancels[i] = context.WithCancel(context.Background()) continue } r := rand.Intn(i) // select a previous context switch rand.Intn(6) { case 0: // same as old ctxs[i], cancels[i] = ctxs[r], cancels[r] case 1: // derive from another ctxs[i], cancels[i] = context.WithCancel(ctxs[r]) case 2: // deadline t := (time.Second) * time.Duration(r+2) // +2 so we dont run into 0 or timing bugs ctxs[i], cancels[i] = context.WithTimeout(ctxs[r], t) default: // new context ctxs[i], cancels[i] = context.WithCancel(context.Background()) } } ctx := WithParents(ctxs...) // test deadline is earliest. d1 := earliestDeadline(ctxs) d2, ok := ctx.Deadline() switch { case d1 == nil && ok: t.Error("nil, should not have deadline") case d1 != nil && !ok: t.Error("not nil, should have deadline") case d1 != nil && ok && !d1.Equal(d2): t.Error("should find same deadline") } if ok { t.Logf("deadline - now: %s", d2.Sub(time.Now())) } select { case <-ctx.Done(): t.Fatal("ended too early") case <-time.After(time.Millisecond): } // cancel just one r := rand.Intn(len(cancels)) cancels[r]() select { case <-ctx.Done(): case <-time.After(time.Millisecond): t.Error("any should've cancelled it") } if ctx.Err() != ctxs[r].Err() { t.Error("errors should match") } } func TestWithParentsMany(t *testing.T) { n := 100 for i := 1; i < n; i++ { SubtestWithParentsMany(t, i) } }