github.com/dara-project/godist@v0.0.0-20200823115410-e0c80c8f0c78/src/net/writev_test.go (about)

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