package repository import ( "math/rand" "testing" "time" "github.com/restic/restic/internal/restic" rtest "github.com/restic/restic/internal/test" ) func TestIndexMapBasic(t *testing.T) { t.Parallel() var ( id restic.ID m indexMap r = rand.New(rand.NewSource(98765)) ) for i := 1; i <= 400; i++ { r.Read(id[:]) rtest.Assert(t, m.get(id) == nil, "%v retrieved but not added", id) m.add(id, 0, 0, 0) rtest.Assert(t, m.get(id) != nil, "%v added but not retrieved", id) rtest.Equals(t, uint(i), m.len()) } } func TestIndexMapForeach(t *testing.T) { t.Parallel() const N = 10 var m indexMap // Don't crash on empty map. m.foreach(func(*indexEntry) bool { return true }) for i := 0; i < N; i++ { var id restic.ID id[0] = byte(i) m.add(id, i, uint32(i), uint32(i)) } seen := make(map[int]struct{}) m.foreach(func(e *indexEntry) bool { i := int(e.id[0]) rtest.Assert(t, i < N, "unknown id %v in indexMap", e.id) rtest.Equals(t, i, e.packIndex) rtest.Equals(t, i, int(e.length)) rtest.Equals(t, i, int(e.offset)) seen[i] = struct{}{} return true }) rtest.Equals(t, N, len(seen)) ncalls := 0 m.foreach(func(*indexEntry) bool { ncalls++ return false }) rtest.Equals(t, 1, ncalls) } func TestIndexMapForeachWithID(t *testing.T) { t.Parallel() const ndups = 3 var ( id restic.ID m indexMap r = rand.New(rand.NewSource(1234321)) ) r.Read(id[:]) // No result (and no crash) for empty map. n := 0 m.foreachWithID(id, func(*indexEntry) { n++ }) rtest.Equals(t, 0, n) // Test insertion and retrieval of duplicates. for i := 0; i < ndups; i++ { m.add(id, i, 0, 0) } for i := 0; i < 100; i++ { var otherid restic.ID r.Read(otherid[:]) m.add(otherid, -1, 0, 0) } n = 0 var packs [ndups]bool m.foreachWithID(id, func(e *indexEntry) { packs[e.packIndex] = true n++ }) rtest.Equals(t, ndups, n) for i := range packs { rtest.Assert(t, packs[i], "duplicate from pack %d not retrieved", i) } } func TestIndexMapHash(t *testing.T) { t.Parallel() var m1, m2 indexMap id := restic.NewRandomID() // Add to both maps to initialize them. m1.add(id, 0, 0, 0) m2.add(id, 0, 0, 0) h1 := m1.hash(id) h2 := m2.hash(id) rtest.Equals(t, len(m1.buckets), len(m2.buckets)) // just to be sure if h1 == h2 { // The probability of the zero key should be 2^(-128). if m1.key0 == 0 && m1.key1 == 0 { t.Error("siphash key not set for m1") } if m2.key0 == 0 && m2.key1 == 0 { t.Error("siphash key not set for m2") } } } func BenchmarkIndexMapHash(b *testing.B) { var m indexMap m.add(restic.ID{}, 0, 0, 0) // Trigger lazy initialization. ids := make([]restic.ID, 128) // 4 KiB. r := rand.New(rand.NewSource(time.Now().UnixNano())) for i := range ids { r.Read(ids[i][:]) } b.ReportAllocs() b.SetBytes(int64(len(restic.ID{}) * len(ids))) b.ResetTimer() for i := 0; i < b.N; i++ { for _, id := range ids { m.hash(id) } } }