aboutsummaryrefslogtreecommitdiff
path: root/pack_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'pack_test.go')
-rw-r--r--pack_test.go214
1 files changed, 214 insertions, 0 deletions
diff --git a/pack_test.go b/pack_test.go
new file mode 100644
index 00000000..5661e322
--- /dev/null
+++ b/pack_test.go
@@ -0,0 +1,214 @@
+package furgit
+
+import (
+ "bytes"
+ "compress/zlib"
+ "encoding/binary"
+ "testing"
+)
+
+func compressBytes(t *testing.T, payload []byte) []byte {
+ var buf bytes.Buffer
+ zw := zlib.NewWriter(&buf)
+ if _, err := zw.Write(payload); err != nil {
+ t.Fatalf("compress write: %v", err)
+ }
+ if err := zw.Close(); err != nil {
+ t.Fatalf("compress close: %v", err)
+ }
+ return buf.Bytes()
+}
+
+func TestPackSectionInflate(t *testing.T) {
+ payload := []byte("pack payload")
+ compressed := compressBytes(t, payload)
+ body, err := packSectionInflate(bytes.NewReader(compressed), len(payload))
+ if err != nil {
+ t.Fatalf("packSectionInflate error: %v", err)
+ }
+ if got := string(body.Bytes()); got != string(payload) {
+ t.Fatalf("unexpected inflated data: %q", got)
+ }
+ body.Release()
+
+ body, err = packSectionInflate(bytes.NewReader(compressed), 0)
+ if err != nil {
+ t.Fatalf("packSectionInflate streaming error: %v", err)
+ }
+ if got := string(body.Bytes()); got != string(payload) {
+ t.Fatalf("unexpected streaming data: %q", got)
+ }
+ body.Release()
+}
+
+func encodePackHeader(ty ObjType, size int) []byte {
+ first := byte((ty & 0x7) << 4)
+ first |= byte(size & 0x0f)
+ size >>= 4
+ if size == 0 {
+ return []byte{first}
+ }
+ first |= 0x80
+ out := []byte{first}
+ for size > 0 {
+ b := byte(size & 0x7f)
+ size >>= 7
+ if size != 0 {
+ b |= 0x80
+ }
+ out = append(out, b)
+ }
+ return out
+}
+
+func TestPackHeaderRead(t *testing.T) {
+ buf := encodePackHeader(ObjTree, 0x1fff)
+ ty, size, err := packHeaderRead(bytes.NewReader(buf))
+ if err != nil {
+ t.Fatalf("packHeaderRead error: %v", err)
+ }
+ if ty != ObjTree || size != 0x1fff {
+ t.Fatalf("unexpected header decode ty=%d size=%d", ty, size)
+ }
+ if _, _, err := packHeaderRead(bytes.NewReader([]byte{0x80})); err == nil {
+ t.Fatal("expected error for truncated header")
+ }
+}
+
+func encodeVarint(value int) []byte {
+ var out []byte
+ for {
+ b := byte(value & 0x7f)
+ value >>= 7
+ if value != 0 {
+ b |= 0x80
+ }
+ out = append(out, b)
+ if value == 0 {
+ break
+ }
+ }
+ return out
+}
+
+func TestPackVarintRead(t *testing.T) {
+ buf := encodeVarint(0x3456)
+ pos := 0
+ val, err := packVarintRead(buf, &pos)
+ if err != nil {
+ t.Fatalf("packVarintRead error: %v", err)
+ }
+ if val != 0x3456 {
+ t.Fatalf("unexpected varint value: %d", val)
+ }
+ if pos != len(buf) {
+ t.Fatalf("expected pos %d, got %d", len(buf), pos)
+ }
+ bad := []byte{0x80}
+ pos = 0
+ if _, err := packVarintRead(bad, &pos); err == nil {
+ t.Fatal("expected error for unterminated varint")
+ }
+}
+
+func TestPackDeltaApply(t *testing.T) {
+ base := borrowedFromOwned([]byte("abcdefghij"))
+ defer base.Release()
+ deltaBytes := []byte{0x0a, 0x0a, 0x91, 0x00, 0x03, 0x03, 'X', 'Y', 'Z', 0x91, 0x06, 0x04}
+ delta := borrowedFromOwned(deltaBytes)
+ defer delta.Release()
+ out, err := packDeltaApply(base, delta)
+ if err != nil {
+ t.Fatalf("packDeltaApply error: %v", err)
+ }
+ if got := string(out.Bytes()); got != "abcXYZghij" {
+ t.Fatalf("unexpected delta output: %q", got)
+ }
+ out.Release()
+}
+
+func TestPackDeltaApplyMismatchedBaseSize(t *testing.T) {
+ base := borrowedFromOwned([]byte("abc"))
+ defer base.Release()
+ delta := borrowedFromOwned([]byte{0x04, 0x04})
+ defer delta.Release()
+ if _, err := packDeltaApply(base, delta); err == nil {
+ t.Fatal("expected error for mismatched base size")
+ }
+}
+
+func TestPackDeltaReadOfsDistance(t *testing.T) {
+ dist, err := packDeltaReadOfsDistance(bytes.NewReader([]byte{0x81, 0x01}))
+ if err != nil {
+ t.Fatalf("packDeltaReadOfsDistance error: %v", err)
+ }
+ if dist != 257 {
+ t.Fatalf("unexpected distance: %d", dist)
+ }
+ if _, err := packDeltaReadOfsDistance(bytes.NewReader([]byte{})); err == nil {
+ t.Fatal("expected error for empty reader")
+ }
+}
+
+func TestBsearchHash(t *testing.T) {
+ h1 := hashWithByte(0x01)
+ h2 := hashWithByte(0x03)
+ names := append(append([]byte(nil), h1[:]...), h2[:]...)
+ idx, found := bsearchHash(names, HashSize, 0, 2, h2)
+ if !found || idx != 1 {
+ t.Fatalf("expected to find second hash, idx=%d found=%v", idx, found)
+ }
+ _, found = bsearchHash(names, HashSize, 0, 2, hashWithByte(0x05))
+ if found {
+ t.Fatalf("did not expect to find unknown hash")
+ }
+}
+
+func buildTestPackIndexBuffer(hash Hash, offset uint32) []byte {
+ fanout := make([]byte, 256*4)
+ first := int(hash[0])
+ for i := 0; i < 256; i++ {
+ var val uint32
+ if i >= first {
+ val = 1
+ }
+ binary.BigEndian.PutUint32(fanout[i*4:], val)
+ }
+ var buf bytes.Buffer
+ _ = binary.Write(&buf, binary.BigEndian, uint32(idxMagic))
+ _ = binary.Write(&buf, binary.BigEndian, uint32(idxVersion2))
+ buf.Write(fanout)
+ buf.Write(hash[:])
+ buf.Write(make([]byte, 4))
+ off32 := make([]byte, 4)
+ binary.BigEndian.PutUint32(off32, offset)
+ buf.Write(off32)
+ buf.Write(make([]byte, 40))
+ return buf.Bytes()
+}
+
+func TestPackIndexParse(t *testing.T) {
+ h := hashWithByte(0x11)
+ data := buildTestPackIndexBuffer(h, 0x12345678)
+ pi := &packIndex{}
+ if err := pi.parse(data); err != nil {
+ t.Fatalf("parse error: %v", err)
+ }
+ if pi.numObjects != 1 {
+ t.Fatalf("expected 1 object, got %d", pi.numObjects)
+ }
+ if got, err := pi.offset(0); err != nil || got != 0x12345678 {
+ t.Fatalf("unexpected 32-bit offset or error: %d, %v", got, err)
+ }
+}
+
+func TestPackIndexOffset64(t *testing.T) {
+ pi := &packIndex{}
+ pi.offset32 = make([]byte, 4)
+ binary.BigEndian.PutUint32(pi.offset32, 0x80000000)
+ pi.offset64 = make([]byte, 8)
+ binary.BigEndian.PutUint64(pi.offset64, 0x1_0000_0000)
+ if got, err := pi.offset(0); err != nil || got != 0x1_0000_0000 {
+ t.Fatalf("unexpected 64-bit offset or error: %d, %v", got, err)
+ }
+}