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  }