diff options
Diffstat (limited to 'libgo/go/runtime/treap_test.go')
-rw-r--r-- | libgo/go/runtime/treap_test.go | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/libgo/go/runtime/treap_test.go b/libgo/go/runtime/treap_test.go new file mode 100644 index 0000000..110f51c --- /dev/null +++ b/libgo/go/runtime/treap_test.go @@ -0,0 +1,270 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime_test + +import ( + "fmt" + "runtime" + "testing" +) + +var spanDesc = map[uintptr]struct { + pages uintptr + scav bool +}{ + 0xc0000000: {2, false}, + 0xc0006000: {1, false}, + 0xc0010000: {8, false}, + 0xc0022000: {7, false}, + 0xc0034000: {4, true}, + 0xc0040000: {5, false}, + 0xc0050000: {5, true}, + 0xc0060000: {5000, false}, +} + +// Wrap the Treap one more time because go:notinheap doesn't +// actually follow a structure across package boundaries. +// +//go:notinheap +type treap struct { + runtime.Treap +} + +func maskMatchName(mask, match runtime.TreapIterType) string { + return fmt.Sprintf("%0*b-%0*b", runtime.TreapIterBits, uint8(mask), runtime.TreapIterBits, uint8(match)) +} + +func TestTreapFilter(t *testing.T) { + var iterTypes = [...]struct { + mask, match runtime.TreapIterType + filter runtime.TreapIterFilter // expected filter + }{ + {0, 0, 0xf}, + {runtime.TreapIterScav, 0, 0x5}, + {runtime.TreapIterScav, runtime.TreapIterScav, 0xa}, + {runtime.TreapIterScav | runtime.TreapIterHuge, runtime.TreapIterHuge, 0x4}, + {runtime.TreapIterScav | runtime.TreapIterHuge, 0, 0x1}, + {0, runtime.TreapIterScav, 0x0}, + } + for _, it := range iterTypes { + t.Run(maskMatchName(it.mask, it.match), func(t *testing.T) { + if f := runtime.TreapFilter(it.mask, it.match); f != it.filter { + t.Fatalf("got %#x, want %#x", f, it.filter) + } + }) + } +} + +// This test ensures that the treap implementation in the runtime +// maintains all stated invariants after different sequences of +// insert, removeSpan, find, and erase. Invariants specific to the +// treap data structure are checked implicitly: after each mutating +// operation, treap-related invariants are checked for the entire +// treap. +func TestTreap(t *testing.T) { + // Set up a bunch of spans allocated into mheap_. + // Also, derive a set of typeCounts of each type of span + // according to runtime.TreapIterType so we can verify against + // them later. + spans := make([]runtime.Span, 0, len(spanDesc)) + typeCounts := [1 << runtime.TreapIterBits][1 << runtime.TreapIterBits]int{} + for base, de := range spanDesc { + s := runtime.AllocSpan(base, de.pages, de.scav) + defer s.Free() + spans = append(spans, s) + + for i := runtime.TreapIterType(0); i < 1<<runtime.TreapIterBits; i++ { + for j := runtime.TreapIterType(0); j < 1<<runtime.TreapIterBits; j++ { + if s.MatchesIter(i, j) { + typeCounts[i][j]++ + } + } + } + } + t.Run("TypeCountsSanity", func(t *testing.T) { + // Just sanity check type counts for a few values. + check := func(mask, match runtime.TreapIterType, count int) { + tc := typeCounts[mask][match] + if tc != count { + name := maskMatchName(mask, match) + t.Fatalf("failed a sanity check for mask/match %s counts: got %d, wanted %d", name, tc, count) + } + } + check(0, 0, len(spanDesc)) + check(runtime.TreapIterScav, 0, 6) + check(runtime.TreapIterScav, runtime.TreapIterScav, 2) + }) + t.Run("Insert", func(t *testing.T) { + tr := treap{} + // Test just a very basic insert/remove for sanity. + tr.Insert(spans[0]) + tr.RemoveSpan(spans[0]) + }) + t.Run("FindTrivial", func(t *testing.T) { + tr := treap{} + // Test just a very basic find operation for sanity. + tr.Insert(spans[0]) + i := tr.Find(1) + if i.Span() != spans[0] { + t.Fatal("found unknown span in treap") + } + tr.RemoveSpan(spans[0]) + }) + t.Run("FindFirstFit", func(t *testing.T) { + // Run this 10 times, recreating the treap each time. + // Because of the non-deterministic structure of a treap, + // we'll be able to test different structures this way. + for i := 0; i < 10; i++ { + tr := runtime.Treap{} + for _, s := range spans { + tr.Insert(s) + } + i := tr.Find(5) + if i.Span().Base() != 0xc0010000 { + t.Fatalf("expected span at lowest address which could fit 5 pages, instead found span at %x", i.Span().Base()) + } + for _, s := range spans { + tr.RemoveSpan(s) + } + } + }) + t.Run("Iterate", func(t *testing.T) { + for mask := runtime.TreapIterType(0); mask < 1<<runtime.TreapIterBits; mask++ { + for match := runtime.TreapIterType(0); match < 1<<runtime.TreapIterBits; match++ { + iterName := maskMatchName(mask, match) + t.Run(iterName, func(t *testing.T) { + t.Run("StartToEnd", func(t *testing.T) { + // Ensure progressing an iterator actually goes over the whole treap + // from the start and that it iterates over the elements in order. + // Furthermore, ensure that it only iterates over the relevant parts + // of the treap. + // Finally, ensures that Start returns a valid iterator. + tr := treap{} + for _, s := range spans { + tr.Insert(s) + } + nspans := 0 + lastBase := uintptr(0) + for i := tr.Start(mask, match); i.Valid(); i = i.Next() { + nspans++ + if lastBase > i.Span().Base() { + t.Fatalf("not iterating in correct order: encountered base %x before %x", lastBase, i.Span().Base()) + } + lastBase = i.Span().Base() + if !i.Span().MatchesIter(mask, match) { + t.Fatalf("found non-matching span while iteration over mask/match %s: base %x", iterName, i.Span().Base()) + } + } + if nspans != typeCounts[mask][match] { + t.Fatal("failed to iterate forwards over full treap") + } + for _, s := range spans { + tr.RemoveSpan(s) + } + }) + t.Run("EndToStart", func(t *testing.T) { + // See StartToEnd tests. + tr := treap{} + for _, s := range spans { + tr.Insert(s) + } + nspans := 0 + lastBase := ^uintptr(0) + for i := tr.End(mask, match); i.Valid(); i = i.Prev() { + nspans++ + if lastBase < i.Span().Base() { + t.Fatalf("not iterating in correct order: encountered base %x before %x", lastBase, i.Span().Base()) + } + lastBase = i.Span().Base() + if !i.Span().MatchesIter(mask, match) { + t.Fatalf("found non-matching span while iteration over mask/match %s: base %x", iterName, i.Span().Base()) + } + } + if nspans != typeCounts[mask][match] { + t.Fatal("failed to iterate backwards over full treap") + } + for _, s := range spans { + tr.RemoveSpan(s) + } + }) + }) + } + } + t.Run("Prev", func(t *testing.T) { + // Test the iterator invariant that i.prev().next() == i. + tr := treap{} + for _, s := range spans { + tr.Insert(s) + } + i := tr.Start(0, 0).Next().Next() + p := i.Prev() + if !p.Valid() { + t.Fatal("i.prev() is invalid") + } + if p.Next().Span() != i.Span() { + t.Fatal("i.prev().next() != i") + } + for _, s := range spans { + tr.RemoveSpan(s) + } + }) + t.Run("Next", func(t *testing.T) { + // Test the iterator invariant that i.next().prev() == i. + tr := treap{} + for _, s := range spans { + tr.Insert(s) + } + i := tr.Start(0, 0).Next().Next() + n := i.Next() + if !n.Valid() { + t.Fatal("i.next() is invalid") + } + if n.Prev().Span() != i.Span() { + t.Fatal("i.next().prev() != i") + } + for _, s := range spans { + tr.RemoveSpan(s) + } + }) + }) + t.Run("EraseOne", func(t *testing.T) { + // Test that erasing one iterator correctly retains + // all relationships between elements. + tr := treap{} + for _, s := range spans { + tr.Insert(s) + } + i := tr.Start(0, 0).Next().Next().Next() + s := i.Span() + n := i.Next() + p := i.Prev() + tr.Erase(i) + if n.Prev().Span() != p.Span() { + t.Fatal("p, n := i.Prev(), i.Next(); n.prev() != p after i was erased") + } + if p.Next().Span() != n.Span() { + t.Fatal("p, n := i.Prev(), i.Next(); p.next() != n after i was erased") + } + tr.Insert(s) + for _, s := range spans { + tr.RemoveSpan(s) + } + }) + t.Run("EraseAll", func(t *testing.T) { + // Test that erasing iterators actually removes nodes from the treap. + tr := treap{} + for _, s := range spans { + tr.Insert(s) + } + for i := tr.Start(0, 0); i.Valid(); { + n := i.Next() + tr.Erase(i) + i = n + } + if size := tr.Size(); size != 0 { + t.Fatalf("should have emptied out treap, %d spans left", size) + } + }) +} |