From 2aecb1f4f570849ace353c577436f63a9f4de209 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Sat, 3 Jan 2026 08:22:51 -0800 Subject: Add support for seeking within a stream Oh God what have I done --- oae2_test.go | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) (limited to 'oae2_test.go') diff --git a/oae2_test.go b/oae2_test.go index 96b83af..e326ef2 100644 --- a/oae2_test.go +++ b/oae2_test.go @@ -55,6 +55,74 @@ func TestReadFromWriteTo(t *testing.T) { } } +func TestReader_Seek(t *testing.T) { + t.Parallel() + const ( + msg = "Hello World!" + key = "asdf" + blockSize = 1 + ) + for _, tc := range []struct { + desc string + offset int64 + whence int + wantOffset int64 + wantMsg string + }{{ + desc: "SeekStart", + offset: 6, + whence: io.SeekStart, + wantOffset: 6, + wantMsg: "World!", + }, { + desc: "SeekEnd", + offset: -6, + whence: io.SeekEnd, + wantOffset: 6, + wantMsg: "World!", + }, { + desc: "SeekEndOffsetZero", + offset: 0, + whence: io.SeekEnd, + wantOffset: 12, + wantMsg: "", + }, { + desc: "SeekCurrent", + offset: 6, + whence: io.SeekCurrent, + wantOffset: 6, + wantMsg: "World!", + }} { + t.Run(tc.desc, func(t *testing.T) { + t.Parallel() + + buf := new(bytes.Buffer) + w := NewWriter(buf, []byte(key), blockSize) + if _, err := io.WriteString(w, msg); err != nil { + t.Fatalf("Writer.Write failed: %s", err) + } + if err := w.Close(); err != nil { + t.Fatalf("Writer.Close failed: %s", err) + } + r := NewReader(bytes.NewReader(buf.Bytes()), []byte(key), blockSize) + n, err := r.Seek(tc.offset, tc.whence) + if err != nil { + t.Fatalf("Reader.Seek(%d, %d) failed: %s", tc.offset, tc.whence, err) + } + if n != tc.wantOffset { + t.Errorf("Reader.Seek(%d, %d) = %d, want %d", tc.offset, tc.whence, n, tc.wantOffset) + } + got, err := io.ReadAll(r) + if err != nil { + t.Fatalf("Reader.Read failed: %s", err) + } + if string(got) != tc.wantMsg { + t.Errorf("Incorrect message after Seek: got %q, want %q", got, tc.wantMsg) + } + }) + } +} + func FuzzRoundTrip(f *testing.F) { f.Add(1, []byte("Hello World!")) f.Fuzz(func(t *testing.T, segmentSize int, msg []byte) { @@ -118,3 +186,129 @@ func FuzzReadFromWriteTo(f *testing.F) { } }) } + +func FuzzSeekStart(f *testing.F) { + f.Add(1, []byte("Hello World!"), 6) + f.Add(30, []byte("000000"), 6) + f.Fuzz(func(t *testing.T, segmentSize int, msg []byte, offset int) { + const password = "asdf" + + if segmentSize <= 0 || offset > len(msg) { + return + } + buf := new(bytes.Buffer) + w := NewWriter(buf, []byte(password), segmentSize) + if _, err := w.Write(msg); err != nil { + t.Fatalf("Writer.Write(%q) failed: %s", msg, err) + } + if err := w.Close(); err != nil { + t.Fatalf("Writer.Close failed: %s", err) + } + r := NewReader(bytes.NewReader(buf.Bytes()), []byte(password), segmentSize) + n, err := r.Seek(int64(offset), io.SeekStart) + if offset < 0 { + if err == nil { + t.Errorf("Seek(%d, io.SeekStart) succeeded, want error", offset) + } + return + } + if err != nil { + t.Fatalf("Seek(%d, io.SeekStart) failed: %s", offset, err) + } + if n != int64(offset) { + t.Errorf("Seek(%d, io.SeekStart) = %d, want %d", offset, n, offset) + } + got, err := io.ReadAll(r) + if err != nil { + t.Fatalf("Reader.Read failed: %s", err) + } + if !bytes.Equal(got, msg[offset:]) { + t.Errorf("Message after seek is %q, want %q", got, msg[offset:]) + } + }) +} + +func FuzzSeekEnd(f *testing.F) { + f.Add(1, []byte("Hello World!"), -6) + f.Fuzz(func(t *testing.T, segmentSize int, msg []byte, offset int) { + const password = "asdf" + + if segmentSize <= 0 || offset > 0 { + return + } + buf := new(bytes.Buffer) + w := NewWriter(buf, []byte(password), segmentSize) + if _, err := w.Write(msg); err != nil { + t.Fatalf("Writer.Write(%q) failed: %s", msg, err) + } + if err := w.Close(); err != nil { + t.Fatalf("Writer.Close failed: %s", err) + } + r := NewReader(bytes.NewReader(buf.Bytes()), []byte(password), segmentSize) + n, err := r.Seek(int64(offset), io.SeekEnd) + if len(msg)+offset < 0 { + if err == nil { + t.Errorf("Seek(%d, io.SeekEnd) succeeded, want error", offset) + } + return + } + if err != nil { + t.Fatalf("Seek(%d, io.SeekEnd) failed: %s", offset, err) + } + if n != int64(len(msg)+offset) { + t.Errorf("Seek(%d, io.SeekEnd) = %d, want %d", offset, n, offset) + } + got, err := io.ReadAll(r) + if err != nil { + t.Fatalf("Reader.Read failed: %s", err) + } + if !bytes.Equal(got, msg[len(msg)+offset:]) { + t.Errorf("Message after seek is %q, want %q", got, msg[offset:]) + } + }) +} + +func FuzzSeekCurrent(f *testing.F) { + f.Add(1, []byte("Hello World!"), 3, 3) + f.Add(1, []byte("000000"), 3, 3) + f.Add(1, []byte("000"), 3, -3) + f.Fuzz(func(t *testing.T, segmentSize int, msg []byte, offset1, offset2 int) { + const password = "asdf" + + if segmentSize <= 0 || offset1 < 0 || offset1 > len(msg) || offset1+offset2 > len(msg) { + return + } + buf := new(bytes.Buffer) + w := NewWriter(buf, []byte(password), segmentSize) + if _, err := w.Write(msg); err != nil { + t.Fatalf("Writer.Write(%q) failed: %s", msg, err) + } + if err := w.Close(); err != nil { + t.Fatalf("Writer.Close failed: %s", err) + } + r := NewReader(bytes.NewReader(buf.Bytes()), []byte(password), segmentSize) + if _, err := io.ReadFull(r, make([]byte, offset1)); err != nil { + t.Fatalf("Reader.Read failed: %s", err) + } + n, err := r.Seek(int64(offset2), io.SeekCurrent) + if offset1+offset2 < 0 { + if err == nil { + t.Errorf("Seek(%d, io.SeekCurrent) succeeded, want error", offset2) + } + return + } + if err != nil { + t.Fatalf("Seek(%d, io.SeekCurrent) failed: %s", offset2, err) + } + if n != int64(offset1+offset2) { + t.Errorf("Seek(%d, io.SeekEnd) = %d, want %d", offset2, n, offset1+offset2) + } + got, err := io.ReadAll(r) + if err != nil { + t.Fatalf("Reader.Read failed: %s", err) + } + if !bytes.Equal(got, msg[offset1+offset2:]) { + t.Errorf("Message after seek is %q, want %q", got, msg[offset1+offset2:]) + } + }) +} -- cgit v1.3.1