Skip to content

Commit

Permalink
net/http: deflake TestServerCancelsReadTimeoutWhenIdle
Browse files Browse the repository at this point in the history
I can reproduce with a very short timeout (fractions of a millisecond)
combined with -race.

But given that this is inherently sensitive to actual time, add a
testing mechanism to retry with increasingly large times to compensate
for busy buidlers. This also means the test is usually faster now,
too, since we can start with smaller durations.

Fixes #19608

Change-Id: I3a222464720195849da768e9801eb7b43baa4aeb
Reviewed-on: https://go-review.googlesource.com/82595
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
  • Loading branch information
bradfitz committed Dec 8, 2017
1 parent 613f8ca commit 95fab85
Showing 1 changed file with 46 additions and 24 deletions.
70 changes: 46 additions & 24 deletions src/net/http/serve_test.go
Expand Up @@ -5478,32 +5478,54 @@ func testServerKeepAlivesEnabled(t *testing.T, h2 bool) {
func TestServerCancelsReadTimeoutWhenIdle(t *testing.T) {
setParallel(t)
defer afterTest(t)
const timeout = 250 * time.Millisecond
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
select {
case <-time.After(2 * timeout):
fmt.Fprint(w, "ok")
case <-r.Context().Done():
fmt.Fprint(w, r.Context().Err())
}
}))
ts.Config.ReadTimeout = timeout
ts.Start()
defer ts.Close()
runTimeSensitiveTest(t, []time.Duration{
10 * time.Millisecond,
50 * time.Millisecond,
250 * time.Millisecond,
time.Second,
2 * time.Second,
}, func(t *testing.T, timeout time.Duration) error {
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
select {
case <-time.After(2 * timeout):
fmt.Fprint(w, "ok")
case <-r.Context().Done():
fmt.Fprint(w, r.Context().Err())
}
}))
ts.Config.ReadTimeout = timeout
ts.Start()
defer ts.Close()

c := ts.Client()
c := ts.Client()

res, err := c.Get(ts.URL)
if err != nil {
t.Fatal(err)
}
slurp, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Fatal(err)
}
if string(slurp) != "ok" {
t.Fatalf("Got: %q, want ok", slurp)
res, err := c.Get(ts.URL)
if err != nil {
return fmt.Errorf("Get: %v", err)
}
slurp, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return fmt.Errorf("Body ReadAll: %v", err)
}
if string(slurp) != "ok" {
return fmt.Errorf("got: %q, want ok", slurp)
}
return nil
})
}

// runTimeSensitiveTest runs test with the provided durations until one passes.
// If they all fail, t.Fatal is called with the last one's duration and error value.
func runTimeSensitiveTest(t *testing.T, durations []time.Duration, test func(t *testing.T, d time.Duration) error) {
for i, d := range durations {
err := test(t, d)
if err == nil {
return
}
if i == len(durations)-1 {
t.Fatalf("failed with duration %v: %v", d, err)
}
}
}

Expand Down

0 comments on commit 95fab85

Please sign in to comment.