Exemple #1
0
// Ensure that the mock's Ticker can be stopped.
func TestMock_Ticker_Stop(t *testing.T) {
	var n int32
	clock := clock.NewMock()

	// Create a channel to increment every second.
	ticker := clock.Ticker(1 * time.Second)
	go func() {
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	gosched()

	// Move clock forward.
	clock.Add(5 * time.Second)
	if atomic.LoadInt32(&n) != 5 {
		t.Fatalf("expected 5, got: %d", n)
	}

	ticker.Stop()

	// Move clock forward again.
	clock.Add(5 * time.Second)
	if atomic.LoadInt32(&n) != 5 {
		t.Fatalf("still expected 5, got: %d", n)
	}
}
Exemple #2
0
// Ensure that multiple tickers can be used together.
func TestMock_Ticker_Multi(t *testing.T) {
	var n int32
	clock := clock.NewMock()

	go func() {
		a := clock.Ticker(1 * time.Microsecond)
		b := clock.Ticker(3 * time.Microsecond)

		for {
			select {
			case <-a.C:
				atomic.AddInt32(&n, 1)
			case <-b.C:
				atomic.AddInt32(&n, 100)
			}
		}
	}()
	gosched()

	// Move clock forward.
	clock.Add(10 * time.Microsecond)
	gosched()
	if atomic.LoadInt32(&n) != 310 {
		t.Fatalf("unexpected: %d", n)
	}
}
func TestBreakerEvents(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	events := cb.Subscribe()

	cb.Trip()
	if e := <-events; e != BreakerTripped {
		t.Fatalf("expected to receive a trip event, got %d", e)
	}

	c.Add(cb.nextBackOff + 1)
	cb.Ready()
	if e := <-events; e != BreakerReady {
		t.Fatalf("expected to receive a breaker ready event, got %d", e)
	}

	cb.Reset()
	if e := <-events; e != BreakerReset {
		t.Fatalf("expected to receive a reset event, got %d", e)
	}

	cb.Fail()
	if e := <-events; e != BreakerFail {
		t.Fatalf("expected to receive a fail event, got %d", e)
	}
}
Exemple #4
0
func ExampleMock_Ticker() {
	// Create a new mock clock.
	clock := clock.NewMock()
	count := 0

	// Increment count every mock second.
	go func() {
		ticker := clock.Ticker(1 * time.Second)
		for {
			<-ticker.C
			count++
		}
	}()
	runtime.Gosched()

	// Move the clock forward 10 seconds and print the new value.
	clock.Add(10 * time.Second)
	fmt.Printf("Count is %d after 10 seconds\n", count)

	// Move the clock forward 5 more seconds and print the new value.
	clock.Add(5 * time.Second)
	fmt.Printf("Count is %d after 15 seconds\n", count)

	// Output:
	// Count is 10 after 10 seconds
	// Count is 15 after 15 seconds
}
Exemple #5
0
func ExampleMock_After() {
	// Create a new mock clock.
	clock := clock.NewMock()
	count := 0

	// Create a channel to execute after 10 mock seconds.
	go func() {
		<-clock.After(10 * time.Second)
		count = 100
	}()
	runtime.Gosched()

	// Print the starting value.
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Move the clock forward 5 seconds and print the value again.
	clock.Add(5 * time.Second)
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Move the clock forward 5 seconds to the tick time and check the value.
	clock.Add(5 * time.Second)
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Output:
	// 1970-01-01 00:00:00 +0000 UTC: 0
	// 1970-01-01 00:00:05 +0000 UTC: 0
	// 1970-01-01 00:00:10 +0000 UTC: 100
}
func TestThresholdBreakerResets(t *testing.T) {
	called := 0
	success := false
	circuit := func() error {
		if called == 0 {
			called++
			return fmt.Errorf("error")
		}
		success = true
		return nil
	}

	c := clock.NewMock()
	cb := NewThresholdBreaker(1)
	cb.Clock = c
	err := cb.Call(circuit, 0)
	if err == nil {
		t.Fatal("Expected cb to return an error")
	}

	c.Add(cb.nextBackOff + 1)
	for i := 0; i < 4; i++ {
		err = cb.Call(circuit, 0)
		if err != nil {
			t.Fatal("Expected cb to be successful")
		}

		if !success {
			t.Fatal("Expected cb to have been reset")
		}
	}
}
Exemple #7
0
// The following example shows how to create mock hashers for testing the rate
// limiter in your code:
func ExamplePerSecondHasher() {
	// Create a mock clock.
	mock := clock.NewMock()

	// Create a new per second hasher with the mock clock.
	hasher := PerMinuteHasher{
		Clock: mock,
	}

	// Generate two consecutive hashes. On most systems, the following should
	// generate two identical hashes.
	hashOne := hasher.Hash("127.0.0.1")
	hashTwo := hasher.Hash("127.0.0.1")

	// Now we push the clock forward by a minute (time travel).
	mock.Add(time.Minute)

	// The third hash should be different now.
	hashThree := hasher.Hash("127.0.0.1")

	fmt.Println(hashOne == hashTwo)
	fmt.Println(hashOne == hashThree)
	// Output: true
	// false
}
Exemple #8
0
func Test_PerMinute_Hash(t *testing.T) {
	mock := clock.NewMock()
	hasher := PerMinuteHasher{
		Clock: mock,
	}

	resultOne := hasher.Hash("127.0.0.1")

	mock.Add(time.Minute)

	resultTwo := hasher.Hash("127.0.0.1")

	assert.NotEqual(t, resultOne, resultTwo)

	resultThree := hasher.Hash("127.0.0.1")
	resultFour := hasher.Hash("127.0.0.1")
	resultFive := hasher.Hash("127.0.0.2")

	assert.Equal(t, resultThree, resultFour)
	assert.NotEqual(t, resultFour, resultFive)

	// Test that it can create a new clock
	hasher = PerMinuteHasher{}
	hasher.Hash("127.0.0.1")
}
Exemple #9
0
// Ensure that the mock's Tick channel sends at the correct time.
func TestMock_Tick(t *testing.T) {
	var n int32
	clock := clock.NewMock()

	// Create a channel to increment every 10 seconds.
	go func() {
		tick := clock.Tick(10 * time.Second)
		for {
			<-tick
			atomic.AddInt32(&n, 1)
		}
	}()
	gosched()

	// Move clock forward to just before the first tick.
	clock.Add(9 * time.Second)
	if atomic.LoadInt32(&n) != 0 {
		t.Fatalf("expected 0, got %d", n)
	}

	// Move clock forward to the start of the first tick.
	clock.Add(1 * time.Second)
	if atomic.LoadInt32(&n) != 1 {
		t.Fatalf("expected 1, got %d", n)
	}

	// Move clock forward over several ticks.
	clock.Add(30 * time.Second)
	if atomic.LoadInt32(&n) != 4 {
		t.Fatalf("expected 4, got %d", n)
	}
}
Exemple #10
0
// Ensure that the mock's current time can be changed.
func TestMock_Now(t *testing.T) {
	clock := clock.NewMock()
	if now := clock.Now(); !now.Equal(time.Unix(0, 0)) {
		t.Fatalf("expected epoch, got: ", now)
	}

	// Add 10 seconds and check the time.
	clock.Add(10 * time.Second)
	if now := clock.Now(); !now.Equal(time.Unix(10, 0)) {
		t.Fatalf("expected epoch, got: ", now)
	}
}
Exemple #11
0
func NewHarness(t testing.TB) *Harness {
	te := Harness{
		T:     t,
		Clock: clock.NewMock(),
	}
	te.Env = &Env{
		Out:            &te.Out,
		Err:            &te.Err,
		Clock:          te.Clock,
		ParseAPIClient: &ParseAPIClient{APIClient: &parse.Client{}},
	}
	return &te
}
Exemple #12
0
// Ensure that the mock's AfterFunc doesn't execute if stopped.
func TestMock_AfterFunc_Stop(t *testing.T) {
	// Execute function after duration.
	clock := clock.NewMock()
	timer := clock.AfterFunc(10*time.Second, func() {
		t.Fatal("unexpected function execution")
	})
	gosched()

	// Stop timer & move clock forward.
	timer.Stop()
	clock.Add(10 * time.Second)
	gosched()
}
Exemple #13
0
func newHarness(t testing.TB) *Harness {
	te := Harness{
		T:     t,
		Clock: clock.NewMock(),
	}
	te.env = &env{
		Out:    &te.Out,
		Err:    &te.Err,
		Clock:  te.Clock,
		Client: &Client{client: &parse.Client{}},
	}
	return &te
}
Exemple #14
0
func TestHTTPStopTimeoutMissed(t *testing.T) {
	t.Parallel()

	klock := clock.NewMock()

	const count = 10000
	hello := []byte("hello")
	finOkHandler := make(chan struct{})
	unblockOkHandler := make(chan struct{})
	okHandler := func(w http.ResponseWriter, r *http.Request) {
		defer close(finOkHandler)
		w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count))
		w.WriteHeader(200)
		for i := 0; i < count/2; i++ {
			w.Write(hello)
		}
		<-unblockOkHandler
		for i := 0; i < count/2; i++ {
			w.Write(hello)
		}
	}

	listener, err := net.Listen("tcp", "127.0.0.1:0")
	ensure.Nil(t, err)
	server := &http.Server{Handler: http.HandlerFunc(okHandler)}
	transport := &http.Transport{}
	client := &http.Client{Transport: transport}
	down := &httpdown.HTTP{
		StopTimeout: time.Minute,
		Clock:       klock,
	}
	s := down.Serve(server, listener)
	res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
	ensure.Nil(t, err)

	finStop := make(chan struct{})
	go func() {
		defer close(finStop)
		ensure.Nil(t, s.Stop())
	}()

	klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After
	klock.Add(down.StopTimeout)

	_, err = ioutil.ReadAll(res.Body)
	ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$"))
	ensure.Nil(t, res.Body.Close())
	close(unblockOkHandler)
	<-finOkHandler
	<-finStop
}
Exemple #15
0
func TestStatsTicker(t *testing.T) {
	t.Parallel()

	klock := clock.NewMock()
	expected := []string{"alive", "idle", "out", "waiting"}

	statsDone := make(chan string, 4)
	hc := &stats.HookClient{
		BumpAvgHook: func(key string, val float64) {
			if contains(expected, key) {
				statsDone <- key
			}
		},
	}

	var cm resourceMaker
	p := Pool{
		New:           cm.New,
		Stats:         hc,
		Max:           4,
		MinIdle:       2,
		IdleTimeout:   time.Second,
		ClosePoolSize: 2,
		Clock:         klock,
	}

	// acquire and release some resources to make them idle
	var resources []io.Closer
	for i := p.Max; i > 0; i-- {
		r, err := p.Acquire()
		ensure.Nil(t, err)
		resources = append(resources, r)
	}
	for _, r := range resources {
		p.Release(r)
	}

	// tick IdleTimeout to make them eligible
	klock.Add(p.IdleTimeout)

	// tick Minute to trigger stats
	klock.Add(time.Minute)

	// stats should soon show idle closed
	ensure.SameElements(
		t,
		[]string{<-statsDone, <-statsDone, <-statsDone, <-statsDone},
		expected,
	)
}
func TestTrippableBreakerManualBreak(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	cb.Break()
	c.Add(cb.nextBackOff + 1)

	if cb.Ready() {
		t.Fatal("expected breaker to still be tripped")
	}

	cb.Reset()
	cb.Trip()
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready")
	}
}
func TestRateBreakerResets(t *testing.T) {
	serviceError := fmt.Errorf("service error")

	called := 0
	success := false
	circuit := func() error {
		if called < 4 {
			called++
			return serviceError
		}
		success = true
		return nil
	}

	c := clock.NewMock()
	cb := NewRateBreaker(0.5, 4)
	cb.Clock = c
	var err error
	for i := 0; i < 4; i++ {
		err = cb.Call(circuit, 0)
		if err == nil {
			t.Fatal("Expected cb to return an error (closed breaker, service failure)")
		} else if err != serviceError {
			t.Fatal("Expected cb to return error from service (closed breaker, service failure)")
		}
	}

	err = cb.Call(circuit, 0)
	if err == nil {
		t.Fatal("Expected cb to return an error (open breaker)")
	} else if err != ErrBreakerOpen {
		t.Fatal("Expected cb to return open open breaker error (open breaker)")
	}

	c.Add(cb.nextBackOff + 1)
	err = cb.Call(circuit, 0)
	if err != nil {
		t.Fatal("Expected cb to be successful")
	}

	if !success {
		t.Fatal("Expected cb to have been reset")
	}
}
Exemple #18
0
// Ensure that the mock's Ticker channel sends at the correct time.
func TestMock_Ticker(t *testing.T) {
	var n int32
	clock := clock.NewMock()

	// Create a channel to increment every microsecond.
	go func() {
		ticker := clock.Ticker(1 * time.Microsecond)
		for {
			<-ticker.C
			atomic.AddInt32(&n, 1)
		}
	}()
	gosched()

	// Move clock forward.
	clock.Add(10 * time.Microsecond)
	if atomic.LoadInt32(&n) != 10 {
		t.Fatalf("unexpected: %d", n)
	}
}
func TestTimeoutBreaker(t *testing.T) {
	c := clock.NewMock()
	called := int32(0)
	circuit := func() error {
		atomic.AddInt32(&called, 1)
		c.Add(time.Millisecond)
		return nil
	}

	cb := NewThresholdBreaker(1)
	cb.Clock = c
	err := cb.Call(circuit, time.Millisecond)
	if err == nil {
		t.Fatal("expected timeout breaker to return an error")
	}
	cb.Call(circuit, time.Millisecond)

	if !cb.Tripped() {
		t.Fatal("expected timeout breaker to be open")
	}
}
Exemple #20
0
// Ensure that the mock's AfterFunc executes at the correct time.
func TestMock_AfterFunc(t *testing.T) {
	var ok int32
	clock := clock.NewMock()

	// Execute function after duration.
	clock.AfterFunc(10*time.Second, func() {
		atomic.StoreInt32(&ok, 1)
	})

	// Move clock forward to just before the time.
	clock.Add(9 * time.Second)
	if atomic.LoadInt32(&ok) == 1 {
		t.Fatal("too early")
	}

	// Move clock forward to the after channel's time.
	clock.Add(1 * time.Second)
	if atomic.LoadInt32(&ok) == 0 {
		t.Fatal("too late")
	}
}
Exemple #21
0
func TestContiniousSendWithTimeoutOnly(t *testing.T) {
	t.Parallel()
	var fireTotal, addedTotal uint64
	c := &testClient{
		BatchTimeout: 5 * time.Millisecond,
		Fire: func(actual []string, notifier Notifier) {
			defer notifier.Done()
			atomic.AddUint64(&fireTotal, uint64(len(actual)))
		},
		PendingWorkCapacity: 100,
	}
	klock := clock.NewMock()
	c.muster.klock = klock
	errCall(t, c.Start)

	finished := make(chan struct{})
	go func() {
		defer close(finished)
		for {
			select {
			case <-finished:
				return
			case c.muster.Work <- "42":
				atomic.AddUint64(&addedTotal, 1)
			default:
			}
		}
	}()

	for i := 0; i < 3; i++ {
		klock.Add(c.BatchTimeout)
	}
	finished <- struct{}{}
	<-finished

	errCall(t, c.Stop)
	if fireTotal != addedTotal {
		t.Fatalf("fireTotal=%d VS addedTotal=%d", fireTotal, addedTotal)
	}
}
Exemple #22
0
func ExampleMock_AfterFunc() {
	// Create a new mock clock.
	clock := clock.NewMock()
	count := 0

	// Execute a function after 10 mock seconds.
	clock.AfterFunc(10*time.Second, func() {
		count = 100
	})
	runtime.Gosched()

	// Print the starting value.
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Move the clock forward 10 seconds and print the new value.
	clock.Add(10 * time.Second)
	fmt.Printf("%s: %d\n", clock.Now().UTC(), count)

	// Output:
	// 1970-01-01 00:00:00 +0000 UTC: 0
	// 1970-01-01 00:00:10 +0000 UTC: 100
}
// TestPartialSecondBackoff ensures that the breaker event less than nextBackoff value
// time after tripping the breaker isn't allowed.
func TestPartialSecondBackoff(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c

	// Set the time to 0.5 seconds after the epoch, then trip the breaker.
	c.Add(500 * time.Millisecond)
	cb.Trip()

	// Move forward 100 milliseconds in time and ensure that the backoff time
	// is set to a larger number than the clock advanced.
	c.Add(100 * time.Millisecond)
	cb.nextBackOff = 500 * time.Millisecond
	if cb.Ready() {
		t.Fatalf("expected breaker not to be ready after less time than nextBackoff had passed")
	}

	c.Add(401 * time.Millisecond)
	if !cb.Ready() {
		t.Fatalf("expected breaker to be ready after more than nextBackoff time had passed")
	}
}
Exemple #24
0
// Ensure that the mock's After channel sends at the correct time.
func TestMock_After(t *testing.T) {
	var ok int32
	clock := clock.NewMock()

	// Create a channel to execute after 10 mock seconds.
	ch := clock.After(10 * time.Second)
	go func(ch <-chan time.Time) {
		<-ch
		atomic.StoreInt32(&ok, 1)
	}(ch)

	// Move clock forward to just before the time.
	clock.Add(9 * time.Second)
	if atomic.LoadInt32(&ok) == 1 {
		t.Fatal("too early")
	}

	// Move clock forward to the after channel's time.
	clock.Add(1 * time.Second)
	if atomic.LoadInt32(&ok) == 0 {
		t.Fatal("too late")
	}
}
Exemple #25
0
// Ensure that the mock can sleep for the correct time.
func TestMock_Sleep(t *testing.T) {
	var ok int32
	clock := clock.NewMock()

	// Create a channel to execute after 10 mock seconds.
	go func() {
		clock.Sleep(10 * time.Second)
		atomic.StoreInt32(&ok, 1)
	}()
	gosched()

	// Move clock forward to just before the sleep duration.
	clock.Add(9 * time.Second)
	if atomic.LoadInt32(&ok) == 1 {
		t.Fatal("too early")
	}

	// Move clock forward to the after the sleep duration.
	clock.Add(1 * time.Second)
	if atomic.LoadInt32(&ok) == 0 {
		t.Fatal("too late")
	}
}
func TestAddRemoveListener(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c
	events := make(chan ListenerEvent, 100)
	cb.AddListener(events)

	cb.Trip()
	if e := <-events; e.Event != BreakerTripped {
		t.Fatalf("expected to receive a trip event, got %v", e)
	}

	c.Add(cb.nextBackOff + 1)
	cb.Ready()
	if e := <-events; e.Event != BreakerReady {
		t.Fatalf("expected to receive a breaker ready event, got %v", e)
	}

	cb.Reset()
	if e := <-events; e.Event != BreakerReset {
		t.Fatalf("expected to receive a reset event, got %v", e)
	}

	cb.Fail()
	if e := <-events; e.Event != BreakerFail {
		t.Fatalf("expected to receive a fail event, got %v", e)
	}

	cb.RemoveListener(events)
	cb.Reset()
	select {
	case e := <-events:
		t.Fatalf("after removing listener, should not receive reset event; got %v", e)
	default:
		// Expected.
	}
}
Exemple #27
0
func TestWindowSlides(t *testing.T) {
	c := clock.NewMock()

	w := newWindow(time.Millisecond*10, 2)
	w.clock = c
	w.lastAccess = c.Now()

	w.Fail()
	c.Add(time.Millisecond * 6)
	w.Fail()

	counts := 0
	w.buckets.Do(func(x interface{}) {
		b := x.(*bucket)
		if b.failure > 0 {
			counts++
		}
	})

	if counts != 2 {
		t.Fatalf("expected 2 buckets to have failures, got %d", counts)
	}

	c.Add(time.Millisecond * 15)
	w.Success()
	counts = 0
	w.buckets.Do(func(x interface{}) {
		b := x.(*bucket)
		if b.failure > 0 {
			counts++
		}
	})

	if counts != 0 {
		t.Fatalf("expected 0 buckets to have failures, got %d", counts)
	}
}
func TestTimeoutBreaker(t *testing.T) {
	wait := make(chan struct{})

	c := clock.NewMock()
	called := int32(0)

	circuit := func() error {
		wait <- struct{}{}
		atomic.AddInt32(&called, 1)
		<-wait
		return nil
	}

	cb := NewThresholdBreaker(1)
	cb.Clock = c

	errc := make(chan error)
	go func() { errc <- cb.Call(circuit, time.Millisecond) }()

	<-wait
	c.Add(time.Millisecond * 3)
	wait <- struct{}{}

	err := <-errc
	if err == nil {
		t.Fatal("expected timeout breaker to return an error")
	}

	go cb.Call(circuit, time.Millisecond)
	<-wait
	c.Add(time.Millisecond * 3)
	wait <- struct{}{}

	if !cb.Tripped() {
		t.Fatal("expected timeout breaker to be open")
	}
}
func TestTrippableBreakerState(t *testing.T) {
	c := clock.NewMock()
	cb := NewBreaker()
	cb.Clock = c

	if !cb.Ready() {
		t.Fatal("expected breaker to be ready")
	}

	cb.Trip()
	if cb.Ready() {
		t.Fatal("expected breaker to not be ready")
	}
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready after reset timeout")
	}

	cb.Fail()
	c.Add(cb.nextBackOff + 1)
	if !cb.Ready() {
		t.Fatal("expected breaker to be ready after reset timeout, post failure")
	}
}
Exemple #30
0
func TestHTTPKillTimeoutMissed(t *testing.T) {
	t.Parallel()

	klock := clock.NewMock()

	statsDone := make(chan struct{}, 1)
	hc := &stats.HookClient{
		BumpSumHook: func(key string, val float64) {
			if key == "kill.timeout" && val == 1 {
				statsDone <- struct{}{}
			}
		},
	}

	const count = 10000
	hello := []byte("hello")
	finOkHandler := make(chan struct{})
	unblockOkHandler := make(chan struct{})
	okHandler := func(w http.ResponseWriter, r *http.Request) {
		defer close(finOkHandler)
		w.Header().Set("Content-Length", fmt.Sprint(len(hello)*count))
		w.WriteHeader(200)
		for i := 0; i < count/2; i++ {
			w.Write(hello)
		}
		<-unblockOkHandler
		for i := 0; i < count/2; i++ {
			w.Write(hello)
		}
	}

	listener, err := net.Listen("tcp", "127.0.0.1:0")
	ensure.Nil(t, err)
	unblockConnClose := make(chan chan struct{}, 1)
	listener = &closeErrConnListener{
		Listener:     listener,
		unblockClose: unblockConnClose,
	}

	server := &http.Server{Handler: http.HandlerFunc(okHandler)}
	transport := &http.Transport{}
	client := &http.Client{Transport: transport}
	down := &httpdown.HTTP{
		StopTimeout: time.Minute,
		KillTimeout: time.Minute,
		Stats:       hc,
		Clock:       klock,
	}
	s := down.Serve(server, listener)
	res, err := client.Get(fmt.Sprintf("http://%s/", listener.Addr().String()))
	ensure.Nil(t, err)

	// Start the Stop process.
	finStop := make(chan struct{})
	go func() {
		defer close(finStop)
		ensure.Nil(t, s.Stop())
	}()

	klock.Wait(clock.Calls{After: 1}) // wait for Stop to call After
	klock.Add(down.StopTimeout)       // trigger stop timeout
	klock.Wait(clock.Calls{After: 2}) // wait for Kill to call After
	klock.Add(down.KillTimeout)       // trigger kill timeout

	// We hit both the StopTimeout & the KillTimeout.
	<-finStop

	// Then we unblock the Close, so we get an unexpected EOF since we close
	// before we finish writing the response.
	connCloseDone := make(chan struct{})
	unblockConnClose <- connCloseDone
	<-connCloseDone
	close(unblockConnClose)

	// Then we unblock the handler which tries to write the rest of the data.
	close(unblockOkHandler)

	_, err = ioutil.ReadAll(res.Body)
	ensure.Err(t, err, regexp.MustCompile("^unexpected EOF$"))
	ensure.Nil(t, res.Body.Close())
	<-finOkHandler
	<-statsDone
}