Source file src/testing/synctest/example_test.go

     1  // Copyright 2024 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 synctest_test
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"context"
    11  	"io"
    12  	"net"
    13  	"net/http"
    14  	"strings"
    15  	"testing"
    16  	"testing/synctest"
    17  	"time"
    18  )
    19  
    20  // Keep the following tests in sync with the package documentation.
    21  
    22  func TestTime(t *testing.T) {
    23  	synctest.Test(t, func(t *testing.T) {
    24  		start := time.Now() // always midnight UTC 2001-01-01
    25  		go func() {
    26  			time.Sleep(1 * time.Nanosecond)
    27  			t.Log(time.Since(start)) // always logs "1ns"
    28  		}()
    29  		time.Sleep(2 * time.Nanosecond) // the AfterFunc will run before this Sleep returns
    30  		t.Log(time.Since(start))        // always logs "2ns"
    31  	})
    32  }
    33  
    34  func TestWait(t *testing.T) {
    35  	synctest.Test(t, func(t *testing.T) {
    36  		done := false
    37  		go func() {
    38  			done = true
    39  		}()
    40  		// Wait will block until the goroutine above has finished.
    41  		synctest.Wait()
    42  		t.Log(done) // always logs "true"
    43  	})
    44  }
    45  
    46  func TestContextAfterFunc(t *testing.T) {
    47  	synctest.Test(t, func(t *testing.T) {
    48  		// Create a context.Context which can be canceled.
    49  		ctx, cancel := context.WithCancel(t.Context())
    50  
    51  		// context.AfterFunc registers a function to be called
    52  		// when a context is canceled.
    53  		afterFuncCalled := false
    54  		context.AfterFunc(ctx, func() {
    55  			afterFuncCalled = true
    56  		})
    57  
    58  		// The context has not been canceled, so the AfterFunc is not called.
    59  		synctest.Wait()
    60  		if afterFuncCalled {
    61  			t.Fatalf("before context is canceled: AfterFunc called")
    62  		}
    63  
    64  		// Cancel the context and wait for the AfterFunc to finish executing.
    65  		// Verify that the AfterFunc ran.
    66  		cancel()
    67  		synctest.Wait()
    68  		if !afterFuncCalled {
    69  			t.Fatalf("before context is canceled: AfterFunc not called")
    70  		}
    71  	})
    72  }
    73  
    74  func TestContextWithTimeout(t *testing.T) {
    75  	synctest.Test(t, func(t *testing.T) {
    76  		// Create a context.Context which is canceled after a timeout.
    77  		const timeout = 5 * time.Second
    78  		ctx, cancel := context.WithTimeout(t.Context(), timeout)
    79  		defer cancel()
    80  
    81  		// Wait just less than the timeout.
    82  		time.Sleep(timeout - time.Nanosecond)
    83  		synctest.Wait()
    84  		if err := ctx.Err(); err != nil {
    85  			t.Fatalf("before timeout: ctx.Err() = %v, want nil\n", err)
    86  		}
    87  
    88  		// Wait the rest of the way until the timeout.
    89  		time.Sleep(time.Nanosecond)
    90  		synctest.Wait()
    91  		if err := ctx.Err(); err != context.DeadlineExceeded {
    92  			t.Fatalf("after timeout: ctx.Err() = %v, want DeadlineExceeded\n", err)
    93  		}
    94  	})
    95  }
    96  
    97  func TestHTTPTransport100Continue(t *testing.T) {
    98  	synctest.Test(t, func(*testing.T) {
    99  		// Create an in-process fake network connection.
   100  		// We cannot use a loopback network connection for this test,
   101  		// because goroutines blocked on network I/O prevent a synctest
   102  		// bubble from becoming idle.
   103  		srvConn, cliConn := net.Pipe()
   104  		defer cliConn.Close()
   105  		defer srvConn.Close()
   106  
   107  		tr := &http.Transport{
   108  			// Use the fake network connection created above.
   109  			DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
   110  				return cliConn, nil
   111  			},
   112  			// Enable "Expect: 100-continue" handling.
   113  			ExpectContinueTimeout: 5 * time.Second,
   114  		}
   115  
   116  		// Send a request with the "Expect: 100-continue" header set.
   117  		// Send it in a new goroutine, since it won't complete until the end of the test.
   118  		body := "request body"
   119  		go func() {
   120  			req, _ := http.NewRequest("PUT", "http://test.tld/", strings.NewReader(body))
   121  			req.Header.Set("Expect", "100-continue")
   122  			resp, err := tr.RoundTrip(req)
   123  			if err != nil {
   124  				t.Errorf("RoundTrip: unexpected error %v\n", err)
   125  			} else {
   126  				resp.Body.Close()
   127  			}
   128  		}()
   129  
   130  		// Read the request headers sent by the client.
   131  		req, err := http.ReadRequest(bufio.NewReader(srvConn))
   132  		if err != nil {
   133  			t.Fatalf("ReadRequest: %v\n", err)
   134  		}
   135  
   136  		// Start a new goroutine copying the body sent by the client into a buffer.
   137  		// Wait for all goroutines in the bubble to block and verify that we haven't
   138  		// read anything from the client yet.
   139  		var gotBody bytes.Buffer
   140  		go io.Copy(&gotBody, req.Body)
   141  		synctest.Wait()
   142  		if got, want := gotBody.String(), ""; got != want {
   143  			t.Fatalf("before sending 100 Continue, read body: %q, want %q\n", got, want)
   144  		}
   145  
   146  		// Write a "100 Continue" response to the client and verify that
   147  		// it sends the request body.
   148  		srvConn.Write([]byte("HTTP/1.1 100 Continue\r\n\r\n"))
   149  		synctest.Wait()
   150  		if got, want := gotBody.String(), body; got != want {
   151  			t.Fatalf("after sending 100 Continue, read body: %q, want %q\n", got, want)
   152  		}
   153  
   154  		// Finish up by sending the "200 OK" response to conclude the request.
   155  		srvConn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
   156  
   157  		// We started several goroutines during the test.
   158  		// The synctest.Test call will wait for all of them to exit before returning.
   159  	})
   160  }
   161  

View as plain text