Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/writev_test.go

Documentation: net

     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.
     4  
     5  //go:build !js
     6  // +build !js
     7  
     8  package net
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"internal/poll"
    14  	"io"
    15  	"reflect"
    16  	"runtime"
    17  	"sync"
    18  	"testing"
    19  )
    20  
    21  func TestBuffers_read(t *testing.T) {
    22  	const story = "once upon a time in Gopherland ... "
    23  	buffers := Buffers{
    24  		[]byte("once "),
    25  		[]byte("upon "),
    26  		[]byte("a "),
    27  		[]byte("time "),
    28  		[]byte("in "),
    29  		[]byte("Gopherland ... "),
    30  	}
    31  	got, err := io.ReadAll(&buffers)
    32  	if err != nil {
    33  		t.Fatal(err)
    34  	}
    35  	if string(got) != story {
    36  		t.Errorf("read %q; want %q", got, story)
    37  	}
    38  	if len(buffers) != 0 {
    39  		t.Errorf("len(buffers) = %d; want 0", len(buffers))
    40  	}
    41  }
    42  
    43  func TestBuffers_consume(t *testing.T) {
    44  	tests := []struct {
    45  		in      Buffers
    46  		consume int64
    47  		want    Buffers
    48  	}{
    49  		{
    50  			in:      Buffers{[]byte("foo"), []byte("bar")},
    51  			consume: 0,
    52  			want:    Buffers{[]byte("foo"), []byte("bar")},
    53  		},
    54  		{
    55  			in:      Buffers{[]byte("foo"), []byte("bar")},
    56  			consume: 2,
    57  			want:    Buffers{[]byte("o"), []byte("bar")},
    58  		},
    59  		{
    60  			in:      Buffers{[]byte("foo"), []byte("bar")},
    61  			consume: 3,
    62  			want:    Buffers{[]byte("bar")},
    63  		},
    64  		{
    65  			in:      Buffers{[]byte("foo"), []byte("bar")},
    66  			consume: 4,
    67  			want:    Buffers{[]byte("ar")},
    68  		},
    69  		{
    70  			in:      Buffers{nil, nil, nil, []byte("bar")},
    71  			consume: 1,
    72  			want:    Buffers{[]byte("ar")},
    73  		},
    74  		{
    75  			in:      Buffers{nil, nil, nil, []byte("foo")},
    76  			consume: 0,
    77  			want:    Buffers{[]byte("foo")},
    78  		},
    79  		{
    80  			in:      Buffers{nil, nil, nil},
    81  			consume: 0,
    82  			want:    Buffers{},
    83  		},
    84  	}
    85  	for i, tt := range tests {
    86  		in := tt.in
    87  		in.consume(tt.consume)
    88  		if !reflect.DeepEqual(in, tt.want) {
    89  			t.Errorf("%d. after consume(%d) = %+v, want %+v", i, tt.consume, in, tt.want)
    90  		}
    91  	}
    92  }
    93  
    94  func TestBuffers_WriteTo(t *testing.T) {
    95  	for _, name := range []string{"WriteTo", "Copy"} {
    96  		for _, size := range []int{0, 10, 1023, 1024, 1025} {
    97  			t.Run(fmt.Sprintf("%s/%d", name, size), func(t *testing.T) {
    98  				testBuffer_writeTo(t, size, name == "Copy")
    99  			})
   100  		}
   101  	}
   102  }
   103  
   104  func testBuffer_writeTo(t *testing.T, chunks int, useCopy bool) {
   105  	oldHook := poll.TestHookDidWritev
   106  	defer func() { poll.TestHookDidWritev = oldHook }()
   107  	var writeLog struct {
   108  		sync.Mutex
   109  		log []int
   110  	}
   111  	poll.TestHookDidWritev = func(size int) {
   112  		writeLog.Lock()
   113  		writeLog.log = append(writeLog.log, size)
   114  		writeLog.Unlock()
   115  	}
   116  	var want bytes.Buffer
   117  	for i := 0; i < chunks; i++ {
   118  		want.WriteByte(byte(i))
   119  	}
   120  
   121  	withTCPConnPair(t, func(c *TCPConn) error {
   122  		buffers := make(Buffers, chunks)
   123  		for i := range buffers {
   124  			buffers[i] = want.Bytes()[i : i+1]
   125  		}
   126  		var n int64
   127  		var err error
   128  		if useCopy {
   129  			n, err = io.Copy(c, &buffers)
   130  		} else {
   131  			n, err = buffers.WriteTo(c)
   132  		}
   133  		if err != nil {
   134  			return err
   135  		}
   136  		if len(buffers) != 0 {
   137  			return fmt.Errorf("len(buffers) = %d; want 0", len(buffers))
   138  		}
   139  		if n != int64(want.Len()) {
   140  			return fmt.Errorf("Buffers.WriteTo returned %d; want %d", n, want.Len())
   141  		}
   142  		return nil
   143  	}, func(c *TCPConn) error {
   144  		all, err := io.ReadAll(c)
   145  		if !bytes.Equal(all, want.Bytes()) || err != nil {
   146  			return fmt.Errorf("client read %q, %v; want %q, nil", all, err, want.Bytes())
   147  		}
   148  
   149  		writeLog.Lock() // no need to unlock
   150  		var gotSum int
   151  		for _, v := range writeLog.log {
   152  			gotSum += v
   153  		}
   154  
   155  		var wantSum int
   156  		switch runtime.GOOS {
   157  		case "android", "darwin", "ios", "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd":
   158  			var wantMinCalls int
   159  			wantSum = want.Len()
   160  			v := chunks
   161  			for v > 0 {
   162  				wantMinCalls++
   163  				v -= 1024
   164  			}
   165  			if len(writeLog.log) < wantMinCalls {
   166  				t.Errorf("write calls = %v < wanted min %v", len(writeLog.log), wantMinCalls)
   167  			}
   168  		case "windows":
   169  			var wantCalls int
   170  			wantSum = want.Len()
   171  			if wantSum > 0 {
   172  				wantCalls = 1 // windows will always do 1 syscall, unless sending empty buffer
   173  			}
   174  			if len(writeLog.log) != wantCalls {
   175  				t.Errorf("write calls = %v; want %v", len(writeLog.log), wantCalls)
   176  			}
   177  		}
   178  		if gotSum != wantSum {
   179  			t.Errorf("writev call sum  = %v; want %v", gotSum, wantSum)
   180  		}
   181  		return nil
   182  	})
   183  }
   184  
   185  func TestWritevError(t *testing.T) {
   186  	if runtime.GOOS == "windows" {
   187  		t.Skipf("skipping the test: windows does not have problem sending large chunks of data")
   188  	}
   189  
   190  	ln, err := newLocalListener("tcp")
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	defer ln.Close()
   195  
   196  	ch := make(chan Conn, 1)
   197  	go func() {
   198  		defer close(ch)
   199  		c, err := ln.Accept()
   200  		if err != nil {
   201  			t.Error(err)
   202  			return
   203  		}
   204  		ch <- c
   205  	}()
   206  	c1, err := Dial("tcp", ln.Addr().String())
   207  	if err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	defer c1.Close()
   211  	c2 := <-ch
   212  	if c2 == nil {
   213  		t.Fatal("no server side connection")
   214  	}
   215  	c2.Close()
   216  
   217  	// 1 GB of data should be enough to notice the connection is gone.
   218  	// Just a few bytes is not enough.
   219  	// Arrange to reuse the same 1 MB buffer so that we don't allocate much.
   220  	buf := make([]byte, 1<<20)
   221  	buffers := make(Buffers, 1<<10)
   222  	for i := range buffers {
   223  		buffers[i] = buf
   224  	}
   225  	if _, err := buffers.WriteTo(c1); err == nil {
   226  		t.Fatal("Buffers.WriteTo(closed conn) succeeded, want error")
   227  	}
   228  }
   229  

View as plain text