github.com/stffabi/git-lfs@v2.3.5-0.20180214015214-8eeaa8d88902+incompatible/git/odb/tree_test.go (about) 1 package odb 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "sort" 8 "strconv" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestTreeReturnsCorrectObjectType(t *testing.T) { 16 assert.Equal(t, TreeObjectType, new(Tree).Type()) 17 } 18 19 func TestTreeEncoding(t *testing.T) { 20 tree := &Tree{ 21 Entries: []*TreeEntry{ 22 { 23 Name: "a.dat", 24 Oid: []byte("aaaaaaaaaaaaaaaaaaaa"), 25 Filemode: 0100644, 26 }, 27 { 28 Name: "subdir", 29 Oid: []byte("bbbbbbbbbbbbbbbbbbbb"), 30 Filemode: 040000, 31 }, 32 { 33 Name: "submodule", 34 Oid: []byte("cccccccccccccccccccc"), 35 Filemode: 0160000, 36 }, 37 }, 38 } 39 40 buf := new(bytes.Buffer) 41 42 n, err := tree.Encode(buf) 43 assert.Nil(t, err) 44 assert.NotEqual(t, 0, n) 45 46 assertTreeEntry(t, buf, "a.dat", []byte("aaaaaaaaaaaaaaaaaaaa"), 0100644) 47 assertTreeEntry(t, buf, "subdir", []byte("bbbbbbbbbbbbbbbbbbbb"), 040000) 48 assertTreeEntry(t, buf, "submodule", []byte("cccccccccccccccccccc"), 0160000) 49 50 assert.Equal(t, 0, buf.Len()) 51 } 52 53 func TestTreeDecoding(t *testing.T) { 54 from := new(bytes.Buffer) 55 fmt.Fprintf(from, "%s %s\x00%s", 56 strconv.FormatInt(int64(0100644), 8), 57 "a.dat", []byte("aaaaaaaaaaaaaaaaaaaa")) 58 fmt.Fprintf(from, "%s %s\x00%s", 59 strconv.FormatInt(int64(040000), 8), 60 "subdir", []byte("bbbbbbbbbbbbbbbbbbbb")) 61 fmt.Fprintf(from, "%s %s\x00%s", 62 strconv.FormatInt(int64(0120000), 8), 63 "symlink", []byte("cccccccccccccccccccc")) 64 fmt.Fprintf(from, "%s %s\x00%s", 65 strconv.FormatInt(int64(0160000), 8), 66 "submodule", []byte("dddddddddddddddddddd")) 67 68 flen := from.Len() 69 70 tree := new(Tree) 71 n, err := tree.Decode(from, int64(flen)) 72 73 assert.Nil(t, err) 74 assert.Equal(t, flen, n) 75 76 require.Equal(t, 4, len(tree.Entries)) 77 assert.Equal(t, &TreeEntry{ 78 Name: "a.dat", 79 Oid: []byte("aaaaaaaaaaaaaaaaaaaa"), 80 Filemode: 0100644, 81 }, tree.Entries[0]) 82 assert.Equal(t, &TreeEntry{ 83 Name: "subdir", 84 Oid: []byte("bbbbbbbbbbbbbbbbbbbb"), 85 Filemode: 040000, 86 }, tree.Entries[1]) 87 assert.Equal(t, &TreeEntry{ 88 Name: "symlink", 89 Oid: []byte("cccccccccccccccccccc"), 90 Filemode: 0120000, 91 }, tree.Entries[2]) 92 assert.Equal(t, &TreeEntry{ 93 Name: "submodule", 94 Oid: []byte("dddddddddddddddddddd"), 95 Filemode: 0160000, 96 }, tree.Entries[3]) 97 } 98 99 func TestTreeDecodingShaBoundary(t *testing.T) { 100 var from bytes.Buffer 101 102 fmt.Fprintf(&from, "%s %s\x00%s", 103 strconv.FormatInt(int64(0100644), 8), 104 "a.dat", []byte("aaaaaaaaaaaaaaaaaaaa")) 105 106 flen := from.Len() 107 108 tree := new(Tree) 109 n, err := tree.Decode(bufio.NewReaderSize(&from, flen-2), int64(flen)) 110 111 assert.Nil(t, err) 112 assert.Equal(t, flen, n) 113 114 require.Len(t, tree.Entries, 1) 115 assert.Equal(t, &TreeEntry{ 116 Name: "a.dat", 117 Oid: []byte("aaaaaaaaaaaaaaaaaaaa"), 118 Filemode: 0100644, 119 }, tree.Entries[0]) 120 } 121 122 func TestTreeMergeReplaceElements(t *testing.T) { 123 e1 := &TreeEntry{Name: "a", Filemode: 0100644, Oid: []byte{0x1}} 124 e2 := &TreeEntry{Name: "b", Filemode: 0100644, Oid: []byte{0x2}} 125 e3 := &TreeEntry{Name: "c", Filemode: 0100644, Oid: []byte{0x3}} 126 127 e4 := &TreeEntry{Name: "b", Filemode: 0100644, Oid: []byte{0x4}} 128 e5 := &TreeEntry{Name: "c", Filemode: 0100644, Oid: []byte{0x5}} 129 130 t1 := &Tree{Entries: []*TreeEntry{e1, e2, e3}} 131 132 t2 := t1.Merge(e4, e5) 133 134 require.Len(t, t1.Entries, 3) 135 assert.True(t, bytes.Equal(t1.Entries[0].Oid, []byte{0x1})) 136 assert.True(t, bytes.Equal(t1.Entries[1].Oid, []byte{0x2})) 137 assert.True(t, bytes.Equal(t1.Entries[2].Oid, []byte{0x3})) 138 139 require.Len(t, t2.Entries, 3) 140 assert.True(t, bytes.Equal(t2.Entries[0].Oid, []byte{0x1})) 141 assert.True(t, bytes.Equal(t2.Entries[1].Oid, []byte{0x4})) 142 assert.True(t, bytes.Equal(t2.Entries[2].Oid, []byte{0x5})) 143 } 144 145 func TestMergeInsertElementsInSubtreeOrder(t *testing.T) { 146 e1 := &TreeEntry{Name: "a-b", Filemode: 0100644, Oid: []byte{0x1}} 147 e2 := &TreeEntry{Name: "a", Filemode: 040000, Oid: []byte{0x2}} 148 e3 := &TreeEntry{Name: "a=", Filemode: 0100644, Oid: []byte{0x3}} 149 e4 := &TreeEntry{Name: "a-", Filemode: 0100644, Oid: []byte{0x4}} 150 151 t1 := &Tree{Entries: []*TreeEntry{e1, e2, e3}} 152 t2 := t1.Merge(e4) 153 154 require.Len(t, t1.Entries, 3) 155 assert.True(t, bytes.Equal(t1.Entries[0].Oid, []byte{0x1})) 156 assert.True(t, bytes.Equal(t1.Entries[1].Oid, []byte{0x2})) 157 assert.True(t, bytes.Equal(t1.Entries[2].Oid, []byte{0x3})) 158 159 assert.True(t, bytes.Equal(t2.Entries[0].Oid, []byte{0x4})) 160 assert.True(t, bytes.Equal(t2.Entries[1].Oid, []byte{0x1})) 161 assert.True(t, bytes.Equal(t2.Entries[2].Oid, []byte{0x2})) 162 assert.True(t, bytes.Equal(t2.Entries[3].Oid, []byte{0x3})) 163 } 164 165 type TreeEntryTypeTestCase struct { 166 Filemode int32 167 Expected ObjectType 168 } 169 170 func (c *TreeEntryTypeTestCase) Assert(t *testing.T) { 171 e := &TreeEntry{Filemode: c.Filemode} 172 173 got := e.Type() 174 175 assert.Equal(t, c.Expected, got, 176 "git/odb: expected type: %s, got: %s", c.Expected, got) 177 } 178 179 func TestTreeEntryTypeResolution(t *testing.T) { 180 for desc, c := range map[string]*TreeEntryTypeTestCase{ 181 "blob": {0100644, BlobObjectType}, 182 "subtree": {040000, TreeObjectType}, 183 "symlink": {0120000, BlobObjectType}, 184 "commit": {0160000, CommitObjectType}, 185 } { 186 t.Run(desc, c.Assert) 187 } 188 } 189 190 func TestTreeEntryTypeResolutionUnknown(t *testing.T) { 191 e := &TreeEntry{Filemode: -1} 192 193 defer func() { 194 if err := recover(); err == nil { 195 t.Fatal("git/odb: expected panic(), got none") 196 } else { 197 assert.Equal(t, "git/odb: unknown object type: -1", err) 198 } 199 }() 200 201 e.Type() 202 } 203 204 func TestSubtreeOrder(t *testing.T) { 205 // The below list (e1, e2, ..., e5) is entered in subtree order: that 206 // is, lexicographically byte-ordered as if blobs end in a '\0', and 207 // sub-trees end in a '/'. 208 // 209 // See: 210 // http://public-inbox.org/git/7vac6jfzem.fsf@assigned-by-dhcp.cox.net 211 e1 := &TreeEntry{Filemode: 0100644, Name: "a-"} 212 e2 := &TreeEntry{Filemode: 0100644, Name: "a-b"} 213 e3 := &TreeEntry{Filemode: 040000, Name: "a"} 214 e4 := &TreeEntry{Filemode: 0100644, Name: "a="} 215 e5 := &TreeEntry{Filemode: 0100644, Name: "a=b"} 216 217 // Create a set of entries in the wrong order: 218 entries := []*TreeEntry{e3, e4, e1, e5, e2} 219 220 sort.Sort(SubtreeOrder(entries)) 221 222 // Assert that they are in the correct order after sorting in sub-tree 223 // order: 224 require.Len(t, entries, 5) 225 assert.Equal(t, "a-", entries[0].Name) 226 assert.Equal(t, "a-b", entries[1].Name) 227 assert.Equal(t, "a", entries[2].Name) 228 assert.Equal(t, "a=", entries[3].Name) 229 assert.Equal(t, "a=b", entries[4].Name) 230 } 231 232 func TestSubtreeOrderReturnsEmptyForOutOfBounds(t *testing.T) { 233 o := SubtreeOrder([]*TreeEntry{{Name: "a"}}) 234 235 assert.Equal(t, "", o.Name(len(o)+1)) 236 } 237 238 func TestSubtreeOrderReturnsEmptyForNilElements(t *testing.T) { 239 o := SubtreeOrder([]*TreeEntry{nil}) 240 241 assert.Equal(t, "", o.Name(0)) 242 } 243 244 func TestTreeEqualReturnsTrueWithUnchangedContents(t *testing.T) { 245 t1 := &Tree{Entries: []*TreeEntry{ 246 {Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)}, 247 }} 248 t2 := &Tree{Entries: []*TreeEntry{ 249 {Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)}, 250 }} 251 252 assert.True(t, t1.Equal(t2)) 253 } 254 255 func TestTreeEqualReturnsFalseWithChangedContents(t *testing.T) { 256 t1 := &Tree{Entries: []*TreeEntry{ 257 {Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)}, 258 {Name: "b.dat", Filemode: 0100644, Oid: make([]byte, 20)}, 259 }} 260 t2 := &Tree{Entries: []*TreeEntry{ 261 {Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)}, 262 {Name: "c.dat", Filemode: 0100644, Oid: make([]byte, 20)}, 263 }} 264 265 assert.False(t, t1.Equal(t2)) 266 } 267 268 func TestTreeEqualReturnsTrueWhenOneTreeIsNil(t *testing.T) { 269 t1 := &Tree{Entries: []*TreeEntry{ 270 {Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)}, 271 }} 272 t2 := (*Tree)(nil) 273 274 assert.False(t, t1.Equal(t2)) 275 assert.False(t, t2.Equal(t1)) 276 } 277 278 func TestTreeEqualReturnsTrueWhenBothTreesAreNil(t *testing.T) { 279 t1 := (*Tree)(nil) 280 t2 := (*Tree)(nil) 281 282 assert.True(t, t1.Equal(t2)) 283 } 284 285 func TestTreeEntryEqualReturnsTrueWhenEntriesAreTheSame(t *testing.T) { 286 e1 := &TreeEntry{Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)} 287 e2 := &TreeEntry{Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)} 288 289 assert.True(t, e1.Equal(e2)) 290 } 291 292 func TestTreeEntryEqualReturnsFalseWhenDifferentNames(t *testing.T) { 293 e1 := &TreeEntry{Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)} 294 e2 := &TreeEntry{Name: "b.dat", Filemode: 0100644, Oid: make([]byte, 20)} 295 296 assert.False(t, e1.Equal(e2)) 297 } 298 299 func TestTreeEntryEqualReturnsFalseWhenDifferentOids(t *testing.T) { 300 e1 := &TreeEntry{Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)} 301 e2 := &TreeEntry{Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)} 302 303 e2.Oid[0] = 1 304 305 assert.False(t, e1.Equal(e2)) 306 } 307 308 func TestTreeEntryEqualReturnsFalseWhenDifferentFilemodes(t *testing.T) { 309 e1 := &TreeEntry{Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)} 310 e2 := &TreeEntry{Name: "a.dat", Filemode: 0100755, Oid: make([]byte, 20)} 311 312 assert.False(t, e1.Equal(e2)) 313 } 314 315 func TestTreeEntryEqualReturnsFalseWhenOneEntryIsNil(t *testing.T) { 316 e1 := &TreeEntry{Name: "a.dat", Filemode: 0100644, Oid: make([]byte, 20)} 317 e2 := (*TreeEntry)(nil) 318 319 assert.False(t, e1.Equal(e2)) 320 } 321 322 func TestTreeEntryEqualReturnsTrueWhenBothEntriesAreNil(t *testing.T) { 323 e1 := (*TreeEntry)(nil) 324 e2 := (*TreeEntry)(nil) 325 326 assert.True(t, e1.Equal(e2)) 327 } 328 329 func assertTreeEntry(t *testing.T, buf *bytes.Buffer, 330 name string, oid []byte, mode int32) { 331 332 fmode, err := buf.ReadBytes(' ') 333 assert.Nil(t, err) 334 assert.Equal(t, []byte(strconv.FormatInt(int64(mode), 8)+" "), fmode) 335 336 fname, err := buf.ReadBytes('\x00') 337 assert.Nil(t, err) 338 assert.Equal(t, []byte(name+"\x00"), fname) 339 340 var sha [20]byte 341 _, err = buf.Read(sha[:]) 342 assert.Nil(t, err) 343 assert.Equal(t, oid, sha[:]) 344 }