(t *testing.T)
| 560 | } |
| 561 | |
| 562 | func TestAsyncCallStateCancellation(t *testing.T) { |
| 563 | ctx, cancel := context.WithCancel(context.Background()) |
| 564 | frame, closeFrame := newTestFrame(t, ctx) |
| 565 | defer closeFrame() |
| 566 | |
| 567 | completions := make(chan int64, 1) |
| 568 | if err := frame.SetCompletions(completions); err != nil { |
| 569 | t.Fatalf("SetCompletions() failed: %v", err) |
| 570 | } |
| 571 | |
| 572 | var exited sync.WaitGroup |
| 573 | exited.Add(1) |
| 574 | blocking := func(ctx context.Context, args ...ref.Val) <-chan ref.Val { |
| 575 | ch := make(chan ref.Val) // never written; goroutine must exit via ctx.Done |
| 576 | go func() { |
| 577 | defer exited.Done() |
| 578 | <-ctx.Done() |
| 579 | }() |
| 580 | return ch |
| 581 | } |
| 582 | res := frame.ComputeResult(1, "fn", "fn_int", blocking, []ref.Val{types.Int(1)}) |
| 583 | if unk, ok := res.(*types.Unknown); ok { |
| 584 | frame.DispatchPendingAsyncCalls(unk.IDs()) |
| 585 | } |
| 586 | if !types.IsUnknown(res) { |
| 587 | t.Fatalf("ComputeResult() = %v, wanted Unknown while pending", res) |
| 588 | } |
| 589 | cancel() |
| 590 | |
| 591 | done := make(chan struct{}) |
| 592 | go func() { exited.Wait(); close(done) }() |
| 593 | select { |
| 594 | case <-done: |
| 595 | case <-time.After(2 * time.Second): |
| 596 | t.Fatal("async impl goroutine did not exit on cancellation") |
| 597 | } |
| 598 | // No completion should have been delivered for the cancelled call. |
| 599 | select { |
| 600 | case callID := <-completions: |
| 601 | t.Errorf("unexpected completion for cancelled call: %d", callID) |
| 602 | default: |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | func TestAsyncTrackerPoolReleaseClearsState(t *testing.T) { |
| 607 | tracker := newAsyncCallStateTracker() |
nothing calls this directly
no test coverage detected