Source file src/io/io_test.go

     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 file.
     4  
     5  package io_test
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	. "io"
    12  	"os"
    13  	"strings"
    14  	"sync"
    15  	"sync/atomic"
    16  	"testing"
    17  )
    18  
    19  // A version of bytes.Buffer without ReadFrom and WriteTo
    20  type Buffer struct {
    21  	bytes.Buffer
    22  	ReaderFrom // conflicts with and hides bytes.Buffer's ReaderFrom.
    23  	WriterTo   // conflicts with and hides bytes.Buffer's WriterTo.
    24  }
    25  
    26  // Simple tests, primarily to verify the ReadFrom and WriteTo callouts inside Copy, CopyBuffer and CopyN.
    27  
    28  func TestCopy(t *testing.T) {
    29  	rb := new(Buffer)
    30  	wb := new(Buffer)
    31  	rb.WriteString("hello, world.")
    32  	Copy(wb, rb)
    33  	if wb.String() != "hello, world." {
    34  		t.Errorf("Copy did not work properly")
    35  	}
    36  }
    37  
    38  func TestCopyNegative(t *testing.T) {
    39  	rb := new(Buffer)
    40  	wb := new(Buffer)
    41  	rb.WriteString("hello")
    42  	Copy(wb, &LimitedReader{R: rb, N: -1})
    43  	if wb.String() != "" {
    44  		t.Errorf("Copy on LimitedReader with N<0 copied data")
    45  	}
    46  
    47  	CopyN(wb, rb, -1)
    48  	if wb.String() != "" {
    49  		t.Errorf("CopyN with N<0 copied data")
    50  	}
    51  }
    52  
    53  func TestCopyBuffer(t *testing.T) {
    54  	rb := new(Buffer)
    55  	wb := new(Buffer)
    56  	rb.WriteString("hello, world.")
    57  	CopyBuffer(wb, rb, make([]byte, 1)) // Tiny buffer to keep it honest.
    58  	if wb.String() != "hello, world." {
    59  		t.Errorf("CopyBuffer did not work properly")
    60  	}
    61  }
    62  
    63  func TestCopyBufferNil(t *testing.T) {
    64  	rb := new(Buffer)
    65  	wb := new(Buffer)
    66  	rb.WriteString("hello, world.")
    67  	CopyBuffer(wb, rb, nil) // Should allocate a buffer.
    68  	if wb.String() != "hello, world." {
    69  		t.Errorf("CopyBuffer did not work properly")
    70  	}
    71  }
    72  
    73  func TestCopyReadFrom(t *testing.T) {
    74  	rb := new(Buffer)
    75  	wb := new(bytes.Buffer) // implements ReadFrom.
    76  	rb.WriteString("hello, world.")
    77  	Copy(wb, rb)
    78  	if wb.String() != "hello, world." {
    79  		t.Errorf("Copy did not work properly")
    80  	}
    81  }
    82  
    83  func TestCopyWriteTo(t *testing.T) {
    84  	rb := new(bytes.Buffer) // implements WriteTo.
    85  	wb := new(Buffer)
    86  	rb.WriteString("hello, world.")
    87  	Copy(wb, rb)
    88  	if wb.String() != "hello, world." {
    89  		t.Errorf("Copy did not work properly")
    90  	}
    91  }
    92  
    93  // Version of bytes.Buffer that checks whether WriteTo was called or not
    94  type writeToChecker struct {
    95  	bytes.Buffer
    96  	writeToCalled bool
    97  }
    98  
    99  func (wt *writeToChecker) WriteTo(w Writer) (int64, error) {
   100  	wt.writeToCalled = true
   101  	return wt.Buffer.WriteTo(w)
   102  }
   103  
   104  // It's preferable to choose WriterTo over ReaderFrom, since a WriterTo can issue one large write,
   105  // while the ReaderFrom must read until EOF, potentially allocating when running out of buffer.
   106  // Make sure that we choose WriterTo when both are implemented.
   107  func TestCopyPriority(t *testing.T) {
   108  	rb := new(writeToChecker)
   109  	wb := new(bytes.Buffer)
   110  	rb.WriteString("hello, world.")
   111  	Copy(wb, rb)
   112  	if wb.String() != "hello, world." {
   113  		t.Errorf("Copy did not work properly")
   114  	} else if !rb.writeToCalled {
   115  		t.Errorf("WriteTo was not prioritized over ReadFrom")
   116  	}
   117  }
   118  
   119  type zeroErrReader struct {
   120  	err error
   121  }
   122  
   123  func (r zeroErrReader) Read(p []byte) (int, error) {
   124  	return copy(p, []byte{0}), r.err
   125  }
   126  
   127  type errWriter struct {
   128  	err error
   129  }
   130  
   131  func (w errWriter) Write([]byte) (int, error) {
   132  	return 0, w.err
   133  }
   134  
   135  // In case a Read results in an error with non-zero bytes read, and
   136  // the subsequent Write also results in an error, the error from Write
   137  // is returned, as it is the one that prevented progressing further.
   138  func TestCopyReadErrWriteErr(t *testing.T) {
   139  	er, ew := errors.New("readError"), errors.New("writeError")
   140  	r, w := zeroErrReader{err: er}, errWriter{err: ew}
   141  	n, err := Copy(w, r)
   142  	if n != 0 || err != ew {
   143  		t.Errorf("Copy(zeroErrReader, errWriter) = %d, %v; want 0, writeError", n, err)
   144  	}
   145  }
   146  
   147  func TestCopyN(t *testing.T) {
   148  	rb := new(Buffer)
   149  	wb := new(Buffer)
   150  	rb.WriteString("hello, world.")
   151  	CopyN(wb, rb, 5)
   152  	if wb.String() != "hello" {
   153  		t.Errorf("CopyN did not work properly")
   154  	}
   155  }
   156  
   157  func TestCopyNReadFrom(t *testing.T) {
   158  	rb := new(Buffer)
   159  	wb := new(bytes.Buffer) // implements ReadFrom.
   160  	rb.WriteString("hello")
   161  	CopyN(wb, rb, 5)
   162  	if wb.String() != "hello" {
   163  		t.Errorf("CopyN did not work properly")
   164  	}
   165  }
   166  
   167  func TestCopyNWriteTo(t *testing.T) {
   168  	rb := new(bytes.Buffer) // implements WriteTo.
   169  	wb := new(Buffer)
   170  	rb.WriteString("hello, world.")
   171  	CopyN(wb, rb, 5)
   172  	if wb.String() != "hello" {
   173  		t.Errorf("CopyN did not work properly")
   174  	}
   175  }
   176  
   177  func BenchmarkCopyNSmall(b *testing.B) {
   178  	bs := bytes.Repeat([]byte{0}, 512+1)
   179  	rd := bytes.NewReader(bs)
   180  	buf := new(Buffer)
   181  	b.ResetTimer()
   182  
   183  	for i := 0; i < b.N; i++ {
   184  		CopyN(buf, rd, 512)
   185  		rd.Reset(bs)
   186  	}
   187  }
   188  
   189  func BenchmarkCopyNLarge(b *testing.B) {
   190  	bs := bytes.Repeat([]byte{0}, (32*1024)+1)
   191  	rd := bytes.NewReader(bs)
   192  	buf := new(Buffer)
   193  	b.ResetTimer()
   194  
   195  	for i := 0; i < b.N; i++ {
   196  		CopyN(buf, rd, 32*1024)
   197  		rd.Reset(bs)
   198  	}
   199  }
   200  
   201  type noReadFrom struct {
   202  	w Writer
   203  }
   204  
   205  func (w *noReadFrom) Write(p []byte) (n int, err error) {
   206  	return w.w.Write(p)
   207  }
   208  
   209  type wantedAndErrReader struct{}
   210  
   211  func (wantedAndErrReader) Read(p []byte) (int, error) {
   212  	return len(p), errors.New("wantedAndErrReader error")
   213  }
   214  
   215  func TestCopyNEOF(t *testing.T) {
   216  	// Test that EOF behavior is the same regardless of whether
   217  	// argument to CopyN has ReadFrom.
   218  
   219  	b := new(bytes.Buffer)
   220  
   221  	n, err := CopyN(&noReadFrom{b}, strings.NewReader("foo"), 3)
   222  	if n != 3 || err != nil {
   223  		t.Errorf("CopyN(noReadFrom, foo, 3) = %d, %v; want 3, nil", n, err)
   224  	}
   225  
   226  	n, err = CopyN(&noReadFrom{b}, strings.NewReader("foo"), 4)
   227  	if n != 3 || err != EOF {
   228  		t.Errorf("CopyN(noReadFrom, foo, 4) = %d, %v; want 3, EOF", n, err)
   229  	}
   230  
   231  	n, err = CopyN(b, strings.NewReader("foo"), 3) // b has read from
   232  	if n != 3 || err != nil {
   233  		t.Errorf("CopyN(bytes.Buffer, foo, 3) = %d, %v; want 3, nil", n, err)
   234  	}
   235  
   236  	n, err = CopyN(b, strings.NewReader("foo"), 4) // b has read from
   237  	if n != 3 || err != EOF {
   238  		t.Errorf("CopyN(bytes.Buffer, foo, 4) = %d, %v; want 3, EOF", n, err)
   239  	}
   240  
   241  	n, err = CopyN(b, wantedAndErrReader{}, 5)
   242  	if n != 5 || err != nil {
   243  		t.Errorf("CopyN(bytes.Buffer, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err)
   244  	}
   245  
   246  	n, err = CopyN(&noReadFrom{b}, wantedAndErrReader{}, 5)
   247  	if n != 5 || err != nil {
   248  		t.Errorf("CopyN(noReadFrom, wantedAndErrReader, 5) = %d, %v; want 5, nil", n, err)
   249  	}
   250  }
   251  
   252  func TestReadAtLeast(t *testing.T) {
   253  	var rb bytes.Buffer
   254  	testReadAtLeast(t, &rb)
   255  }
   256  
   257  // A version of bytes.Buffer that returns n > 0, err on Read
   258  // when the input is exhausted.
   259  type dataAndErrorBuffer struct {
   260  	err error
   261  	bytes.Buffer
   262  }
   263  
   264  func (r *dataAndErrorBuffer) Read(p []byte) (n int, err error) {
   265  	n, err = r.Buffer.Read(p)
   266  	if n > 0 && r.Buffer.Len() == 0 && err == nil {
   267  		err = r.err
   268  	}
   269  	return
   270  }
   271  
   272  func TestReadAtLeastWithDataAndEOF(t *testing.T) {
   273  	var rb dataAndErrorBuffer
   274  	rb.err = EOF
   275  	testReadAtLeast(t, &rb)
   276  }
   277  
   278  func TestReadAtLeastWithDataAndError(t *testing.T) {
   279  	var rb dataAndErrorBuffer
   280  	rb.err = fmt.Errorf("fake error")
   281  	testReadAtLeast(t, &rb)
   282  }
   283  
   284  func testReadAtLeast(t *testing.T, rb ReadWriter) {
   285  	rb.Write([]byte("0123"))
   286  	buf := make([]byte, 2)
   287  	n, err := ReadAtLeast(rb, buf, 2)
   288  	if err != nil {
   289  		t.Error(err)
   290  	}
   291  	if n != 2 {
   292  		t.Errorf("expected to have read 2 bytes, got %v", n)
   293  	}
   294  	n, err = ReadAtLeast(rb, buf, 4)
   295  	if err != ErrShortBuffer {
   296  		t.Errorf("expected ErrShortBuffer got %v", err)
   297  	}
   298  	if n != 0 {
   299  		t.Errorf("expected to have read 0 bytes, got %v", n)
   300  	}
   301  	n, err = ReadAtLeast(rb, buf, 1)
   302  	if err != nil {
   303  		t.Error(err)
   304  	}
   305  	if n != 2 {
   306  		t.Errorf("expected to have read 2 bytes, got %v", n)
   307  	}
   308  	n, err = ReadAtLeast(rb, buf, 2)
   309  	if err != EOF {
   310  		t.Errorf("expected EOF, got %v", err)
   311  	}
   312  	if n != 0 {
   313  		t.Errorf("expected to have read 0 bytes, got %v", n)
   314  	}
   315  	rb.Write([]byte("4"))
   316  	n, err = ReadAtLeast(rb, buf, 2)
   317  	want := ErrUnexpectedEOF
   318  	if rb, ok := rb.(*dataAndErrorBuffer); ok && rb.err != EOF {
   319  		want = rb.err
   320  	}
   321  	if err != want {
   322  		t.Errorf("expected %v, got %v", want, err)
   323  	}
   324  	if n != 1 {
   325  		t.Errorf("expected to have read 1 bytes, got %v", n)
   326  	}
   327  }
   328  
   329  func TestTeeReader(t *testing.T) {
   330  	src := []byte("hello, world")
   331  	dst := make([]byte, len(src))
   332  	rb := bytes.NewBuffer(src)
   333  	wb := new(bytes.Buffer)
   334  	r := TeeReader(rb, wb)
   335  	if n, err := ReadFull(r, dst); err != nil || n != len(src) {
   336  		t.Fatalf("ReadFull(r, dst) = %d, %v; want %d, nil", n, err, len(src))
   337  	}
   338  	if !bytes.Equal(dst, src) {
   339  		t.Errorf("bytes read = %q want %q", dst, src)
   340  	}
   341  	if !bytes.Equal(wb.Bytes(), src) {
   342  		t.Errorf("bytes written = %q want %q", wb.Bytes(), src)
   343  	}
   344  	if n, err := r.Read(dst); n != 0 || err != EOF {
   345  		t.Errorf("r.Read at EOF = %d, %v want 0, EOF", n, err)
   346  	}
   347  	rb = bytes.NewBuffer(src)
   348  	pr, pw := Pipe()
   349  	pr.Close()
   350  	r = TeeReader(rb, pw)
   351  	if n, err := ReadFull(r, dst); n != 0 || err != ErrClosedPipe {
   352  		t.Errorf("closed tee: ReadFull(r, dst) = %d, %v; want 0, EPIPE", n, err)
   353  	}
   354  }
   355  
   356  func TestSectionReader_ReadAt(t *testing.T) {
   357  	dat := "a long sample data, 1234567890"
   358  	tests := []struct {
   359  		data   string
   360  		off    int
   361  		n      int
   362  		bufLen int
   363  		at     int
   364  		exp    string
   365  		err    error
   366  	}{
   367  		{data: "", off: 0, n: 10, bufLen: 2, at: 0, exp: "", err: EOF},
   368  		{data: dat, off: 0, n: len(dat), bufLen: 0, at: 0, exp: "", err: nil},
   369  		{data: dat, off: len(dat), n: 1, bufLen: 1, at: 0, exp: "", err: EOF},
   370  		{data: dat, off: 0, n: len(dat) + 2, bufLen: len(dat), at: 0, exp: dat, err: nil},
   371  		{data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 0, exp: dat[:len(dat)/2], err: nil},
   372  		{data: dat, off: 0, n: len(dat), bufLen: len(dat), at: 0, exp: dat, err: nil},
   373  		{data: dat, off: 0, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[2 : 2+len(dat)/2], err: nil},
   374  		{data: dat, off: 3, n: len(dat), bufLen: len(dat) / 2, at: 2, exp: dat[5 : 5+len(dat)/2], err: nil},
   375  		{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil},
   376  		{data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF},
   377  		{data: dat, off: 0, n: 0, bufLen: 0, at: -1, exp: "", err: EOF},
   378  		{data: dat, off: 0, n: 0, bufLen: 0, at: 1, exp: "", err: EOF},
   379  	}
   380  	for i, tt := range tests {
   381  		r := strings.NewReader(tt.data)
   382  		s := NewSectionReader(r, int64(tt.off), int64(tt.n))
   383  		buf := make([]byte, tt.bufLen)
   384  		if n, err := s.ReadAt(buf, int64(tt.at)); n != len(tt.exp) || string(buf[:n]) != tt.exp || err != tt.err {
   385  			t.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, tt.at, buf[:n], err, tt.exp, tt.err)
   386  		}
   387  		if _r, off, n := s.Outer(); _r != r || off != int64(tt.off) || n != int64(tt.n) {
   388  			t.Fatalf("%d: Outer() = %v, %d, %d; expected %v, %d, %d", i, _r, off, n, r, tt.off, tt.n)
   389  		}
   390  	}
   391  }
   392  
   393  func TestSectionReader_Seek(t *testing.T) {
   394  	// Verifies that NewSectionReader's Seeker behaves like bytes.NewReader (which is like strings.NewReader)
   395  	br := bytes.NewReader([]byte("foo"))
   396  	sr := NewSectionReader(br, 0, int64(len("foo")))
   397  
   398  	for _, whence := range []int{SeekStart, SeekCurrent, SeekEnd} {
   399  		for offset := int64(-3); offset <= 4; offset++ {
   400  			brOff, brErr := br.Seek(offset, whence)
   401  			srOff, srErr := sr.Seek(offset, whence)
   402  			if (brErr != nil) != (srErr != nil) || brOff != srOff {
   403  				t.Errorf("For whence %d, offset %d: bytes.Reader.Seek = (%v, %v) != SectionReader.Seek = (%v, %v)",
   404  					whence, offset, brOff, brErr, srErr, srOff)
   405  			}
   406  		}
   407  	}
   408  
   409  	// And verify we can just seek past the end and get an EOF
   410  	got, err := sr.Seek(100, SeekStart)
   411  	if err != nil || got != 100 {
   412  		t.Errorf("Seek = %v, %v; want 100, nil", got, err)
   413  	}
   414  
   415  	n, err := sr.Read(make([]byte, 10))
   416  	if n != 0 || err != EOF {
   417  		t.Errorf("Read = %v, %v; want 0, EOF", n, err)
   418  	}
   419  }
   420  
   421  func TestSectionReader_Size(t *testing.T) {
   422  	tests := []struct {
   423  		data string
   424  		want int64
   425  	}{
   426  		{"a long sample data, 1234567890", 30},
   427  		{"", 0},
   428  	}
   429  
   430  	for _, tt := range tests {
   431  		r := strings.NewReader(tt.data)
   432  		sr := NewSectionReader(r, 0, int64(len(tt.data)))
   433  		if got := sr.Size(); got != tt.want {
   434  			t.Errorf("Size = %v; want %v", got, tt.want)
   435  		}
   436  	}
   437  }
   438  
   439  func TestSectionReader_Max(t *testing.T) {
   440  	r := strings.NewReader("abcdef")
   441  	const maxint64 = 1<<63 - 1
   442  	sr := NewSectionReader(r, 3, maxint64)
   443  	n, err := sr.Read(make([]byte, 3))
   444  	if n != 3 || err != nil {
   445  		t.Errorf("Read = %v %v, want 3, nil", n, err)
   446  	}
   447  	n, err = sr.Read(make([]byte, 3))
   448  	if n != 0 || err != EOF {
   449  		t.Errorf("Read = %v, %v, want 0, EOF", n, err)
   450  	}
   451  	if _r, off, n := sr.Outer(); _r != r || off != 3 || n != maxint64 {
   452  		t.Fatalf("Outer = %v, %d, %d; expected %v, %d, %d", _r, off, n, r, 3, int64(maxint64))
   453  	}
   454  }
   455  
   456  // largeWriter returns an invalid count that is larger than the number
   457  // of bytes provided (issue 39978).
   458  type largeWriter struct {
   459  	err error
   460  }
   461  
   462  func (w largeWriter) Write(p []byte) (int, error) {
   463  	return len(p) + 1, w.err
   464  }
   465  
   466  func TestCopyLargeWriter(t *testing.T) {
   467  	want := ErrInvalidWrite
   468  	rb := new(Buffer)
   469  	wb := largeWriter{}
   470  	rb.WriteString("hello, world.")
   471  	if _, err := Copy(wb, rb); err != want {
   472  		t.Errorf("Copy error: got %v, want %v", err, want)
   473  	}
   474  
   475  	want = errors.New("largeWriterError")
   476  	rb = new(Buffer)
   477  	wb = largeWriter{err: want}
   478  	rb.WriteString("hello, world.")
   479  	if _, err := Copy(wb, rb); err != want {
   480  		t.Errorf("Copy error: got %v, want %v", err, want)
   481  	}
   482  }
   483  
   484  func TestNopCloserWriterToForwarding(t *testing.T) {
   485  	for _, tc := range [...]struct {
   486  		Name string
   487  		r    Reader
   488  	}{
   489  		{"not a WriterTo", Reader(nil)},
   490  		{"a WriterTo", struct {
   491  			Reader
   492  			WriterTo
   493  		}{}},
   494  	} {
   495  		nc := NopCloser(tc.r)
   496  
   497  		_, expected := tc.r.(WriterTo)
   498  		_, got := nc.(WriterTo)
   499  		if expected != got {
   500  			t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected)
   501  		}
   502  	}
   503  }
   504  
   505  func TestOffsetWriter_Seek(t *testing.T) {
   506  	tmpfilename := "TestOffsetWriter_Seek"
   507  	tmpfile, err := os.CreateTemp(t.TempDir(), tmpfilename)
   508  	if err != nil || tmpfile == nil {
   509  		t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err)
   510  	}
   511  	defer tmpfile.Close()
   512  	w := NewOffsetWriter(tmpfile, 0)
   513  
   514  	// Should throw error errWhence if whence is not valid
   515  	t.Run("errWhence", func(t *testing.T) {
   516  		for _, whence := range []int{-3, -2, -1, 3, 4, 5} {
   517  			var offset int64 = 0
   518  			gotOff, gotErr := w.Seek(offset, whence)
   519  			if gotOff != 0 || gotErr != ErrWhence {
   520  				t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)",
   521  					whence, offset, gotOff, gotErr, 0, ErrWhence)
   522  			}
   523  		}
   524  	})
   525  
   526  	// Should throw error errOffset if offset is negative
   527  	t.Run("errOffset", func(t *testing.T) {
   528  		for _, whence := range []int{SeekStart, SeekCurrent} {
   529  			for offset := int64(-3); offset < 0; offset++ {
   530  				gotOff, gotErr := w.Seek(offset, whence)
   531  				if gotOff != 0 || gotErr != ErrOffset {
   532  					t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)",
   533  						whence, offset, gotOff, gotErr, 0, ErrOffset)
   534  				}
   535  			}
   536  		}
   537  	})
   538  
   539  	// Normal tests
   540  	t.Run("normal", func(t *testing.T) {
   541  		tests := []struct {
   542  			offset    int64
   543  			whence    int
   544  			returnOff int64
   545  		}{
   546  			// keep in order
   547  			{whence: SeekStart, offset: 1, returnOff: 1},
   548  			{whence: SeekStart, offset: 2, returnOff: 2},
   549  			{whence: SeekStart, offset: 3, returnOff: 3},
   550  			{whence: SeekCurrent, offset: 1, returnOff: 4},
   551  			{whence: SeekCurrent, offset: 2, returnOff: 6},
   552  			{whence: SeekCurrent, offset: 3, returnOff: 9},
   553  		}
   554  		for idx, tt := range tests {
   555  			gotOff, gotErr := w.Seek(tt.offset, tt.whence)
   556  			if gotOff != tt.returnOff || gotErr != nil {
   557  				t.Errorf("%d:: For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, <nil>)",
   558  					idx+1, tt.whence, tt.offset, gotOff, gotErr, tt.returnOff)
   559  			}
   560  		}
   561  	})
   562  }
   563  
   564  func TestOffsetWriter_WriteAt(t *testing.T) {
   565  	const content = "0123456789ABCDEF"
   566  	contentSize := int64(len(content))
   567  	tmpdir, err := os.MkdirTemp(t.TempDir(), "TestOffsetWriter_WriteAt")
   568  	if err != nil {
   569  		t.Fatal(err)
   570  	}
   571  
   572  	work := func(off, at int64) {
   573  		position := fmt.Sprintf("off_%d_at_%d", off, at)
   574  		tmpfile, err := os.CreateTemp(tmpdir, position)
   575  		if err != nil || tmpfile == nil {
   576  			t.Fatalf("CreateTemp(%s) failed: %v", position, err)
   577  		}
   578  		defer tmpfile.Close()
   579  
   580  		var writeN int64
   581  		var wg sync.WaitGroup
   582  		// Concurrent writes, one byte at a time
   583  		for step, value := range []byte(content) {
   584  			wg.Add(1)
   585  			go func(wg *sync.WaitGroup, tmpfile *os.File, value byte, off, at int64, step int) {
   586  				defer wg.Done()
   587  
   588  				w := NewOffsetWriter(tmpfile, off)
   589  				n, e := w.WriteAt([]byte{value}, at+int64(step))
   590  				if e != nil {
   591  					t.Errorf("WriteAt failed. off: %d, at: %d, step: %d\n error: %v", off, at, step, e)
   592  				}
   593  				atomic.AddInt64(&writeN, int64(n))
   594  			}(&wg, tmpfile, value, off, at, step)
   595  		}
   596  		wg.Wait()
   597  
   598  		// Read one more byte to reach EOF
   599  		buf := make([]byte, contentSize+1)
   600  		readN, err := tmpfile.ReadAt(buf, off+at)
   601  		if err != EOF {
   602  			t.Fatalf("ReadAt failed: %v", err)
   603  		}
   604  		readContent := string(buf[:contentSize])
   605  		if writeN != int64(readN) || writeN != contentSize || readContent != content {
   606  			t.Fatalf("%s:: WriteAt(%s, %d) error. \ngot n: %v, content: %s \nexpected n: %v, content: %v",
   607  				position, content, at, readN, readContent, contentSize, content)
   608  		}
   609  	}
   610  	for off := int64(0); off < 2; off++ {
   611  		for at := int64(0); at < 2; at++ {
   612  			work(off, at)
   613  		}
   614  	}
   615  }
   616  
   617  func TestWriteAt_PositionPriorToBase(t *testing.T) {
   618  	tmpdir := t.TempDir()
   619  	tmpfilename := "TestOffsetWriter_WriteAt"
   620  	tmpfile, err := os.CreateTemp(tmpdir, tmpfilename)
   621  	if err != nil {
   622  		t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err)
   623  	}
   624  	defer tmpfile.Close()
   625  
   626  	// start writing position in OffsetWriter
   627  	offset := int64(10)
   628  	// position we want to write to the tmpfile
   629  	at := int64(-1)
   630  	w := NewOffsetWriter(tmpfile, offset)
   631  	_, e := w.WriteAt([]byte("hello"), at)
   632  	if e == nil {
   633  		t.Errorf("error expected to be not nil")
   634  	}
   635  }
   636  
   637  func TestOffsetWriter_Write(t *testing.T) {
   638  	const content = "0123456789ABCDEF"
   639  	contentSize := len(content)
   640  	tmpdir := t.TempDir()
   641  
   642  	makeOffsetWriter := func(name string) (*OffsetWriter, *os.File) {
   643  		tmpfilename := "TestOffsetWriter_Write_" + name
   644  		tmpfile, err := os.CreateTemp(tmpdir, tmpfilename)
   645  		if err != nil || tmpfile == nil {
   646  			t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err)
   647  		}
   648  		return NewOffsetWriter(tmpfile, 0), tmpfile
   649  	}
   650  	checkContent := func(name string, f *os.File) {
   651  		// Read one more byte to reach EOF
   652  		buf := make([]byte, contentSize+1)
   653  		readN, err := f.ReadAt(buf, 0)
   654  		if err != EOF {
   655  			t.Fatalf("ReadAt failed, err: %v", err)
   656  		}
   657  		readContent := string(buf[:contentSize])
   658  		if readN != contentSize || readContent != content {
   659  			t.Fatalf("%s error. \ngot n: %v, content: %s \nexpected n: %v, content: %v",
   660  				name, readN, readContent, contentSize, content)
   661  		}
   662  	}
   663  
   664  	var name string
   665  	name = "Write"
   666  	t.Run(name, func(t *testing.T) {
   667  		// Write directly (off: 0, at: 0)
   668  		// Write content to file
   669  		w, f := makeOffsetWriter(name)
   670  		defer f.Close()
   671  		for _, value := range []byte(content) {
   672  			n, err := w.Write([]byte{value})
   673  			if err != nil {
   674  				t.Fatalf("Write failed, n: %d, err: %v", n, err)
   675  			}
   676  		}
   677  		checkContent(name, f)
   678  
   679  		// Copy -> Write
   680  		// Copy file f to file f2
   681  		name = "Copy"
   682  		w2, f2 := makeOffsetWriter(name)
   683  		defer f2.Close()
   684  		Copy(w2, f)
   685  		checkContent(name, f2)
   686  	})
   687  
   688  	// Copy -> WriteTo -> Write
   689  	// Note: strings.Reader implements the io.WriterTo interface.
   690  	name = "Write_Of_Copy_WriteTo"
   691  	t.Run(name, func(t *testing.T) {
   692  		w, f := makeOffsetWriter(name)
   693  		defer f.Close()
   694  		Copy(w, strings.NewReader(content))
   695  		checkContent(name, f)
   696  	})
   697  }
   698  

View as plain text