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 }