Source file src/internal/nettest/nettest_test.go

     1  // Copyright 2026 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.
     4  
     5  package nettest_test
     6  
     7  import (
     8  	"errors"
     9  	"internal/nettest"
    10  	"net"
    11  	"os"
    12  	"sync/atomic"
    13  	"testing"
    14  	"testing/synctest"
    15  	"time"
    16  )
    17  
    18  var (
    19  	_ net.Conn       = (*nettest.Conn)(nil)
    20  	_ net.Listener   = (*nettest.Listener)(nil)
    21  	_ net.PacketConn = (*nettest.PacketConn)(nil)
    22  )
    23  
    24  func synctestSubtest(t *testing.T, name string, f func(t *testing.T)) {
    25  	t.Run(name, func(t *testing.T) {
    26  		synctest.Test(t, func(t *testing.T) {
    27  			f(t)
    28  		})
    29  	})
    30  }
    31  
    32  // A deadlineTest describes an operation which blocks until a deadline,
    33  // and a separate operation which unblocks it.
    34  type deadlineTest struct {
    35  	what        string                // name of the blocking op; e.g., "Read"
    36  	block       func() error          // blocking op; e.g., reading from a conn
    37  	unblock     func()                // unblocking op; e.g. writing to the other side
    38  	setDeadline func(d time.Duration) // deadline func; e.g., SetReadDeadline
    39  }
    40  
    41  // testDeadline tests a variety of scenarios involving deadlines.
    42  func testDeadline(t *testing.T, setup func() deadlineTest) {
    43  	synctestSubtest(t, "no deadline", func(t *testing.T) {
    44  		test := setup()
    45  		test.unblock()
    46  		synctest.Wait()
    47  		if err := test.block(); errors.Is(err, os.ErrDeadlineExceeded) {
    48  			t.Errorf("%v: %v, want not deadline exceeded", test.what, err)
    49  		}
    50  	})
    51  	synctestSubtest(t, "unblock before setdeadline", func(t *testing.T) {
    52  		test := setup()
    53  		test.unblock()
    54  		synctest.Wait()
    55  		test.setDeadline(5 * time.Second)
    56  		if err := test.block(); errors.Is(err, os.ErrDeadlineExceeded) {
    57  			t.Errorf("%v: %v, want not deadline exceeded", test.what, err)
    58  		}
    59  	})
    60  	synctestSubtest(t, "unblock after blocking", func(t *testing.T) {
    61  		test := setup()
    62  		test.setDeadline(5 * time.Second)
    63  		var done bool
    64  		go func() {
    65  			if err := test.block(); errors.Is(err, os.ErrDeadlineExceeded) {
    66  				t.Errorf("%v: %v, want not deadline exceeded", test.what, err)
    67  			}
    68  			done = true
    69  		}()
    70  		synctest.Wait()
    71  		if done {
    72  			t.Fatalf("%v: unexpectedly returned before unblocking", test.what)
    73  		}
    74  		test.unblock()
    75  		synctest.Wait()
    76  		if !done {
    77  			t.Fatalf("%v: did not return after unblocking", test.what)
    78  		}
    79  	})
    80  	synctestSubtest(t, "deadline expires", func(t *testing.T) {
    81  		test := setup()
    82  		start := time.Now()
    83  		const delay = 5 * time.Second
    84  		test.setDeadline(delay)
    85  		var done atomic.Bool
    86  		go func() {
    87  			if err := test.block(); !errors.Is(err, os.ErrDeadlineExceeded) {
    88  				t.Errorf("%v: %v, want os.ErrDeadlineExceeded", test.what, err)
    89  			}
    90  			if got, want := time.Since(start), delay; got != want {
    91  				t.Errorf("%v: returned after %v, want %v", test.what, got, want)
    92  			}
    93  			done.Store(true)
    94  		}()
    95  		synctest.Wait()
    96  		if done.Load() {
    97  			t.Fatalf("%v: unexpectedly returned before unblocking", test.what)
    98  		}
    99  		time.Sleep(delay)
   100  		synctest.Wait()
   101  		if !done.Load() {
   102  			t.Fatalf("%v: did not return after deadline", test.what)
   103  		}
   104  	})
   105  	synctestSubtest(t, "deadline already expired", func(t *testing.T) {
   106  		test := setup()
   107  		test.setDeadline(-1 * time.Second)
   108  		test.unblock()
   109  		synctest.Wait()
   110  		if err := test.block(); !errors.Is(err, os.ErrDeadlineExceeded) {
   111  			t.Errorf("%v: %v, want os.ErrDeadlineExceeded", test.what, err)
   112  		}
   113  	})
   114  	synctestSubtest(t, "reduce deadline after blocking", func(t *testing.T) {
   115  		test := setup()
   116  		test.setDeadline(5 * time.Second)
   117  		var done bool
   118  		go func() {
   119  			if err := test.block(); !errors.Is(err, os.ErrDeadlineExceeded) {
   120  				t.Errorf("%v: %v, want os.ErrDeadlineExceeded", test.what, err)
   121  			}
   122  			done = true
   123  		}()
   124  		synctest.Wait()
   125  		if done {
   126  			t.Fatalf("%v: unexpectedly returned before reducing deadline", test.what)
   127  		}
   128  		test.setDeadline(-1 * time.Second)
   129  		synctest.Wait()
   130  		if !done {
   131  			t.Fatalf("%v: did not return after deadline", test.what)
   132  		}
   133  	})
   134  }
   135  

View as plain text