Skip to content

Commit 8061277

Browse files
mrjoshuakclaude
andcommitted
Add regression test for multipart ReadChunk roundtrip
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c9d17dd commit 8061277

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

exr/file_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,84 @@ func TestMultiPartWriteChunkPart(t *testing.T) {
416416
t.Logf("Multi-part file size: %d bytes", buf.Len())
417417
}
418418

419+
// TestMultiPartReadChunkRoundtrip verifies that ReadChunk correctly handles
420+
// the 4-byte part number prefix in multipart EXR chunk headers.
421+
func TestMultiPartReadChunkRoundtrip(t *testing.T) {
422+
h1 := NewScanlineHeader(4, 4)
423+
h1.SetCompression(CompressionNone)
424+
h1.Set(&Attribute{Name: AttrNameName, Type: AttrTypeString, Value: "part1"})
425+
426+
h2 := NewScanlineHeader(4, 4)
427+
h2.SetCompression(CompressionNone)
428+
h2.Set(&Attribute{Name: AttrNameName, Type: AttrTypeString, Value: "part2"})
429+
430+
var buf bytes.Buffer
431+
ws := &seekableWriter{Buffer: &buf}
432+
433+
w, err := NewMultiPartWriter(ws, []*Header{h1, h2})
434+
if err != nil {
435+
t.Fatalf("NewMultiPartWriter() error = %v", err)
436+
}
437+
438+
// Write chunks with distinct data per part and scanline
439+
// Each scanline: 4 pixels * 3 channels (R,G,B half) * 2 bytes = 24 bytes
440+
chunkSize := 24
441+
written := make(map[[2]int][]byte) // [part, y] -> data
442+
for part := 0; part < 2; part++ {
443+
for y := 0; y < 4; y++ {
444+
data := make([]byte, chunkSize)
445+
// Fill with recognizable pattern: part in first byte, y in second
446+
for i := range data {
447+
data[i] = byte(part*16 + y + i)
448+
}
449+
written[[2]int{part, y}] = append([]byte(nil), data...)
450+
if err := w.WriteChunkPart(part, int32(y), data); err != nil {
451+
t.Fatalf("WriteChunkPart(%d, %d) error = %v", part, y, err)
452+
}
453+
}
454+
}
455+
456+
if err := w.Close(); err != nil {
457+
t.Fatalf("Close() error = %v", err)
458+
}
459+
460+
// Read back and verify
461+
fileData := buf.Bytes()
462+
r := &readerAtWrapper{bytes.NewReader(fileData)}
463+
464+
f, err := OpenReader(r, int64(len(fileData)))
465+
if err != nil {
466+
t.Fatalf("OpenReader() error = %v", err)
467+
}
468+
469+
if !f.IsMultiPart() {
470+
t.Fatal("expected multipart file")
471+
}
472+
if f.NumParts() != 2 {
473+
t.Fatalf("expected 2 parts, got %d", f.NumParts())
474+
}
475+
476+
for part := 0; part < 2; part++ {
477+
offsets := f.Offsets(part)
478+
if len(offsets) != 4 {
479+
t.Fatalf("part %d: expected 4 chunks, got %d", part, len(offsets))
480+
}
481+
for chunkIdx := 0; chunkIdx < 4; chunkIdx++ {
482+
y, data, err := f.ReadChunk(part, chunkIdx)
483+
if err != nil {
484+
t.Fatalf("ReadChunk(%d, %d) error = %v", part, chunkIdx, err)
485+
}
486+
if int(y) != chunkIdx {
487+
t.Errorf("ReadChunk(%d, %d) y = %d, want %d", part, chunkIdx, y, chunkIdx)
488+
}
489+
expected := written[[2]int{part, chunkIdx}]
490+
if !bytes.Equal(data, expected) {
491+
t.Errorf("ReadChunk(%d, %d) data mismatch: got %v, want %v", part, chunkIdx, data[:4], expected[:4])
492+
}
493+
}
494+
}
495+
}
496+
419497
func TestMultiPartWriteInvalidPart(t *testing.T) {
420498
h := NewScanlineHeader(4, 4)
421499
h.SetCompression(CompressionNone)

0 commit comments

Comments
 (0)