mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-16 03:31:39 +00:00
derp: deflake test
Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
This commit is contained in:
parent
52d9613b42
commit
131541c06d
@ -9,7 +9,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
crand "crypto/rand"
|
crand "crypto/rand"
|
||||||
"errors"
|
"errors"
|
||||||
"expvar"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@ -215,10 +214,27 @@ func TestSendFreeze(t *testing.T) {
|
|||||||
cathyKey := newPrivateKey(t)
|
cathyKey := newPrivateKey(t)
|
||||||
cathyClient, cathyConn := newClient("cathy", cathyKey)
|
cathyClient, cathyConn := newClient("cathy", cathyKey)
|
||||||
|
|
||||||
var aliceCount, bobCount, cathyCount expvar.Int
|
var (
|
||||||
|
aliceCh = make(chan struct{}, 32)
|
||||||
|
bobCh = make(chan struct{}, 32)
|
||||||
|
cathyCh = make(chan struct{}, 32)
|
||||||
|
)
|
||||||
|
chs := func(name string) chan struct{} {
|
||||||
|
switch name {
|
||||||
|
case "alice":
|
||||||
|
return aliceCh
|
||||||
|
case "bob":
|
||||||
|
return bobCh
|
||||||
|
case "cathy":
|
||||||
|
return cathyCh
|
||||||
|
default:
|
||||||
|
panic("unknown ch: " + name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
errCh := make(chan error, 4)
|
errCh := make(chan error, 4)
|
||||||
recvAndCount := func(count *expvar.Int, name string, client *Client) {
|
recv := func(name string, client *Client) {
|
||||||
|
ch := chs(name)
|
||||||
for {
|
for {
|
||||||
b := make([]byte, 1<<9)
|
b := make([]byte, 1<<9)
|
||||||
m, err := client.Recv(b)
|
m, err := client.Recv(b)
|
||||||
@ -235,13 +251,16 @@ func TestSendFreeze(t *testing.T) {
|
|||||||
errCh <- fmt.Errorf("%s: zero Source address in ReceivedPacket", name)
|
errCh <- fmt.Errorf("%s: zero Source address in ReceivedPacket", name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
count.Add(1)
|
select {
|
||||||
|
case ch <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
go recvAndCount(&aliceCount, "alice", aliceClient)
|
go recv("alice", aliceClient)
|
||||||
go recvAndCount(&bobCount, "bob", bobClient)
|
go recv("bob", bobClient)
|
||||||
go recvAndCount(&cathyCount, "cathy", cathyClient)
|
go recv("cathy", cathyClient)
|
||||||
|
|
||||||
var cancel func()
|
var cancel func()
|
||||||
go func() {
|
go func() {
|
||||||
@ -270,38 +289,52 @@ func TestSendFreeze(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var countSnapshot [3]int64
|
drainAny := func(ch chan struct{}) {
|
||||||
loadCounts := func() (adiff, bdiff, cdiff int64) {
|
// We are draining potentially infinite sources,
|
||||||
|
// so place some reasonable upper limit.
|
||||||
|
//
|
||||||
|
// The important thing here is to make sure that
|
||||||
|
// if any tokens remain in the channel, they
|
||||||
|
// must have been generated after drainAny was
|
||||||
|
// called.
|
||||||
|
for i := 0; i < cap(ch); i++ {
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drain := func(t *testing.T, name string) bool {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
timer := time.NewTimer(1 * time.Second)
|
||||||
|
defer timer.Stop()
|
||||||
|
|
||||||
atotal := aliceCount.Value()
|
// Ensure ch has at least one element.
|
||||||
btotal := bobCount.Value()
|
ch := chs(name)
|
||||||
ctotal := cathyCount.Value()
|
select {
|
||||||
|
case <-ch:
|
||||||
adiff = atotal - countSnapshot[0]
|
case <-timer.C:
|
||||||
bdiff = btotal - countSnapshot[1]
|
t.Errorf("no packet received by %s", name)
|
||||||
cdiff = ctotal - countSnapshot[2]
|
return false
|
||||||
|
}
|
||||||
countSnapshot[0] = atotal
|
// Drain remaining.
|
||||||
countSnapshot[1] = btotal
|
drainAny(ch)
|
||||||
countSnapshot[2] = ctotal
|
return true
|
||||||
|
}
|
||||||
t.Logf("count diffs: alice=%d, bob=%d, cathy=%d", adiff, bdiff, cdiff)
|
isEmpty := func(t *testing.T, name string) {
|
||||||
return adiff, bdiff, cdiff
|
t.Helper()
|
||||||
|
select {
|
||||||
|
case <-chs(name):
|
||||||
|
t.Errorf("packet received by %s, want none", name)
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("initial send", func(t *testing.T) {
|
t.Run("initial send", func(t *testing.T) {
|
||||||
time.Sleep(10 * time.Millisecond)
|
drain(t, "bob")
|
||||||
a, b, c := loadCounts()
|
drain(t, "cathy")
|
||||||
if a != 0 {
|
isEmpty(t, "alice")
|
||||||
t.Errorf("alice diff=%d, want 0", a)
|
|
||||||
}
|
|
||||||
if b == 0 {
|
|
||||||
t.Errorf("no bob diff, want positive value")
|
|
||||||
}
|
|
||||||
if c == 0 {
|
|
||||||
t.Errorf("no cathy diff, want positive value")
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("block cathy", func(t *testing.T) {
|
t.Run("block cathy", func(t *testing.T) {
|
||||||
@ -310,17 +343,12 @@ func TestSendFreeze(t *testing.T) {
|
|||||||
cathyConn.SetReadBlock(true)
|
cathyConn.SetReadBlock(true)
|
||||||
time.Sleep(2 * s.WriteTimeout)
|
time.Sleep(2 * s.WriteTimeout)
|
||||||
|
|
||||||
a, b, _ := loadCounts()
|
drain(t, "bob")
|
||||||
if a != 0 {
|
drainAny(chs("cathy"))
|
||||||
t.Errorf("alice diff=%d, want 0", a)
|
isEmpty(t, "alice")
|
||||||
}
|
|
||||||
if b == 0 {
|
|
||||||
t.Errorf("no bob diff, want positive value")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now wait a little longer, and ensure packets still flow to bob
|
// Now wait a little longer, and ensure packets still flow to bob
|
||||||
time.Sleep(10 * time.Millisecond)
|
if !drain(t, "bob") {
|
||||||
if _, b, _ := loadCounts(); b == 0 {
|
|
||||||
t.Errorf("connection alice->bob frozen by alice->cathy")
|
t.Errorf("connection alice->bob frozen by alice->cathy")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user