1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package nettest provides utilities for network testing.
22 aLongTimeAgo = time.Unix(233431200, 0)
23 neverTimeout = time.Time{}
26 // MakePipe creates a connection between two endpoints and returns the pair
27 // as c1 and c2, such that anything written to c1 is read by c2 and vice-versa.
28 // The stop function closes all resources, including c1, c2, and the underlying
29 // net.Listener (if there is one), and should not be nil.
30 type MakePipe func() (c1, c2 net.Conn, stop func(), err error)
32 // TestConn tests that a net.Conn implementation properly satisfies the interface.
33 // The tests should not produce any false positives, but may experience
34 // false negatives. Thus, some issues may only be detected when the test is
35 // run multiple times. For maximal effectiveness, run the tests under the
37 func TestConn(t *testing.T, mp MakePipe) {
41 type connTester func(t *testing.T, c1, c2 net.Conn)
43 func timeoutWrapper(t *testing.T, mp MakePipe, f connTester) {
45 c1, c2, stop, err := mp()
47 t.Fatalf("unable to make pipe: %v", err)
50 defer once.Do(func() { stop() })
51 timer := time.AfterFunc(time.Minute, func() {
53 t.Error("test timed out; terminating pipe")
61 // testBasicIO tests that the data sent on c1 is properly received on c2.
62 func testBasicIO(t *testing.T, c1, c2 net.Conn) {
63 want := make([]byte, 1<<20)
64 rand.New(rand.NewSource(0)).Read(want)
66 dataCh := make(chan []byte)
68 rd := bytes.NewReader(want)
69 if err := chunkedCopy(c1, rd); err != nil {
70 t.Errorf("unexpected c1.Write error: %v", err)
72 if err := c1.Close(); err != nil {
73 t.Errorf("unexpected c1.Close error: %v", err)
78 wr := new(bytes.Buffer)
79 if err := chunkedCopy(wr, c2); err != nil {
80 t.Errorf("unexpected c2.Read error: %v", err)
82 if err := c2.Close(); err != nil {
83 t.Errorf("unexpected c2.Close error: %v", err)
88 if got := <-dataCh; !bytes.Equal(got, want) {
89 t.Errorf("transmitted data differs")
93 // testPingPong tests that the two endpoints can synchronously send data to
94 // each other in a typical request-response pattern.
95 func testPingPong(t *testing.T, c1, c2 net.Conn) {
99 pingPonger := func(c net.Conn) {
101 buf := make([]byte, 8)
104 if _, err := io.ReadFull(c, buf); err != nil {
108 t.Errorf("unexpected Read error: %v", err)
111 v := binary.LittleEndian.Uint64(buf)
112 binary.LittleEndian.PutUint64(buf, v+1)
113 if prev != 0 && prev+2 != v {
114 t.Errorf("mismatching value: got %d, want %d", v, prev+2)
121 if _, err := c.Write(buf); err != nil {
122 t.Errorf("unexpected Write error: %v", err)
126 if err := c.Close(); err != nil {
127 t.Errorf("unexpected Close error: %v", err)
135 // Start off the chain reaction.
136 if _, err := c1.Write(make([]byte, 8)); err != nil {
137 t.Errorf("unexpected c1.Write error: %v", err)
141 // testRacyRead tests that it is safe to mutate the input Read buffer
142 // immediately after cancelation has occurred.
143 func testRacyRead(t *testing.T, c1, c2 net.Conn) {
144 go chunkedCopy(c2, rand.New(rand.NewSource(0)))
146 var wg sync.WaitGroup
149 c1.SetReadDeadline(time.Now().Add(time.Millisecond))
150 for i := 0; i < 10; i++ {
155 b1 := make([]byte, 1024)
156 b2 := make([]byte, 1024)
157 for j := 0; j < 100; j++ {
158 _, err := c1.Read(b1)
159 copy(b1, b2) // Mutate b1 to trigger potential race
161 checkForTimeoutError(t, err)
162 c1.SetReadDeadline(time.Now().Add(time.Millisecond))
169 // testRacyWrite tests that it is safe to mutate the input Write buffer
170 // immediately after cancelation has occurred.
171 func testRacyWrite(t *testing.T, c1, c2 net.Conn) {
172 go chunkedCopy(ioutil.Discard, c2)
174 var wg sync.WaitGroup
177 c1.SetWriteDeadline(time.Now().Add(time.Millisecond))
178 for i := 0; i < 10; i++ {
183 b1 := make([]byte, 1024)
184 b2 := make([]byte, 1024)
185 for j := 0; j < 100; j++ {
186 _, err := c1.Write(b1)
187 copy(b1, b2) // Mutate b1 to trigger potential race
189 checkForTimeoutError(t, err)
190 c1.SetWriteDeadline(time.Now().Add(time.Millisecond))
197 // testReadTimeout tests that Read timeouts do not affect Write.
198 func testReadTimeout(t *testing.T, c1, c2 net.Conn) {
199 go chunkedCopy(ioutil.Discard, c2)
201 c1.SetReadDeadline(aLongTimeAgo)
202 _, err := c1.Read(make([]byte, 1024))
203 checkForTimeoutError(t, err)
204 if _, err := c1.Write(make([]byte, 1024)); err != nil {
205 t.Errorf("unexpected Write error: %v", err)
209 // testWriteTimeout tests that Write timeouts do not affect Read.
210 func testWriteTimeout(t *testing.T, c1, c2 net.Conn) {
211 go chunkedCopy(c2, rand.New(rand.NewSource(0)))
213 c1.SetWriteDeadline(aLongTimeAgo)
214 _, err := c1.Write(make([]byte, 1024))
215 checkForTimeoutError(t, err)
216 if _, err := c1.Read(make([]byte, 1024)); err != nil {
217 t.Errorf("unexpected Read error: %v", err)
221 // testPastTimeout tests that a deadline set in the past immediately times out
222 // Read and Write requests.
223 func testPastTimeout(t *testing.T, c1, c2 net.Conn) {
224 go chunkedCopy(c2, c2)
228 c1.SetDeadline(aLongTimeAgo)
229 n, err := c1.Write(make([]byte, 1024))
231 t.Errorf("unexpected Write count: got %d, want 0", n)
233 checkForTimeoutError(t, err)
234 n, err = c1.Read(make([]byte, 1024))
236 t.Errorf("unexpected Read count: got %d, want 0", n)
238 checkForTimeoutError(t, err)
243 // testPresentTimeout tests that a past deadline set while there are pending
244 // Read and Write operations immediately times out those operations.
245 func testPresentTimeout(t *testing.T, c1, c2 net.Conn) {
246 var wg sync.WaitGroup
250 deadlineSet := make(chan bool, 1)
253 time.Sleep(100 * time.Millisecond)
255 c1.SetReadDeadline(aLongTimeAgo)
256 c1.SetWriteDeadline(aLongTimeAgo)
260 n, err := c1.Read(make([]byte, 1024))
262 t.Errorf("unexpected Read count: got %d, want 0", n)
264 checkForTimeoutError(t, err)
265 if len(deadlineSet) == 0 {
266 t.Error("Read timed out before deadline is set")
273 _, err = c1.Write(make([]byte, 1024))
275 checkForTimeoutError(t, err)
276 if len(deadlineSet) == 0 {
277 t.Error("Write timed out before deadline is set")
282 // testFutureTimeout tests that a future deadline will eventually time out
283 // Read and Write operations.
284 func testFutureTimeout(t *testing.T, c1, c2 net.Conn) {
285 var wg sync.WaitGroup
288 c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
291 _, err := c1.Read(make([]byte, 1024))
292 checkForTimeoutError(t, err)
298 _, err = c1.Write(make([]byte, 1024))
300 checkForTimeoutError(t, err)
304 go chunkedCopy(c2, c2)
309 // testCloseTimeout tests that calling Close immediately times out pending
310 // Read and Write operations.
311 func testCloseTimeout(t *testing.T, c1, c2 net.Conn) {
312 go chunkedCopy(c2, c2)
314 var wg sync.WaitGroup
318 // Test for cancelation upon connection closure.
319 c1.SetDeadline(neverTimeout)
322 time.Sleep(100 * time.Millisecond)
328 buf := make([]byte, 1024)
330 _, err = c1.Read(buf)
336 buf := make([]byte, 1024)
338 _, err = c1.Write(buf)
343 // testConcurrentMethods tests that the methods of net.Conn can safely
344 // be called concurrently.
345 func testConcurrentMethods(t *testing.T, c1, c2 net.Conn) {
346 if runtime.GOOS == "plan9" {
347 t.Skip("skipping on plan9; see https://golang.org/issue/20489")
349 go chunkedCopy(c2, c2)
351 // The results of the calls may be nonsensical, but this should
352 // not trigger a race detector warning.
353 var wg sync.WaitGroup
354 for i := 0; i < 100; i++ {
358 c1.Read(make([]byte, 1024))
362 c1.Write(make([]byte, 1024))
366 c1.SetDeadline(time.Now().Add(10 * time.Millisecond))
370 c1.SetReadDeadline(aLongTimeAgo)
374 c1.SetWriteDeadline(aLongTimeAgo)
385 wg.Wait() // At worst, the deadline is set 10ms into the future
391 // checkForTimeoutError checks that the error satisfies the Error interface
392 // and that Timeout returns true.
393 func checkForTimeoutError(t *testing.T, err error) {
395 if nerr, ok := err.(net.Error); ok {
397 t.Errorf("err.Timeout() = false, want true")
400 t.Errorf("got %T, want net.Error", err)
404 // testRoundtrip writes something into c and reads it back.
405 // It assumes that everything written into c is echoed back to itself.
406 func testRoundtrip(t *testing.T, c net.Conn) {
408 if err := c.SetDeadline(neverTimeout); err != nil {
409 t.Errorf("roundtrip SetDeadline error: %v", err)
412 const s = "Hello, world!"
414 if _, err := c.Write(buf); err != nil {
415 t.Errorf("roundtrip Write error: %v", err)
417 if _, err := io.ReadFull(c, buf); err != nil {
418 t.Errorf("roundtrip Read error: %v", err)
420 if string(buf) != s {
421 t.Errorf("roundtrip data mismatch: got %q, want %q", buf, s)
425 // resyncConn resynchronizes the connection into a sane state.
426 // It assumes that everything written into c is echoed back to itself.
427 // It assumes that 0xff is not currently on the wire or in the read buffer.
428 func resyncConn(t *testing.T, c net.Conn) {
430 c.SetDeadline(neverTimeout)
431 errCh := make(chan error)
433 _, err := c.Write([]byte{0xff})
436 buf := make([]byte, 1024)
438 n, err := c.Read(buf)
439 if n > 0 && bytes.IndexByte(buf[:n], 0xff) == n-1 {
443 t.Errorf("unexpected Read error: %v", err)
447 if err := <-errCh; err != nil {
448 t.Errorf("unexpected Write error: %v", err)
452 // chunkedCopy copies from r to w in fixed-width chunks to avoid
453 // causing a Write that exceeds the maximum packet size for packet-based
454 // connections like "unixpacket".
455 // We assume that the maximum packet size is at least 1024.
456 func chunkedCopy(w io.Writer, r io.Reader) error {
457 b := make([]byte, 1024)
458 _, err := io.CopyBuffer(struct{ io.Writer }{w}, struct{ io.Reader }{r}, b)