...

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