|  | 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) | 
|  | } | 
|  | } |