Source file src/net/writev_test.go

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

View as plain text