@@ -9,7 +9,10 @@ import (
9
9
"errors"
10
10
"fmt"
11
11
. "io"
12
+ "os"
12
13
"strings"
14
+ "sync"
15
+ "sync/atomic"
13
16
"testing"
14
17
)
15
18
@@ -492,3 +495,177 @@ func TestNopCloserWriterToForwarding(t *testing.T) {
492
495
}
493
496
}
494
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. \n got n: %v, content: %s \n expected 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. \n got n: %v, content: %s \n expected 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
+ }
0 commit comments