Skip to content

Commit db4efeb

Browse files
committed
http2: deflake TestTransportGroupsPendingDials
This test makes assumptions about the internal structure of *clientConnPool, as well as assuming that a goroutine will schedule and run within one second. The former assumption isn't necessary, and the latter causes flakiness. Refactor the test to count dial and close calls, which is all it needs to test the desired behavior (one pending dial per destination). Fixes golang/go#43176. Change-Id: I125b110f196e29f303960c6851089118f8fb5d38 Reviewed-on: https://go-review.googlesource.com/c/net/+/370175 Trust: Damien Neil <[email protected]> Run-TryBot: Damien Neil <[email protected]> Reviewed-by: Bryan Mills <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 04296fa commit db4efeb

File tree

1 file changed

+39
-50
lines changed

1 file changed

+39
-50
lines changed

http2/transport_test.go

Lines changed: 39 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -330,29 +330,50 @@ func testTransportGetGotConnHooks(t *testing.T, useClient bool) {
330330
}
331331
}
332332

333+
type testNetConn struct {
334+
net.Conn
335+
closed bool
336+
onClose func()
337+
}
338+
339+
func (c *testNetConn) Close() error {
340+
if !c.closed {
341+
// We can call Close multiple times on the same net.Conn.
342+
c.onClose()
343+
}
344+
c.closed = true
345+
return c.Conn.Close()
346+
}
347+
333348
// Tests that the Transport only keeps one pending dial open per destination address.
334349
// https://golang.org/issue/13397
335350
func TestTransportGroupsPendingDials(t *testing.T) {
336351
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
337-
io.WriteString(w, r.RemoteAddr)
338352
}, optOnlyServer)
339353
defer st.Close()
340-
tr := &Transport{
341-
TLSClientConfig: tlsConfigInsecure,
342-
}
343-
defer tr.CloseIdleConnections()
344354
var (
345-
mu sync.Mutex
346-
dials = map[string]int{}
355+
mu sync.Mutex
356+
dialCount int
357+
closeCount int
347358
)
348-
var gotConnCnt int32
349-
trace := &httptrace.ClientTrace{
350-
GotConn: func(connInfo httptrace.GotConnInfo) {
351-
if !connInfo.Reused {
352-
atomic.AddInt32(&gotConnCnt, 1)
353-
}
359+
tr := &Transport{
360+
TLSClientConfig: tlsConfigInsecure,
361+
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
362+
mu.Lock()
363+
dialCount++
364+
mu.Unlock()
365+
c, err := tls.Dial(network, addr, cfg)
366+
return &testNetConn{
367+
Conn: c,
368+
onClose: func() {
369+
mu.Lock()
370+
closeCount++
371+
mu.Unlock()
372+
},
373+
}, err
354374
},
355375
}
376+
defer tr.CloseIdleConnections()
356377
var wg sync.WaitGroup
357378
for i := 0; i < 10; i++ {
358379
wg.Add(1)
@@ -363,53 +384,21 @@ func TestTransportGroupsPendingDials(t *testing.T) {
363384
t.Error(err)
364385
return
365386
}
366-
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
367387
res, err := tr.RoundTrip(req)
368388
if err != nil {
369389
t.Error(err)
370390
return
371391
}
372-
defer res.Body.Close()
373-
slurp, err := ioutil.ReadAll(res.Body)
374-
if err != nil {
375-
t.Errorf("Body read: %v", err)
376-
}
377-
addr := strings.TrimSpace(string(slurp))
378-
if addr == "" {
379-
t.Errorf("didn't get an addr in response")
380-
}
381-
mu.Lock()
382-
dials[addr]++
383-
mu.Unlock()
392+
res.Body.Close()
384393
}()
385394
}
386395
wg.Wait()
387-
if len(dials) != 1 {
388-
t.Errorf("saw %d dials; want 1: %v", len(dials), dials)
389-
}
390396
tr.CloseIdleConnections()
391-
if err := retry(50, 10*time.Millisecond, func() error {
392-
cp, ok := tr.connPool().(*clientConnPool)
393-
if !ok {
394-
return fmt.Errorf("Conn pool is %T; want *clientConnPool", tr.connPool())
395-
}
396-
cp.mu.Lock()
397-
defer cp.mu.Unlock()
398-
if len(cp.dialing) != 0 {
399-
return fmt.Errorf("dialing map = %v; want empty", cp.dialing)
400-
}
401-
if len(cp.conns) != 0 {
402-
return fmt.Errorf("conns = %v; want empty", cp.conns)
403-
}
404-
if len(cp.keys) != 0 {
405-
return fmt.Errorf("keys = %v; want empty", cp.keys)
406-
}
407-
return nil
408-
}); err != nil {
409-
t.Errorf("State of pool after CloseIdleConnections: %v", err)
397+
if dialCount != 1 {
398+
t.Errorf("saw %d dials; want 1", dialCount)
410399
}
411-
if got, want := gotConnCnt, int32(1); got != want {
412-
t.Errorf("Too many got connections: %d", gotConnCnt)
400+
if closeCount != 1 {
401+
t.Errorf("saw %d closes; want 1", closeCount)
413402
}
414403
}
415404

0 commit comments

Comments
 (0)