Skip to content

Commit beecff2

Browse files
corylanouclaude
andcommitted
test: add 64KB page size tests for block format
Add tests to verify LZ4 block compression works correctly with 64KB pages (SQLite's maximum page size): - Compressible test: repetitive data that compresses well - Incompressible test: random data stored uncompressed Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent f9e8a22 commit beecff2

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

decoder_test.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ltx_test
22

33
import (
44
"bytes"
5+
"crypto/rand"
56
"io"
67
"reflect"
78
"testing"
@@ -239,3 +240,149 @@ func TestDecoder_DecodeDatabaseTo(t *testing.T) {
239240
}
240241
})
241242
}
243+
244+
func TestDecoder_64KBPageSize(t *testing.T) {
245+
const pageSize = 65536 // 64KB - maximum SQLite page size
246+
247+
t.Run("Compressible", func(t *testing.T) {
248+
// Test with compressible data (repetitive pattern).
249+
page1Data := bytes.Repeat([]byte("A"), pageSize)
250+
page2Data := bytes.Repeat([]byte("B"), pageSize)
251+
252+
// Calculate correct post-apply checksum.
253+
chksum := ltx.ChecksumFlag
254+
chksum = ltx.ChecksumFlag | (chksum ^ ltx.ChecksumPage(1, page1Data))
255+
chksum = ltx.ChecksumFlag | (chksum ^ ltx.ChecksumPage(2, page2Data))
256+
257+
var buf bytes.Buffer
258+
enc, err := ltx.NewEncoder(&buf)
259+
if err != nil {
260+
t.Fatal(err)
261+
}
262+
if err := enc.EncodeHeader(ltx.Header{
263+
Version: ltx.Version,
264+
PageSize: pageSize,
265+
Commit: 2,
266+
MinTXID: 1,
267+
MaxTXID: 1,
268+
Timestamp: 1000,
269+
}); err != nil {
270+
t.Fatal(err)
271+
}
272+
if err := enc.EncodePage(ltx.PageHeader{Pgno: 1}, page1Data); err != nil {
273+
t.Fatal(err)
274+
}
275+
if err := enc.EncodePage(ltx.PageHeader{Pgno: 2}, page2Data); err != nil {
276+
t.Fatal(err)
277+
}
278+
enc.SetPostApplyChecksum(chksum)
279+
if err := enc.Close(); err != nil {
280+
t.Fatal(err)
281+
}
282+
283+
// Decode and verify.
284+
dec := ltx.NewDecoder(&buf)
285+
if err := dec.DecodeHeader(); err != nil {
286+
t.Fatal(err)
287+
}
288+
289+
var hdr ltx.PageHeader
290+
data := make([]byte, pageSize)
291+
292+
if err := dec.DecodePage(&hdr, data); err != nil {
293+
t.Fatal(err)
294+
}
295+
if hdr.Pgno != 1 {
296+
t.Fatalf("expected pgno 1, got %d", hdr.Pgno)
297+
}
298+
if !bytes.Equal(data, page1Data) {
299+
t.Fatal("page 1 data mismatch")
300+
}
301+
// Should be compressed (flag without uncompressed bit).
302+
if hdr.Flags != ltx.PageHeaderFlagCompressedSize {
303+
t.Fatalf("expected compressed flag only, got 0x%x", hdr.Flags)
304+
}
305+
306+
if err := dec.DecodePage(&hdr, data); err != nil {
307+
t.Fatal(err)
308+
}
309+
if hdr.Pgno != 2 {
310+
t.Fatalf("expected pgno 2, got %d", hdr.Pgno)
311+
}
312+
if !bytes.Equal(data, page2Data) {
313+
t.Fatal("page 2 data mismatch")
314+
}
315+
316+
if err := dec.DecodePage(&hdr, data); err != io.EOF {
317+
t.Fatalf("expected EOF, got %v", err)
318+
}
319+
if err := dec.Close(); err != nil {
320+
t.Fatal(err)
321+
}
322+
})
323+
324+
t.Run("Incompressible", func(t *testing.T) {
325+
// Test with incompressible data (truly random bytes).
326+
page1Data := make([]byte, pageSize)
327+
if _, err := rand.Read(page1Data); err != nil {
328+
t.Fatal(err)
329+
}
330+
331+
// Calculate correct post-apply checksum.
332+
chksum := ltx.ChecksumFlag | ltx.ChecksumPage(1, page1Data)
333+
334+
var buf bytes.Buffer
335+
enc, err := ltx.NewEncoder(&buf)
336+
if err != nil {
337+
t.Fatal(err)
338+
}
339+
if err := enc.EncodeHeader(ltx.Header{
340+
Version: ltx.Version,
341+
PageSize: pageSize,
342+
Commit: 1,
343+
MinTXID: 1,
344+
MaxTXID: 1,
345+
Timestamp: 1000,
346+
}); err != nil {
347+
t.Fatal(err)
348+
}
349+
if err := enc.EncodePage(ltx.PageHeader{Pgno: 1}, page1Data); err != nil {
350+
t.Fatal(err)
351+
}
352+
enc.SetPostApplyChecksum(chksum)
353+
if err := enc.Close(); err != nil {
354+
t.Fatal(err)
355+
}
356+
357+
// Decode and verify.
358+
dec := ltx.NewDecoder(&buf)
359+
if err := dec.DecodeHeader(); err != nil {
360+
t.Fatal(err)
361+
}
362+
363+
var hdr ltx.PageHeader
364+
data := make([]byte, pageSize)
365+
366+
if err := dec.DecodePage(&hdr, data); err != nil {
367+
t.Fatal(err)
368+
}
369+
if hdr.Pgno != 1 {
370+
t.Fatalf("expected pgno 1, got %d", hdr.Pgno)
371+
}
372+
if !bytes.Equal(data, page1Data) {
373+
t.Fatal("page 1 data mismatch")
374+
}
375+
// Should be stored uncompressed (random data doesn't compress).
376+
expectedFlags := ltx.PageHeaderFlagCompressedSize | ltx.PageHeaderFlagUncompressed
377+
if hdr.Flags != expectedFlags {
378+
t.Fatalf("expected uncompressed flags 0x%x, got 0x%x", expectedFlags, hdr.Flags)
379+
}
380+
381+
if err := dec.DecodePage(&hdr, data); err != io.EOF {
382+
t.Fatalf("expected EOF, got %v", err)
383+
}
384+
if err := dec.Close(); err != nil {
385+
t.Fatal(err)
386+
}
387+
})
388+
}

0 commit comments

Comments
 (0)