github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/git/odb/pack/packfile_test.go (about)

     1  package pack
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"sort"
     8  	"strings"
     9  	"sync/atomic"
    10  	"testing"
    11  
    12  	"github.com/git-lfs/git-lfs/errors"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  )
    16  
    17  func TestPackObjectReturnsObjectWithSingleBaseAtLowOffset(t *testing.T) {
    18  	const original = "Hello, world!\n"
    19  	compressed, _ := compress(original)
    20  
    21  	p := &Packfile{
    22  		idx: IndexWith(map[string]uint32{
    23  			"cccccccccccccccccccccccccccccccccccccccc": 32,
    24  		}),
    25  		r: bytes.NewReader(append([]byte{
    26  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    27  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    28  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    29  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    30  
    31  			// (0001 1000) (msb=0, type=commit, size=14)
    32  			0x1e}, compressed...),
    33  		),
    34  	}
    35  
    36  	o, err := p.Object(DecodeHex(t, "cccccccccccccccccccccccccccccccccccccccc"))
    37  	assert.NoError(t, err)
    38  
    39  	assert.Equal(t, TypeCommit, o.Type())
    40  
    41  	unpacked, err := o.Unpack()
    42  	assert.Equal(t, []byte(original), unpacked)
    43  	assert.NoError(t, err)
    44  }
    45  
    46  func TestPackObjectReturnsObjectWithSingleBaseAtHighOffset(t *testing.T) {
    47  	original := strings.Repeat("four", 64)
    48  	compressed, _ := compress(original)
    49  
    50  	p := &Packfile{
    51  		idx: IndexWith(map[string]uint32{
    52  			"cccccccccccccccccccccccccccccccccccccccc": 32,
    53  		}),
    54  		r: bytes.NewReader(append([]byte{
    55  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    56  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    57  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    58  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
    59  
    60  			// (1001 0000) (msb=1, type=commit, size=0)
    61  			0x90,
    62  			// (1000 0000) (msb=0, size=1 -> size=256)
    63  			0x10},
    64  
    65  			compressed...,
    66  		)),
    67  	}
    68  
    69  	o, err := p.Object(DecodeHex(t, "cccccccccccccccccccccccccccccccccccccccc"))
    70  	assert.NoError(t, err)
    71  
    72  	assert.Equal(t, TypeCommit, o.Type())
    73  
    74  	unpacked, err := o.Unpack()
    75  	assert.Equal(t, []byte(original), unpacked)
    76  	assert.NoError(t, err)
    77  }
    78  
    79  func TestPackObjectReturnsObjectWithDeltaBaseOffset(t *testing.T) {
    80  	const original = "Hello"
    81  	compressed, _ := compress(original)
    82  
    83  	delta, err := compress(string([]byte{
    84  		0x05, // Source size: 5.
    85  		0x0e, // Destination size: 14.
    86  
    87  		0x91, // (1000 0001) (instruction=copy, bitmask=0001)
    88  		0x00, // (0000 0000) (offset=0)
    89  		0x05, // (0000 0101) (size=5)
    90  
    91  		0x09, // (0000 0111) (instruction=add, size=7)
    92  
    93  		// Contents: ...
    94  		',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n',
    95  	}))
    96  
    97  	p := &Packfile{
    98  		idx: IndexWith(map[string]uint32{
    99  			"cccccccccccccccccccccccccccccccccccccccc": uint32(32 + 1 + len(compressed)),
   100  		}),
   101  		r: bytes.NewReader(append(append([]byte{
   102  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   103  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   104  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   105  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   106  
   107  			0x35, // (0011 0101) (msb=0, type=blob, size=5)
   108  		}, compressed...), append([]byte{
   109  			0x6e, // (0110 1010) (msb=0, type=obj_ofs_delta, size=10)
   110  			0x12, // (0001 0001) (ofs_delta=-17, len(compressed))
   111  		}, delta...)...)),
   112  	}
   113  
   114  	o, err := p.Object(DecodeHex(t, "cccccccccccccccccccccccccccccccccccccccc"))
   115  	assert.NoError(t, err)
   116  
   117  	assert.Equal(t, TypeBlob, o.Type())
   118  
   119  	unpacked, err := o.Unpack()
   120  	assert.Equal(t, []byte(original+", world!\n"), unpacked)
   121  	assert.NoError(t, err)
   122  }
   123  
   124  func TestPackfileObjectReturnsObjectWithDeltaBaseReference(t *testing.T) {
   125  	const original = "Hello!\n"
   126  	compressed, _ := compress(original)
   127  
   128  	delta, _ := compress(string([]byte{
   129  		0x07, // Source size: 7.
   130  		0x0e, // Destination size: 14.
   131  
   132  		0x91, // (1001 0001) (copy, smask=0001, omask=0001)
   133  		0x00, // (0000 0000) (offset=0)
   134  		0x05, // (0000 0101) (size=5)
   135  
   136  		0x7,                               // (0000 0111) (add, length=6)
   137  		',', ' ', 'w', 'o', 'r', 'l', 'd', // (data ...)
   138  
   139  		0x91, // (1001 0001) (copy, smask=0001, omask=0001)
   140  		0x05, // (0000 0101) (offset=5)
   141  		0x02, // (0000 0010) (size=2)
   142  	}))
   143  
   144  	p := &Packfile{
   145  		idx: IndexWith(map[string]uint32{
   146  			"cccccccccccccccccccccccccccccccccccccccc": 32,
   147  			"dddddddddddddddddddddddddddddddddddddddd": 52,
   148  		}),
   149  		r: bytes.NewReader(append(append([]byte{
   150  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   151  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   152  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   153  			0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
   154  
   155  			0x37, // (0011 0101) (msb=0, type=blob, size=7)
   156  		}, compressed...), append([]byte{
   157  			0x7f, // (0111 1111) (msb=0, type=obj_ref_delta, size=15)
   158  
   159  			// SHA-1 "cccccccccccccccccccccccccccccccccccccccc",
   160  			// original blob contents is "Hello!\n"
   161  			0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
   162  			0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
   163  		}, delta...)...)),
   164  	}
   165  
   166  	o, err := p.Object(DecodeHex(t, "dddddddddddddddddddddddddddddddddddddddd"))
   167  	assert.NoError(t, err)
   168  
   169  	assert.Equal(t, TypeBlob, o.Type())
   170  
   171  	unpacked, err := o.Unpack()
   172  	assert.Equal(t, []byte("Hello, world!\n"), unpacked)
   173  	assert.NoError(t, err)
   174  }
   175  
   176  func TestPackfileClosesReadClosers(t *testing.T) {
   177  	r := new(ReaderAtCloser)
   178  	p := &Packfile{
   179  		r: r,
   180  	}
   181  
   182  	assert.NoError(t, p.Close())
   183  	assert.EqualValues(t, 1, r.N)
   184  }
   185  
   186  func TestPackfileClosePropogatesCloseErrors(t *testing.T) {
   187  	e := errors.New("git/odb/pack: testing")
   188  	p := &Packfile{
   189  		r: &ReaderAtCloser{E: e},
   190  	}
   191  
   192  	assert.Equal(t, e, p.Close())
   193  }
   194  
   195  type ReaderAtCloser struct {
   196  	E error
   197  	N uint64
   198  }
   199  
   200  func (r *ReaderAtCloser) ReadAt(p []byte, at int64) (int, error) {
   201  	return 0, nil
   202  }
   203  
   204  func (r *ReaderAtCloser) Close() error {
   205  	atomic.AddUint64(&r.N, 1)
   206  	return r.E
   207  }
   208  
   209  func IndexWith(offsets map[string]uint32) *Index {
   210  	header := []byte{
   211  		0xff, 0x74, 0x4f, 0x63,
   212  		0x00, 0x00, 0x00, 0x02,
   213  	}
   214  
   215  	ns := make([][]byte, 0, len(offsets))
   216  	for name, _ := range offsets {
   217  		x, _ := hex.DecodeString(name)
   218  		ns = append(ns, x)
   219  	}
   220  	sort.Slice(ns, func(i, j int) bool {
   221  		return bytes.Compare(ns[i], ns[j]) < 0
   222  	})
   223  
   224  	fanout := make([]uint32, 256)
   225  	for i := 0; i < len(fanout); i++ {
   226  		var n uint32
   227  
   228  		for _, name := range ns {
   229  			if name[0] <= byte(i) {
   230  				n++
   231  			}
   232  		}
   233  
   234  		fanout[i] = n
   235  	}
   236  
   237  	crcs := make([]byte, 4*len(offsets))
   238  	for i, _ := range ns {
   239  		binary.BigEndian.PutUint32(crcs[i*4:], 0)
   240  	}
   241  
   242  	offs := make([]byte, 4*len(offsets))
   243  	for i, name := range ns {
   244  		binary.BigEndian.PutUint32(offs[i*4:], offsets[hex.EncodeToString(name)])
   245  	}
   246  
   247  	buf := make([]byte, 0)
   248  	buf = append(buf, header...)
   249  	for _, f := range fanout {
   250  		x := make([]byte, 4)
   251  		binary.BigEndian.PutUint32(x, f)
   252  
   253  		buf = append(buf, x...)
   254  	}
   255  	for _, n := range ns {
   256  		buf = append(buf, n...)
   257  	}
   258  	buf = append(buf, crcs...)
   259  	buf = append(buf, offs...)
   260  
   261  	return &Index{
   262  		fanout: fanout,
   263  		r:      bytes.NewReader(buf),
   264  
   265  		version: new(V2),
   266  	}
   267  }
   268  
   269  func DecodeHex(t *testing.T, str string) []byte {
   270  	b, err := hex.DecodeString(str)
   271  	if err != nil {
   272  		t.Fatalf("git/odb/pack: unexpected hex.DecodeString error: %s", err)
   273  	}
   274  
   275  	return b
   276  }