github.com/rafaeltorres324/go/src@v0.0.0-20210519164414-9fdf653a9838/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 // +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, err := newLocalListener("tcp") 190 if err != nil { 191 t.Fatal(err) 192 } 193 defer ln.Close() 194 195 ch := make(chan Conn, 1) 196 go func() { 197 defer close(ch) 198 c, err := ln.Accept() 199 if err != nil { 200 t.Error(err) 201 return 202 } 203 ch <- c 204 }() 205 c1, err := Dial("tcp", ln.Addr().String()) 206 if err != nil { 207 t.Fatal(err) 208 } 209 defer c1.Close() 210 c2 := <-ch 211 if c2 == nil { 212 t.Fatal("no server side connection") 213 } 214 c2.Close() 215 216 // 1 GB of data should be enough to notice the connection is gone. 217 // Just a few bytes is not enough. 218 // Arrange to reuse the same 1 MB buffer so that we don't allocate much. 219 buf := make([]byte, 1<<20) 220 buffers := make(Buffers, 1<<10) 221 for i := range buffers { 222 buffers[i] = buf 223 } 224 if _, err := buffers.WriteTo(c1); err == nil { 225 t.Fatal("Buffers.WriteTo(closed conn) succeeded, want error") 226 } 227 }