github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/internal/index/index_test.go (about) 1 package index 2 3 import ( 4 "context" 5 "math/rand" 6 "testing" 7 "time" 8 9 "github.com/restic/restic/internal/checker" 10 "github.com/restic/restic/internal/repository" 11 "github.com/restic/restic/internal/restic" 12 "github.com/restic/restic/internal/test" 13 ) 14 15 var ( 16 snapshotTime = time.Unix(1470492820, 207401672) 17 depth = 3 18 ) 19 20 func createFilledRepo(t testing.TB, snapshots int, dup float32) (restic.Repository, func()) { 21 repo, cleanup := repository.TestRepository(t) 22 23 for i := 0; i < 3; i++ { 24 restic.TestCreateSnapshot(t, repo, snapshotTime.Add(time.Duration(i)*time.Second), depth, dup) 25 } 26 27 return repo, cleanup 28 } 29 30 func validateIndex(t testing.TB, repo restic.Repository, idx *Index) { 31 for id := range repo.List(context.TODO(), restic.DataFile) { 32 p, ok := idx.Packs[id] 33 if !ok { 34 t.Errorf("pack %v missing from index", id.Str()) 35 } 36 37 if !p.ID.Equal(id) { 38 t.Errorf("pack %v has invalid ID: want %v, got %v", id.Str(), id, p.ID) 39 } 40 } 41 } 42 43 func TestIndexNew(t *testing.T) { 44 repo, cleanup := createFilledRepo(t, 3, 0) 45 defer cleanup() 46 47 idx, _, err := New(context.TODO(), repo, restic.NewIDSet(), nil) 48 if err != nil { 49 t.Fatalf("New() returned error %v", err) 50 } 51 52 if idx == nil { 53 t.Fatalf("New() returned nil index") 54 } 55 56 validateIndex(t, repo, idx) 57 } 58 59 func TestIndexLoad(t *testing.T) { 60 repo, cleanup := createFilledRepo(t, 3, 0) 61 defer cleanup() 62 63 loadIdx, err := Load(context.TODO(), repo, nil) 64 if err != nil { 65 t.Fatalf("Load() returned error %v", err) 66 } 67 68 if loadIdx == nil { 69 t.Fatalf("Load() returned nil index") 70 } 71 72 validateIndex(t, repo, loadIdx) 73 74 newIdx, _, err := New(context.TODO(), repo, restic.NewIDSet(), nil) 75 if err != nil { 76 t.Fatalf("New() returned error %v", err) 77 } 78 79 if len(loadIdx.Packs) != len(newIdx.Packs) { 80 t.Errorf("number of packs does not match: want %v, got %v", 81 len(loadIdx.Packs), len(newIdx.Packs)) 82 } 83 84 validateIndex(t, repo, newIdx) 85 86 for packID, packNew := range newIdx.Packs { 87 packLoad, ok := loadIdx.Packs[packID] 88 89 if !ok { 90 t.Errorf("loaded index does not list pack %v", packID.Str()) 91 continue 92 } 93 94 if len(packNew.Entries) != len(packLoad.Entries) { 95 t.Errorf(" number of entries in pack %v does not match: %d != %d\n %v\n %v", 96 packID.Str(), len(packNew.Entries), len(packLoad.Entries), 97 packNew.Entries, packLoad.Entries) 98 continue 99 } 100 101 for _, entryNew := range packNew.Entries { 102 found := false 103 for _, entryLoad := range packLoad.Entries { 104 if !entryLoad.ID.Equal(entryNew.ID) { 105 continue 106 } 107 108 if entryLoad.Type != entryNew.Type { 109 continue 110 } 111 112 if entryLoad.Offset != entryNew.Offset { 113 continue 114 } 115 116 if entryLoad.Length != entryNew.Length { 117 continue 118 } 119 120 found = true 121 break 122 } 123 124 if !found { 125 t.Errorf("blob not found in loaded index: %v", entryNew) 126 } 127 } 128 } 129 } 130 131 func BenchmarkIndexNew(b *testing.B) { 132 repo, cleanup := createFilledRepo(b, 3, 0) 133 defer cleanup() 134 135 b.ResetTimer() 136 137 for i := 0; i < b.N; i++ { 138 idx, _, err := New(context.TODO(), repo, restic.NewIDSet(), nil) 139 140 if err != nil { 141 b.Fatalf("New() returned error %v", err) 142 } 143 144 if idx == nil { 145 b.Fatalf("New() returned nil index") 146 } 147 b.Logf("idx %v packs", len(idx.Packs)) 148 } 149 } 150 151 func BenchmarkIndexSave(b *testing.B) { 152 repo, cleanup := repository.TestRepository(b) 153 defer cleanup() 154 155 idx, _, err := New(context.TODO(), repo, restic.NewIDSet(), nil) 156 test.OK(b, err) 157 158 for i := 0; i < 8000; i++ { 159 entries := make([]restic.Blob, 0, 200) 160 for j := 0; j < cap(entries); j++ { 161 entries = append(entries, restic.Blob{ 162 ID: restic.NewRandomID(), 163 Length: 1000, 164 Offset: 5, 165 Type: restic.DataBlob, 166 }) 167 } 168 169 idx.AddPack(restic.NewRandomID(), 10000, entries) 170 } 171 172 b.ResetTimer() 173 174 for i := 0; i < b.N; i++ { 175 id, err := idx.Save(context.TODO(), repo, nil) 176 if err != nil { 177 b.Fatalf("New() returned error %v", err) 178 } 179 180 b.Logf("saved as %v", id.Str()) 181 } 182 } 183 184 func TestIndexDuplicateBlobs(t *testing.T) { 185 repo, cleanup := createFilledRepo(t, 3, 0.01) 186 defer cleanup() 187 188 idx, _, err := New(context.TODO(), repo, restic.NewIDSet(), nil) 189 if err != nil { 190 t.Fatal(err) 191 } 192 193 dups := idx.DuplicateBlobs() 194 if len(dups) == 0 { 195 t.Errorf("no duplicate blobs found") 196 } 197 t.Logf("%d packs, %d duplicate blobs", len(idx.Packs), len(dups)) 198 199 packs := idx.PacksForBlobs(dups) 200 if len(packs) == 0 { 201 t.Errorf("no packs with duplicate blobs found") 202 } 203 t.Logf("%d packs with duplicate blobs", len(packs)) 204 } 205 206 func loadIndex(t testing.TB, repo restic.Repository) *Index { 207 idx, err := Load(context.TODO(), repo, nil) 208 if err != nil { 209 t.Fatalf("Load() returned error %v", err) 210 } 211 212 return idx 213 } 214 215 func TestSave(t *testing.T) { 216 repo, cleanup := createFilledRepo(t, 3, 0) 217 defer cleanup() 218 219 idx := loadIndex(t, repo) 220 221 packs := make(map[restic.ID][]restic.Blob) 222 for id := range idx.Packs { 223 if rand.Float32() < 0.5 { 224 packs[id] = idx.Packs[id].Entries 225 } 226 } 227 228 t.Logf("save %d/%d packs in a new index\n", len(packs), len(idx.Packs)) 229 230 id, err := Save(context.TODO(), repo, packs, idx.IndexIDs.List()) 231 if err != nil { 232 t.Fatalf("unable to save new index: %v", err) 233 } 234 235 t.Logf("new index saved as %v", id.Str()) 236 237 for id := range idx.IndexIDs { 238 t.Logf("remove index %v", id.Str()) 239 h := restic.Handle{Type: restic.IndexFile, Name: id.String()} 240 err = repo.Backend().Remove(context.TODO(), h) 241 if err != nil { 242 t.Errorf("error removing index %v: %v", id, err) 243 } 244 } 245 246 idx2 := loadIndex(t, repo) 247 t.Logf("load new index with %d packs", len(idx2.Packs)) 248 249 if len(idx2.Packs) != len(packs) { 250 t.Errorf("wrong number of packs in new index, want %d, got %d", len(packs), len(idx2.Packs)) 251 } 252 253 for id := range packs { 254 if _, ok := idx2.Packs[id]; !ok { 255 t.Errorf("pack %v is not contained in new index", id.Str()) 256 } 257 } 258 259 for id := range idx2.Packs { 260 if _, ok := packs[id]; !ok { 261 t.Errorf("pack %v is not contained in new index", id.Str()) 262 } 263 } 264 } 265 266 func TestIndexSave(t *testing.T) { 267 repo, cleanup := createFilledRepo(t, 3, 0) 268 defer cleanup() 269 270 idx := loadIndex(t, repo) 271 272 id, err := idx.Save(context.TODO(), repo, idx.IndexIDs.List()) 273 if err != nil { 274 t.Fatalf("unable to save new index: %v", err) 275 } 276 277 t.Logf("new index saved as %v", id.Str()) 278 279 for id := range idx.IndexIDs { 280 t.Logf("remove index %v", id.Str()) 281 h := restic.Handle{Type: restic.IndexFile, Name: id.String()} 282 err = repo.Backend().Remove(context.TODO(), h) 283 if err != nil { 284 t.Errorf("error removing index %v: %v", id, err) 285 } 286 } 287 288 idx2 := loadIndex(t, repo) 289 t.Logf("load new index with %d packs", len(idx2.Packs)) 290 291 checker := checker.New(repo) 292 hints, errs := checker.LoadIndex(context.TODO()) 293 for _, h := range hints { 294 t.Logf("hint: %v\n", h) 295 } 296 297 for _, err := range errs { 298 t.Errorf("checker found error: %v", err) 299 } 300 } 301 302 func TestIndexAddRemovePack(t *testing.T) { 303 repo, cleanup := createFilledRepo(t, 3, 0) 304 defer cleanup() 305 306 idx, err := Load(context.TODO(), repo, nil) 307 if err != nil { 308 t.Fatalf("Load() returned error %v", err) 309 } 310 311 packID := <-repo.List(context.TODO(), restic.DataFile) 312 313 t.Logf("selected pack %v", packID.Str()) 314 315 blobs := idx.Packs[packID].Entries 316 317 idx.RemovePack(packID) 318 319 if _, ok := idx.Packs[packID]; ok { 320 t.Errorf("removed pack %v found in index.Packs", packID.Str()) 321 } 322 323 for _, blob := range blobs { 324 h := restic.BlobHandle{ID: blob.ID, Type: blob.Type} 325 _, err := idx.FindBlob(h) 326 if err == nil { 327 t.Errorf("removed blob %v found in index", h) 328 } 329 } 330 } 331 332 // example index serialization from doc/Design.rst 333 var docExample = []byte(` 334 { 335 "supersedes": [ 336 "ed54ae36197f4745ebc4b54d10e0f623eaaaedd03013eb7ae90df881b7781452" 337 ], 338 "packs": [ 339 { 340 "id": "73d04e6125cf3c28a299cc2f3cca3b78ceac396e4fcf9575e34536b26782413c", 341 "blobs": [ 342 { 343 "id": "3ec79977ef0cf5de7b08cd12b874cd0f62bbaf7f07f3497a5b1bbcc8cb39b1ce", 344 "type": "data", 345 "offset": 0, 346 "length": 25 347 },{ 348 "id": "9ccb846e60d90d4eb915848add7aa7ea1e4bbabfc60e573db9f7bfb2789afbae", 349 "type": "tree", 350 "offset": 38, 351 "length": 100 352 }, 353 { 354 "id": "d3dc577b4ffd38cc4b32122cabf8655a0223ed22edfd93b353dc0c3f2b0fdf66", 355 "type": "data", 356 "offset": 150, 357 "length": 123 358 } 359 ] 360 } 361 ] 362 } 363 `) 364 365 func TestIndexLoadDocReference(t *testing.T) { 366 repo, cleanup := repository.TestRepository(t) 367 defer cleanup() 368 369 id, err := repo.SaveUnpacked(context.TODO(), restic.IndexFile, docExample) 370 if err != nil { 371 t.Fatalf("SaveUnpacked() returned error %v", err) 372 } 373 374 t.Logf("index saved as %v", id.Str()) 375 376 idx := loadIndex(t, repo) 377 378 blobID := restic.TestParseID("d3dc577b4ffd38cc4b32122cabf8655a0223ed22edfd93b353dc0c3f2b0fdf66") 379 locs, err := idx.FindBlob(restic.BlobHandle{ID: blobID, Type: restic.DataBlob}) 380 if err != nil { 381 t.Errorf("FindBlob() returned error %v", err) 382 } 383 384 if len(locs) != 1 { 385 t.Errorf("blob found %d times, expected just one", len(locs)) 386 } 387 388 l := locs[0] 389 if !l.ID.Equal(blobID) { 390 t.Errorf("blob IDs are not equal: %v != %v", l.ID, blobID) 391 } 392 393 if l.Type != restic.DataBlob { 394 t.Errorf("want type %v, got %v", restic.DataBlob, l.Type) 395 } 396 397 if l.Offset != 150 { 398 t.Errorf("wrong offset, want %d, got %v", 150, l.Offset) 399 } 400 401 if l.Length != 123 { 402 t.Errorf("wrong length, want %d, got %v", 123, l.Length) 403 } 404 }