// 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< 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) } }) }