lab.nexedi.com/kirr/go123@v0.0.0-20240207185015-8299741fa871/xio/pipe_test.go (about) 1 // Copyright 2009 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-go file. 4 5 package xio_test 6 7 import ( 8 "bytes" 9 "context" 10 "fmt" 11 "io" 12 . "lab.nexedi.com/kirr/go123/xio" 13 "sort" 14 "strings" 15 "testing" 16 "time" 17 ) 18 19 var bg = context.Background() 20 21 func checkWrite(t *testing.T, w Writer, data []byte, c chan int) { 22 n, err := w.Write(bg, data) 23 if err != nil { 24 t.Errorf("write: %v", err) 25 } 26 if n != len(data) { 27 t.Errorf("short write: %d != %d", n, len(data)) 28 } 29 c <- 0 30 } 31 32 // Test a single read/write pair. 33 func TestPipe1(t *testing.T) { 34 c := make(chan int) 35 r, w := Pipe() 36 var buf = make([]byte, 64) 37 go checkWrite(t, w, []byte("hello, world"), c) 38 n, err := r.Read(bg, buf) 39 if err != nil { 40 t.Errorf("read: %v", err) 41 } else if n != 12 || string(buf[0:12]) != "hello, world" { 42 t.Errorf("bad read: got %q", buf[0:n]) 43 } 44 <-c 45 r.Close() 46 w.Close() 47 } 48 49 func reader(t *testing.T, r Reader, c chan int) { 50 var buf = make([]byte, 64) 51 for { 52 n, err := r.Read(bg, buf) 53 if err == io.EOF { 54 c <- 0 55 break 56 } 57 if err != nil { 58 t.Errorf("read: %v", err) 59 } 60 c <- n 61 } 62 } 63 64 // Test a sequence of read/write pairs. 65 func TestPipe2(t *testing.T) { 66 c := make(chan int) 67 r, w := Pipe() 68 go reader(t, r, c) 69 var buf = make([]byte, 64) 70 for i := 0; i < 5; i++ { 71 p := buf[0 : 5+i*10] 72 n, err := w.Write(bg, p) 73 if n != len(p) { 74 t.Errorf("wrote %d, got %d", len(p), n) 75 } 76 if err != nil { 77 t.Errorf("write: %v", err) 78 } 79 nn := <-c 80 if nn != n { 81 t.Errorf("wrote %d, read got %d", n, nn) 82 } 83 } 84 w.Close() 85 nn := <-c 86 if nn != 0 { 87 t.Errorf("final read got %d", nn) 88 } 89 } 90 91 type pipeReturn struct { 92 n int 93 err error 94 } 95 96 // Test a large write that requires multiple reads to satisfy. 97 func writer(w io.WriteCloser, buf []byte, c chan pipeReturn) { 98 n, err := w.Write(buf) 99 w.Close() 100 c <- pipeReturn{n, err} 101 } 102 103 func TestPipe3(t *testing.T) { 104 c := make(chan pipeReturn) 105 r, w := Pipe() 106 var wdat = make([]byte, 128) 107 for i := 0; i < len(wdat); i++ { 108 wdat[i] = byte(i) 109 } 110 go writer(BindCtxWC(w, bg), wdat, c) 111 var rdat = make([]byte, 1024) 112 tot := 0 113 for n := 1; n <= 256; n *= 2 { 114 nn, err := r.Read(bg, rdat[tot : tot+n]) 115 if err != nil && err != io.EOF { 116 t.Fatalf("read: %v", err) 117 } 118 119 // only final two reads should be short - 1 byte, then 0 120 expect := n 121 if n == 128 { 122 expect = 1 123 } else if n == 256 { 124 expect = 0 125 if err != io.EOF { 126 t.Fatalf("read at end: %v", err) 127 } 128 } 129 if nn != expect { 130 t.Fatalf("read %d, expected %d, got %d", n, expect, nn) 131 } 132 tot += nn 133 } 134 pr := <-c 135 if pr.n != 128 || pr.err != nil { 136 t.Fatalf("write 128: %d, %v", pr.n, pr.err) 137 } 138 if tot != 128 { 139 t.Fatalf("total read %d != 128", tot) 140 } 141 for i := 0; i < 128; i++ { 142 if rdat[i] != byte(i) { 143 t.Fatalf("rdat[%d] = %d", i, rdat[i]) 144 } 145 } 146 } 147 148 // Test read after/before writer close. 149 150 type closer interface { 151 CloseWithError(error) error 152 Close() error 153 } 154 155 type pipeTest struct { 156 async bool 157 err error 158 closeWithError bool 159 } 160 161 func (p pipeTest) String() string { 162 return fmt.Sprintf("async=%v err=%v closeWithError=%v", p.async, p.err, p.closeWithError) 163 } 164 165 var pipeTests = []pipeTest{ 166 {true, nil, false}, 167 {true, nil, true}, 168 {true, io.ErrShortWrite, true}, 169 {false, nil, false}, 170 {false, nil, true}, 171 {false, io.ErrShortWrite, true}, 172 } 173 174 func delayClose(t *testing.T, cl closer, ch chan int, tt pipeTest) { 175 time.Sleep(1 * time.Millisecond) 176 var err error 177 if tt.closeWithError { 178 err = cl.CloseWithError(tt.err) 179 } else { 180 err = cl.Close() 181 } 182 if err != nil { 183 t.Errorf("delayClose: %v", err) 184 } 185 ch <- 0 186 } 187 188 func TestPipeReadClose(t *testing.T) { 189 for _, tt := range pipeTests { 190 c := make(chan int, 1) 191 r, w := Pipe() 192 if tt.async { 193 go delayClose(t, w, c, tt) 194 } else { 195 delayClose(t, w, c, tt) 196 } 197 var buf = make([]byte, 64) 198 n, err := r.Read(bg, buf) 199 <-c 200 want := tt.err 201 if want == nil { 202 want = io.EOF 203 } 204 if err != want { 205 t.Errorf("read from closed pipe: %v want %v", err, want) 206 } 207 if n != 0 { 208 t.Errorf("read on closed pipe returned %d", n) 209 } 210 if err = r.Close(); err != nil { 211 t.Errorf("r.Close: %v", err) 212 } 213 } 214 } 215 216 // Test close on Read side during Read. 217 func TestPipeReadClose2(t *testing.T) { 218 c := make(chan int, 1) 219 r, _ := Pipe() 220 go delayClose(t, r, c, pipeTest{}) 221 n, err := r.Read(bg, make([]byte, 64)) 222 <-c 223 if n != 0 || err != io.ErrClosedPipe { 224 t.Errorf("read from closed pipe: %v, %v want %v, %v", n, err, 0, io.ErrClosedPipe) 225 } 226 } 227 228 // Test write after/before reader close. 229 230 func TestPipeWriteClose(t *testing.T) { 231 for _, tt := range pipeTests { 232 c := make(chan int, 1) 233 r, w := Pipe() 234 if tt.async { 235 go delayClose(t, r, c, tt) 236 } else { 237 delayClose(t, r, c, tt) 238 } 239 n, err := io.WriteString(BindCtxW(w, bg), "hello, world") 240 <-c 241 expect := tt.err 242 if expect == nil { 243 expect = io.ErrClosedPipe 244 } 245 if err != expect { 246 t.Errorf("write on closed pipe: %v want %v", err, expect) 247 } 248 if n != 0 { 249 t.Errorf("write on closed pipe returned %d", n) 250 } 251 if err = w.Close(); err != nil { 252 t.Errorf("w.Close: %v", err) 253 } 254 } 255 } 256 257 // Test close on Write side during Write. 258 func TestPipeWriteClose2(t *testing.T) { 259 c := make(chan int, 1) 260 _, w := Pipe() 261 go delayClose(t, w, c, pipeTest{}) 262 n, err := w.Write(bg, make([]byte, 64)) 263 <-c 264 if n != 0 || err != io.ErrClosedPipe { 265 t.Errorf("write to closed pipe: %v, %v want %v, %v", n, err, 0, io.ErrClosedPipe) 266 } 267 } 268 269 func TestWriteEmpty(t *testing.T) { 270 r, w := Pipe() 271 go func() { 272 w.Write(bg, []byte{}) 273 w.Close() 274 }() 275 var b [2]byte 276 io.ReadFull(BindCtxR(r, bg), b[0:2]) 277 r.Close() 278 } 279 280 func TestWriteNil(t *testing.T) { 281 r, w := Pipe() 282 go func() { 283 w.Write(bg, nil) 284 w.Close() 285 }() 286 var b [2]byte 287 io.ReadFull(BindCtxR(r, bg), b[0:2]) 288 r.Close() 289 } 290 291 func TestWriteAfterWriterClose(t *testing.T) { 292 r, w := Pipe() 293 294 done := make(chan bool) 295 var writeErr error 296 go func() { 297 _, err := w.Write(bg, []byte("hello")) 298 if err != nil { 299 t.Errorf("got error: %q; expected none", err) 300 } 301 w.Close() 302 _, writeErr = w.Write(bg, []byte("world")) 303 done <- true 304 }() 305 306 buf := make([]byte, 100) 307 var result string 308 n, err := io.ReadFull(BindCtxR(r, bg), buf) 309 if err != nil && err != io.ErrUnexpectedEOF { 310 t.Fatalf("got: %q; want: %q", err, io.ErrUnexpectedEOF) 311 } 312 result = string(buf[0:n]) 313 <-done 314 315 if result != "hello" { 316 t.Errorf("got: %q; want: %q", result, "hello") 317 } 318 if writeErr != io.ErrClosedPipe { 319 t.Errorf("got: %q; want: %q", writeErr, io.ErrClosedPipe) 320 } 321 } 322 323 func TestPipeCloseError(t *testing.T) { 324 type testError1 struct{ error } 325 type testError2 struct{ error } 326 327 r, w := Pipe() 328 r.CloseWithError(testError1{}) 329 if _, err := w.Write(bg, nil); err != (testError1{}) { 330 t.Errorf("Write error: got %T, want testError1", err) 331 } 332 r.CloseWithError(testError2{}) 333 if _, err := w.Write(bg, nil); err != (testError1{}) { 334 t.Errorf("Write error: got %T, want testError1", err) 335 } 336 337 r, w = Pipe() 338 w.CloseWithError(testError1{}) 339 if _, err := r.Read(bg, nil); err != (testError1{}) { 340 t.Errorf("Read error: got %T, want testError1", err) 341 } 342 w.CloseWithError(testError2{}) 343 if _, err := r.Read(bg, nil); err != (testError1{}) { 344 t.Errorf("Read error: got %T, want testError1", err) 345 } 346 } 347 348 func TestPipeConcurrent(t *testing.T) { 349 const ( 350 input = "0123456789abcdef" 351 count = 8 352 readSize = 2 353 ) 354 355 t.Run("Write", func(t *testing.T) { 356 r, w := Pipe() 357 358 for i := 0; i < count; i++ { 359 go func() { 360 time.Sleep(time.Millisecond) // Increase probability of race 361 if n, err := w.Write(bg, []byte(input)); n != len(input) || err != nil { 362 t.Errorf("Write() = (%d, %v); want (%d, nil)", n, err, len(input)) 363 } 364 }() 365 } 366 367 buf := make([]byte, count*len(input)) 368 for i := 0; i < len(buf); i += readSize { 369 if n, err := r.Read(bg, buf[i : i+readSize]); n != readSize || err != nil { 370 t.Errorf("Read() = (%d, %v); want (%d, nil)", n, err, readSize) 371 } 372 } 373 374 // Since each Write is fully gated, if multiple Read calls were needed, 375 // the contents of Write should still appear together in the output. 376 got := string(buf) 377 want := strings.Repeat(input, count) 378 if got != want { 379 t.Errorf("got: %q; want: %q", got, want) 380 } 381 }) 382 383 t.Run("Read", func(t *testing.T) { 384 r, w := Pipe() 385 386 c := make(chan []byte, count*len(input)/readSize) 387 for i := 0; i < cap(c); i++ { 388 go func() { 389 time.Sleep(time.Millisecond) // Increase probability of race 390 buf := make([]byte, readSize) 391 if n, err := r.Read(bg, buf); n != readSize || err != nil { 392 t.Errorf("Read() = (%d, %v); want (%d, nil)", n, err, readSize) 393 } 394 c <- buf 395 }() 396 } 397 398 for i := 0; i < count; i++ { 399 if n, err := w.Write(bg, []byte(input)); n != len(input) || err != nil { 400 t.Errorf("Write() = (%d, %v); want (%d, nil)", n, err, len(input)) 401 } 402 } 403 404 // Since each read is independent, the only guarantee about the output 405 // is that it is a permutation of the input in readSized groups. 406 got := make([]byte, 0, count*len(input)) 407 for i := 0; i < cap(c); i++ { 408 got = append(got, (<-c)...) 409 } 410 got = sortBytesInGroups(got, readSize) 411 want := bytes.Repeat([]byte(input), count) 412 want = sortBytesInGroups(want, readSize) 413 if string(got) != string(want) { 414 t.Errorf("got: %q; want: %q", got, want) 415 } 416 }) 417 } 418 419 func sortBytesInGroups(b []byte, n int) []byte { 420 var groups [][]byte 421 for len(b) > 0 { 422 groups = append(groups, b[:n]) 423 b = b[n:] 424 } 425 sort.Slice(groups, func(i, j int) bool { return bytes.Compare(groups[i], groups[j]) < 0 }) 426 return bytes.Join(groups, nil) 427 } 428 429 430 // Verify that .Read and .Write handle cancellation. 431 func TestPipeCancel(t *testing.T) { 432 buf := make([]byte, 64) 433 r, _ := Pipe() 434 ctx, cancel := context.WithCancel(bg) 435 436 go func() { 437 time.Sleep(1*time.Millisecond) 438 cancel() 439 }() 440 441 n, err := r.Read(ctx, buf) 442 if eok := context.Canceled; !(n == 0 && err == eok) { 443 t.Errorf("read: got (%v, %v) ; want (%v, %v)", n, err, 0, eok) 444 } 445 446 447 _, w := Pipe() 448 ctx, cancel = context.WithTimeout(bg, 1*time.Millisecond) 449 450 n, err = w.Write(ctx, buf) 451 if eok := context.DeadlineExceeded; !(n == 0 && err == eok) { 452 t.Errorf("write: got (%v, %v) ; want (%v, %v)", n, err, 0, eok) 453 } 454 455 }