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  }