blob: b7f2625f47799e1d105d810be1f52b153c94c513 [file] [log] [blame]
package combinectx
import (
"context"
"errors"
"testing"
"time"
)
func TestCancel(t *testing.T) {
a, aC := context.WithCancel(context.Background())
b, bC := context.WithCancel(context.Background())
c := Combine(a, b)
if want, got := error(nil), c.Err(); want != got {
t.Fatalf("Newly combined context should return %v, got %v", want, got)
}
if _, ok := c.Deadline(); ok {
t.Errorf("Newly combined context should have no deadline")
}
// Cancel A.
aC()
// Cancels are not synchronous - wait for it to propagate...
<-c.Done()
// ...then cancel B (no-op).
bC()
if c.Err() == nil {
t.Fatalf("After cancel, ctx.Err() should be non-nil")
}
if !errors.Is(c.Err(), a.Err()) {
t.Errorf("After cancel, ctx.Err() should be a.Err()")
}
if !errors.Is(c.Err(), c.Err()) {
t.Errorf("After cancel, ctx.Err() should be ctx.Err()")
}
if !errors.Is(c.Err(), context.Canceled) {
t.Errorf("After cancel, ctx.Err() should be context.Canceled")
}
if !errors.Is(c.Err(), &Error{}) {
t.Errorf("After cancel, ctx.Err() should be a Error pointer")
}
cerr := &Error{}
if !errors.As(c.Err(), &cerr) {
t.Fatalf("After cancel, ctx.Err() should be usable as *Error")
}
if !cerr.First() {
t.Errorf("ctx.Err().First() should be true")
}
if cerr.Second() {
t.Errorf("ctx.Err().Second() should be false")
}
if want, got := a.Err(), cerr.Unwrap(); want != got {
t.Errorf("ctx.Err().Unwrap() should be %v, got %v", want, got)
}
}
func TestDeadline(t *testing.T) {
now := time.Now()
aD := now.Add(100 * time.Millisecond)
bD := now.Add(10 * time.Millisecond)
a, aC := context.WithDeadline(context.Background(), aD)
b, bC := context.WithDeadline(context.Background(), bD)
defer aC()
defer bC()
c := Combine(a, b)
if want, got := error(nil), c.Err(); want != got {
t.Fatalf("Newly combined context should return %v, got %v", want, got)
}
if d, ok := c.Deadline(); !ok || !d.Equal(bD) {
t.Errorf("Newly combined context should have deadline %v, got %v", bD, d)
}
<-c.Done()
if c.Err() == nil {
t.Fatalf("After deadline, ctx.Err() should be non-nil")
}
if !errors.Is(c.Err(), b.Err()) {
t.Errorf("After deadline, ctx.Err() should be b.Err()")
}
if !errors.Is(c.Err(), context.DeadlineExceeded) {
t.Errorf("After cancel, ctx.Err() should be context.DeadlineExceeded")
}
if !errors.Is(c.Err(), &Error{}) {
t.Errorf("After cancel, ctx.Err() should be a Error pointer")
}
cerr := &Error{}
if !errors.As(c.Err(), &cerr) {
t.Fatalf("After cancel, ctx.Err() should be usable as *Error")
}
if cerr.First() {
t.Errorf("ctx.Err().First() should be false")
}
if !cerr.Second() {
t.Errorf("ctx.Err().Second() should be true")
}
if want, got := b.Err(), cerr.Unwrap(); want != got {
t.Errorf("ctx.Err().Unwrap() should be %v, got %v", want, got)
}
}