8af1e4622f15abc9e4f36f33000ab1f50e6c4cd7
[scm/test.git] / git / odb / pack / index_test.go
1 package pack
2
3 import (
4         "bytes"
5         "encoding/binary"
6         "testing"
7
8         "github.com/git-lfs/git-lfs/errors"
9
10         "github.com/stretchr/testify/assert"
11 )
12
13 var (
14         idx *Index
15 )
16
17 func TestIndexEntrySearch(t *testing.T) {
18         e, err := idx.Entry([]byte{
19                 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
20                 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
21         })
22
23         assert.NoError(t, err)
24         assert.EqualValues(t, 6, e.PackOffset)
25 }
26
27 func TestIndexEntrySearchClampLeft(t *testing.T) {
28         e, err := idx.Entry([]byte{
29                 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
30                 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
31         })
32
33         assert.NoError(t, err)
34         assert.EqualValues(t, 0, e.PackOffset)
35 }
36
37 func TestIndexEntrySearchClampRight(t *testing.T) {
38         e, err := idx.Entry([]byte{
39                 0xff, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
40                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
41         })
42
43         assert.NoError(t, err)
44         assert.EqualValues(t, 0x4ff, e.PackOffset)
45 }
46
47 func TestIndexSearchOutOfBounds(t *testing.T) {
48         e, err := idx.Entry([]byte{
49                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
50                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
51         })
52
53         assert.True(t, IsNotFound(err), "expected err to be 'not found'")
54         assert.Nil(t, e)
55 }
56
57 func TestIndexEntryNotFound(t *testing.T) {
58         e, err := idx.Entry([]byte{
59                 0x1, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
60                 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6,
61         })
62
63         assert.True(t, IsNotFound(err), "expected err to be 'not found'")
64         assert.Nil(t, e)
65 }
66
67 func TestIndexCount(t *testing.T) {
68         fanout := make([]uint32, 256)
69         for i := 0; i < len(fanout); i++ {
70                 fanout[i] = uint32(i)
71         }
72
73         idx := &Index{fanout: fanout}
74
75         assert.EqualValues(t, 255, idx.Count())
76 }
77
78 func TestIndexIsNotFound(t *testing.T) {
79         assert.True(t, IsNotFound(errNotFound),
80                 "expected 'errNotFound' to satisfy 'IsNotFound()'")
81 }
82
83 func TestIndexIsNotFoundForOtherErrors(t *testing.T) {
84         assert.False(t, IsNotFound(errors.New("git/odb/pack: misc")),
85                 "expected 'err' not to satisfy 'IsNotFound()'")
86 }
87
88 // init generates some fixture data and then constructs an *Index instance using
89 // it.
90 func init() {
91         // eps is the number of SHA1 names generated under each 0x<t> slot.
92         const eps = 5
93
94         hdr := []byte{
95                 0xff, 0x74, 0x4f, 0x63, // Index file v2+ magic header
96                 0x00, 0x00, 0x00, 0x02, // 4-byte version indicator
97         }
98
99         // Create a fanout table using uint32s (later marshalled using
100         // binary.BigEndian).
101         //
102         // Since we have an even distribution of SHA1s in the generated index,
103         // each entry will increase by the number of entries per slot (see: eps
104         // above).
105         fanout := make([]uint32, indexFanoutEntries)
106         for i := 0; i < len(fanout); i++ {
107                 // Begin the index at (i+1), since the fanout table mandates
108                 // objects less than the value at index "i".
109                 fanout[i] = uint32((i + 1) * eps)
110         }
111
112         offs := make([]uint32, 0, 256*eps)
113         crcs := make([]uint32, 0, 256*eps)
114
115         names := make([][]byte, 0, 256*eps)
116         for i := 0; i < 256; i++ {
117                 // For each name, generate a unique SHA using the prefix "i",
118                 // and then suffix "j".
119                 //
120                 // In other words, when i=1, we will generate:
121                 //   []byte{0x1 0x0 0x0 0x0 ...}
122                 //   []byte{0x1 0x1 0x1 0x1 ...}
123                 //   []byte{0x1 0x2 0x2 0x2 ...}
124                 //
125                 // and etc.
126                 for j := 0; j < eps; j++ {
127                         var sha [20]byte
128
129                         sha[0] = byte(i)
130                         for r := 1; r < len(sha); r++ {
131                                 sha[r] = byte(j)
132                         }
133
134                         cpy := make([]byte, len(sha))
135                         copy(cpy, sha[:])
136
137                         names = append(names, cpy)
138                         offs = append(offs, uint32((i*eps)+j))
139                         crcs = append(crcs, 0)
140                 }
141         }
142
143         // Create a buffer to hold the index contents:
144         buf := bytes.NewBuffer(hdr)
145
146         // Write each value in the fanout table using a 32bit network byte-order
147         // integer.
148         for _, f := range fanout {
149                 binary.Write(buf, binary.BigEndian, f)
150         }
151         // Write each SHA1 name to the table next.
152         for _, name := range names {
153                 buf.Write(name)
154         }
155         // Then write each of the CRC values in network byte-order as a 32bit
156         // unsigned integer.
157         for _, crc := range crcs {
158                 binary.Write(buf, binary.BigEndian, crc)
159         }
160         // Do the same with the offsets.
161         for _, off := range offs {
162                 binary.Write(buf, binary.BigEndian, off)
163         }
164
165         idx = &Index{
166                 fanout: fanout,
167                 // version is unimportant here, use V2 since it's more common in
168                 // the wild.
169                 version: new(V2),
170
171                 // *bytes.Buffer does not implement io.ReaderAt, but
172                 // *bytes.Reader does.
173                 //
174                 // Call (*bytes.Buffer).Bytes() to get the data, and then
175                 // construct a new *bytes.Reader with it to implement
176                 // io.ReaderAt.
177                 r: bytes.NewReader(buf.Bytes()),
178         }
179 }