github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/io/io_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 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  	}
   388  }
   389  
   390  func TestSectionReader_Seek(t *testing.T) {
   391  	// Verifies that NewSectionReader's Seeker behaves like bytes.NewReader (which is like strings.NewReader)
   392  	br := bytes.NewReader([]byte("foo"))
   393  	sr := NewSectionReader(br, 0, int64(len("foo")))
   394  
   395  	for _, whence := range []int{SeekStart, SeekCurrent, SeekEnd} {
   396  		for offset := int64(-3); offset <= 4; offset++ {
   397  			brOff, brErr := br.Seek(offset, whence)
   398  			srOff, srErr := sr.Seek(offset, whence)
   399  			if (brErr != nil) != (srErr != nil) || brOff != srOff {
   400  				t.Errorf("For whence %d, offset %d: bytes.Reader.Seek = (%v, %v) != SectionReader.Seek = (%v, %v)",
   401  					whence, offset, brOff, brErr, srErr, srOff)
   402  			}
   403  		}
   404  	}
   405  
   406  	// And verify we can just seek past the end and get an EOF
   407  	got, err := sr.Seek(100, SeekStart)
   408  	if err != nil || got != 100 {
   409  		t.Errorf("Seek = %v, %v; want 100, nil", got, err)
   410  	}
   411  
   412  	n, err := sr.Read(make([]byte, 10))
   413  	if n != 0 || err != EOF {
   414  		t.Errorf("Read = %v, %v; want 0, EOF", n, err)
   415  	}
   416  }
   417  
   418  func TestSectionReader_Size(t *testing.T) {
   419  	tests := []struct {
   420  		data string
   421  		want int64
   422  	}{
   423  		{"a long sample data, 1234567890", 30},
   424  		{"", 0},
   425  	}
   426  
   427  	for _, tt := range tests {
   428  		r := strings.NewReader(tt.data)
   429  		sr := NewSectionReader(r, 0, int64(len(tt.data)))
   430  		if got := sr.Size(); got != tt.want {
   431  			t.Errorf("Size = %v; want %v", got, tt.want)
   432  		}
   433  	}
   434  }
   435  
   436  func TestSectionReader_Max(t *testing.T) {
   437  	r := strings.NewReader("abcdef")
   438  	const maxint64 = 1<<63 - 1
   439  	sr := NewSectionReader(r, 3, maxint64)
   440  	n, err := sr.Read(make([]byte, 3))
   441  	if n != 3 || err != nil {
   442  		t.Errorf("Read = %v %v, want 3, nil", n, err)
   443  	}
   444  	n, err = sr.Read(make([]byte, 3))
   445  	if n != 0 || err != EOF {
   446  		t.Errorf("Read = %v, %v, want 0, EOF", n, err)
   447  	}
   448  }
   449  
   450  // largeWriter returns an invalid count that is larger than the number
   451  // of bytes provided (issue 39978).
   452  type largeWriter struct {
   453  	err error
   454  }
   455  
   456  func (w largeWriter) Write(p []byte) (int, error) {
   457  	return len(p) + 1, w.err
   458  }
   459  
   460  func TestCopyLargeWriter(t *testing.T) {
   461  	want := ErrInvalidWrite
   462  	rb := new(Buffer)
   463  	wb := largeWriter{}
   464  	rb.WriteString("hello, world.")
   465  	if _, err := Copy(wb, rb); err != want {
   466  		t.Errorf("Copy error: got %v, want %v", err, want)
   467  	}
   468  
   469  	want = errors.New("largeWriterError")
   470  	rb = new(Buffer)
   471  	wb = largeWriter{err: want}
   472  	rb.WriteString("hello, world.")
   473  	if _, err := Copy(wb, rb); err != want {
   474  		t.Errorf("Copy error: got %v, want %v", err, want)
   475  	}
   476  }
   477  
   478  func TestNopCloserWriterToForwarding(t *testing.T) {
   479  	for _, tc := range [...]struct {
   480  		Name string
   481  		r    Reader
   482  	}{
   483  		{"not a WriterTo", Reader(nil)},
   484  		{"a WriterTo", struct {
   485  			Reader
   486  			WriterTo
   487  		}{}},
   488  	} {
   489  		nc := NopCloser(tc.r)
   490  
   491  		_, expected := tc.r.(WriterTo)
   492  		_, got := nc.(WriterTo)
   493  		if expected != got {
   494  			t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected)
   495  		}
   496  	}
   497  }
   498  
   499  func TestOffsetWriter_Seek(t *testing.T) {
   500  	tmpfilename := "TestOffsetWriter_Seek"
   501  	tmpfile, err := os.CreateTemp(t.TempDir(), tmpfilename)
   502  	if err != nil || tmpfile == nil {
   503  		t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err)
   504  	}
   505  	defer tmpfile.Close()
   506  	w := NewOffsetWriter(tmpfile, 0)
   507  
   508  	// Should throw error errWhence if whence is not valid
   509  	t.Run("errWhence", func(t *testing.T) {
   510  		for _, whence := range []int{-3, -2, -1, 3, 4, 5} {
   511  			var offset int64 = 0
   512  			gotOff, gotErr := w.Seek(offset, whence)
   513  			if gotOff != 0 || gotErr != ErrWhence {
   514  				t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)",
   515  					whence, offset, gotOff, gotErr, 0, ErrWhence)
   516  			}
   517  		}
   518  	})
   519  
   520  	// Should throw error errOffset if offset is negative
   521  	t.Run("errOffset", func(t *testing.T) {
   522  		for _, whence := range []int{SeekStart, SeekCurrent} {
   523  			for offset := int64(-3); offset < 0; offset++ {
   524  				gotOff, gotErr := w.Seek(offset, whence)
   525  				if gotOff != 0 || gotErr != ErrOffset {
   526  					t.Errorf("For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, %v)",
   527  						whence, offset, gotOff, gotErr, 0, ErrOffset)
   528  				}
   529  			}
   530  		}
   531  	})
   532  
   533  	// Normal tests
   534  	t.Run("normal", func(t *testing.T) {
   535  		tests := []struct {
   536  			offset    int64
   537  			whence    int
   538  			returnOff int64
   539  		}{
   540  			// keep in order
   541  			{whence: SeekStart, offset: 1, returnOff: 1},
   542  			{whence: SeekStart, offset: 2, returnOff: 2},
   543  			{whence: SeekStart, offset: 3, returnOff: 3},
   544  			{whence: SeekCurrent, offset: 1, returnOff: 4},
   545  			{whence: SeekCurrent, offset: 2, returnOff: 6},
   546  			{whence: SeekCurrent, offset: 3, returnOff: 9},
   547  		}
   548  		for idx, tt := range tests {
   549  			gotOff, gotErr := w.Seek(tt.offset, tt.whence)
   550  			if gotOff != tt.returnOff || gotErr != nil {
   551  				t.Errorf("%d:: For whence %d, offset %d, OffsetWriter.Seek got: (%d, %v), want: (%d, <nil>)",
   552  					idx+1, tt.whence, tt.offset, gotOff, gotErr, tt.returnOff)
   553  			}
   554  		}
   555  	})
   556  }
   557  
   558  func TestOffsetWriter_WriteAt(t *testing.T) {
   559  	const content = "0123456789ABCDEF"
   560  	contentSize := int64(len(content))
   561  	tmpdir, err := os.MkdirTemp(t.TempDir(), "TestOffsetWriter_WriteAt")
   562  	if err != nil {
   563  		t.Fatal(err)
   564  	}
   565  
   566  	work := func(off, at int64) {
   567  		position := fmt.Sprintf("off_%d_at_%d", off, at)
   568  		tmpfile, err := os.CreateTemp(tmpdir, position)
   569  		if err != nil || tmpfile == nil {
   570  			t.Fatalf("CreateTemp(%s) failed: %v", position, err)
   571  		}
   572  		defer tmpfile.Close()
   573  
   574  		var writeN int64
   575  		var wg sync.WaitGroup
   576  		// Concurrent writes, one byte at a time
   577  		for step, value := range []byte(content) {
   578  			wg.Add(1)
   579  			go func(wg *sync.WaitGroup, tmpfile *os.File, value byte, off, at int64, step int) {
   580  				defer wg.Done()
   581  
   582  				w := NewOffsetWriter(tmpfile, off)
   583  				n, e := w.WriteAt([]byte{value}, at+int64(step))
   584  				if e != nil {
   585  					t.Errorf("WriteAt failed. off: %d, at: %d, step: %d\n error: %v", off, at, step, e)
   586  				}
   587  				atomic.AddInt64(&writeN, int64(n))
   588  			}(&wg, tmpfile, value, off, at, step)
   589  		}
   590  		wg.Wait()
   591  
   592  		// Read one more byte to reach EOF
   593  		buf := make([]byte, contentSize+1)
   594  		readN, err := tmpfile.ReadAt(buf, off+at)
   595  		if err != EOF {
   596  			t.Fatalf("ReadAt failed: %v", err)
   597  		}
   598  		readContent := string(buf[:contentSize])
   599  		if writeN != int64(readN) || writeN != contentSize || readContent != content {
   600  			t.Fatalf("%s:: WriteAt(%s, %d) error. \ngot n: %v, content: %s \nexpected n: %v, content: %v",
   601  				position, content, at, readN, readContent, contentSize, content)
   602  		}
   603  	}
   604  	for off := int64(0); off < 2; off++ {
   605  		for at := int64(0); at < 2; at++ {
   606  			work(off, at)
   607  		}
   608  	}
   609  }
   610  
   611  func TestOffsetWriter_Write(t *testing.T) {
   612  	const content = "0123456789ABCDEF"
   613  	contentSize := len(content)
   614  	tmpdir := t.TempDir()
   615  
   616  	makeOffsetWriter := func(name string) (*OffsetWriter, *os.File) {
   617  		tmpfilename := "TestOffsetWriter_Write_" + name
   618  		tmpfile, err := os.CreateTemp(tmpdir, tmpfilename)
   619  		if err != nil || tmpfile == nil {
   620  			t.Fatalf("CreateTemp(%s) failed: %v", tmpfilename, err)
   621  		}
   622  		return NewOffsetWriter(tmpfile, 0), tmpfile
   623  	}
   624  	checkContent := func(name string, f *os.File) {
   625  		// Read one more byte to reach EOF
   626  		buf := make([]byte, contentSize+1)
   627  		readN, err := f.ReadAt(buf, 0)
   628  		if err != EOF {
   629  			t.Fatalf("ReadAt failed, err: %v", err)
   630  		}
   631  		readContent := string(buf[:contentSize])
   632  		if readN != contentSize || readContent != content {
   633  			t.Fatalf("%s error. \ngot n: %v, content: %s \nexpected n: %v, content: %v",
   634  				name, readN, readContent, contentSize, content)
   635  		}
   636  	}
   637  
   638  	var name string
   639  	name = "Write"
   640  	t.Run(name, func(t *testing.T) {
   641  		// Write directly (off: 0, at: 0)
   642  		// Write content to file
   643  		w, f := makeOffsetWriter(name)
   644  		defer f.Close()
   645  		for _, value := range []byte(content) {
   646  			n, err := w.Write([]byte{value})
   647  			if err != nil {
   648  				t.Fatalf("Write failed, n: %d, err: %v", n, err)
   649  			}
   650  		}
   651  		checkContent(name, f)
   652  
   653  		// Copy -> Write
   654  		// Copy file f to file f2
   655  		name = "Copy"
   656  		w2, f2 := makeOffsetWriter(name)
   657  		defer f2.Close()
   658  		Copy(w2, f)
   659  		checkContent(name, f2)
   660  	})
   661  
   662  	// Copy -> WriteTo -> Write
   663  	// Note: strings.Reader implements the io.WriterTo interface.
   664  	name = "Write_Of_Copy_WriteTo"
   665  	t.Run(name, func(t *testing.T) {
   666  		w, f := makeOffsetWriter(name)
   667  		defer f.Close()
   668  		Copy(w, strings.NewReader(content))
   669  		checkContent(name, f)
   670  	})
   671  }