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
5package io_test
6
7import (
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
20type 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
28func 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
38func 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
53func 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
63func 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
73func 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
83func 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
94type writeToChecker struct {
95	bytes.Buffer
96	writeToCalled bool
97}
98
99func (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.
107func 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
119type zeroErrReader struct {
120	err error
121}
122
123func (r zeroErrReader) Read(p []byte) (int, error) {
124	return copy(p, []byte{0}), r.err
125}
126
127type errWriter struct {
128	err error
129}
130
131func (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.
138func 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
147func 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
157func 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
167func 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
177func 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
189func 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
201type noReadFrom struct {
202	w Writer
203}
204
205func (w *noReadFrom) Write(p []byte) (n int, err error) {
206	return w.w.Write(p)
207}
208
209type wantedAndErrReader struct{}
210
211func (wantedAndErrReader) Read(p []byte) (int, error) {
212	return len(p), errors.New("wantedAndErrReader error")
213}
214
215func 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
252func 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.
259type dataAndErrorBuffer struct {
260	err error
261	bytes.Buffer
262}
263
264func (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
272func TestReadAtLeastWithDataAndEOF(t *testing.T) {
273	var rb dataAndErrorBuffer
274	rb.err = EOF
275	testReadAtLeast(t, &rb)
276}
277
278func TestReadAtLeastWithDataAndError(t *testing.T) {
279	var rb dataAndErrorBuffer
280	rb.err = fmt.Errorf("fake error")
281	testReadAtLeast(t, &rb)
282}
283
284func 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
329func 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
356func 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
393func 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
421func 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
439func 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).
458type largeWriter struct {
459	err error
460}
461
462func (w largeWriter) Write(p []byte) (int, error) {
463	return len(p) + 1, w.err
464}
465
466func 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
484func 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
505func 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
564func 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
617func 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
637func 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