blob: 493c11fa0b35c344f7c47a441b0e6b539e57ac1e [file] [log] [blame]
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package adt
import (
"math/rand"
"testing"
"time"
)
func TestIntervalTreeIntersects(t *testing.T) {
ivt := &IntervalTree{}
ivt.Insert(NewStringInterval("1", "3"), 123)
if ivt.Intersects(NewStringPoint("0")) {
t.Errorf("contains 0")
}
if !ivt.Intersects(NewStringPoint("1")) {
t.Errorf("missing 1")
}
if !ivt.Intersects(NewStringPoint("11")) {
t.Errorf("missing 11")
}
if !ivt.Intersects(NewStringPoint("2")) {
t.Errorf("missing 2")
}
if ivt.Intersects(NewStringPoint("3")) {
t.Errorf("contains 3")
}
}
func TestIntervalTreeStringAffine(t *testing.T) {
ivt := &IntervalTree{}
ivt.Insert(NewStringAffineInterval("8", ""), 123)
if !ivt.Intersects(NewStringAffinePoint("9")) {
t.Errorf("missing 9")
}
if ivt.Intersects(NewStringAffinePoint("7")) {
t.Errorf("contains 7")
}
}
func TestIntervalTreeStab(t *testing.T) {
ivt := &IntervalTree{}
ivt.Insert(NewStringInterval("0", "1"), 123)
ivt.Insert(NewStringInterval("0", "2"), 456)
ivt.Insert(NewStringInterval("5", "6"), 789)
ivt.Insert(NewStringInterval("6", "8"), 999)
ivt.Insert(NewStringInterval("0", "3"), 0)
if ivt.root.max.Compare(StringComparable("8")) != 0 {
t.Fatalf("wrong root max got %v, expected 8", ivt.root.max)
}
if x := len(ivt.Stab(NewStringPoint("0"))); x != 3 {
t.Errorf("got %d, expected 3", x)
}
if x := len(ivt.Stab(NewStringPoint("1"))); x != 2 {
t.Errorf("got %d, expected 2", x)
}
if x := len(ivt.Stab(NewStringPoint("2"))); x != 1 {
t.Errorf("got %d, expected 1", x)
}
if x := len(ivt.Stab(NewStringPoint("3"))); x != 0 {
t.Errorf("got %d, expected 0", x)
}
if x := len(ivt.Stab(NewStringPoint("5"))); x != 1 {
t.Errorf("got %d, expected 1", x)
}
if x := len(ivt.Stab(NewStringPoint("55"))); x != 1 {
t.Errorf("got %d, expected 1", x)
}
if x := len(ivt.Stab(NewStringPoint("6"))); x != 1 {
t.Errorf("got %d, expected 1", x)
}
}
type xy struct {
x int64
y int64
}
func TestIntervalTreeRandom(t *testing.T) {
// generate unique intervals
ivs := make(map[xy]struct{})
ivt := &IntervalTree{}
maxv := 128
rand.Seed(time.Now().UnixNano())
for i := rand.Intn(maxv) + 1; i != 0; i-- {
x, y := int64(rand.Intn(maxv)), int64(rand.Intn(maxv))
if x > y {
t := x
x = y
y = t
} else if x == y {
y++
}
iv := xy{x, y}
if _, ok := ivs[iv]; ok {
// don't double insert
continue
}
ivt.Insert(NewInt64Interval(x, y), 123)
ivs[iv] = struct{}{}
}
for ab := range ivs {
for xy := range ivs {
v := xy.x + int64(rand.Intn(int(xy.y-xy.x)))
if slen := len(ivt.Stab(NewInt64Point(v))); slen == 0 {
t.Fatalf("expected %v stab non-zero for [%+v)", v, xy)
}
if !ivt.Intersects(NewInt64Point(v)) {
t.Fatalf("did not get %d as expected for [%+v)", v, xy)
}
}
if !ivt.Delete(NewInt64Interval(ab.x, ab.y)) {
t.Errorf("did not delete %v as expected", ab)
}
delete(ivs, ab)
}
if ivt.Len() != 0 {
t.Errorf("got ivt.Len() = %v, expected 0", ivt.Len())
}
}
// TestIntervalTreeSortedVisit tests that intervals are visited in sorted order.
func TestIntervalTreeSortedVisit(t *testing.T) {
tests := []struct {
ivls []Interval
visitRange Interval
}{
{
ivls: []Interval{NewInt64Interval(1, 10), NewInt64Interval(2, 5), NewInt64Interval(3, 6)},
visitRange: NewInt64Interval(0, 100),
},
{
ivls: []Interval{NewInt64Interval(1, 10), NewInt64Interval(10, 12), NewInt64Interval(3, 6)},
visitRange: NewInt64Interval(0, 100),
},
{
ivls: []Interval{NewInt64Interval(2, 3), NewInt64Interval(3, 4), NewInt64Interval(6, 7), NewInt64Interval(5, 6)},
visitRange: NewInt64Interval(0, 100),
},
{
ivls: []Interval{
NewInt64Interval(2, 3),
NewInt64Interval(2, 4),
NewInt64Interval(3, 7),
NewInt64Interval(2, 5),
NewInt64Interval(3, 8),
NewInt64Interval(3, 5),
},
visitRange: NewInt64Interval(0, 100),
},
}
for i, tt := range tests {
ivt := &IntervalTree{}
for _, ivl := range tt.ivls {
ivt.Insert(ivl, struct{}{})
}
last := tt.ivls[0].Begin
count := 0
chk := func(iv *IntervalValue) bool {
if last.Compare(iv.Ivl.Begin) > 0 {
t.Errorf("#%d: expected less than %d, got interval %+v", i, last, iv.Ivl)
}
last = iv.Ivl.Begin
count++
return true
}
ivt.Visit(tt.visitRange, chk)
if count != len(tt.ivls) {
t.Errorf("#%d: did not cover all intervals. expected %d, got %d", i, len(tt.ivls), count)
}
}
}
// TestIntervalTreeVisitExit tests that visiting can be stopped.
func TestIntervalTreeVisitExit(t *testing.T) {
ivls := []Interval{NewInt64Interval(1, 10), NewInt64Interval(2, 5), NewInt64Interval(3, 6), NewInt64Interval(4, 8)}
ivlRange := NewInt64Interval(0, 100)
tests := []struct {
f IntervalVisitor
wcount int
}{
{
f: func(n *IntervalValue) bool { return false },
wcount: 1,
},
{
f: func(n *IntervalValue) bool { return n.Ivl.Begin.Compare(ivls[0].Begin) <= 0 },
wcount: 2,
},
{
f: func(n *IntervalValue) bool { return n.Ivl.Begin.Compare(ivls[2].Begin) < 0 },
wcount: 3,
},
{
f: func(n *IntervalValue) bool { return true },
wcount: 4,
},
}
for i, tt := range tests {
ivt := &IntervalTree{}
for _, ivl := range ivls {
ivt.Insert(ivl, struct{}{})
}
count := 0
ivt.Visit(ivlRange, func(n *IntervalValue) bool {
count++
return tt.f(n)
})
if count != tt.wcount {
t.Errorf("#%d: expected count %d, got %d", i, tt.wcount, count)
}
}
}
// TestIntervalTreeContains tests that contains returns true iff the ivt maps the entire interval.
func TestIntervalTreeContains(t *testing.T) {
tests := []struct {
ivls []Interval
chkIvl Interval
wContains bool
}{
{
ivls: []Interval{NewInt64Interval(1, 10)},
chkIvl: NewInt64Interval(0, 100),
wContains: false,
},
{
ivls: []Interval{NewInt64Interval(1, 10)},
chkIvl: NewInt64Interval(1, 10),
wContains: true,
},
{
ivls: []Interval{NewInt64Interval(1, 10)},
chkIvl: NewInt64Interval(2, 8),
wContains: true,
},
{
ivls: []Interval{NewInt64Interval(1, 5), NewInt64Interval(6, 10)},
chkIvl: NewInt64Interval(1, 10),
wContains: false,
},
{
ivls: []Interval{NewInt64Interval(1, 5), NewInt64Interval(3, 10)},
chkIvl: NewInt64Interval(1, 10),
wContains: true,
},
{
ivls: []Interval{NewInt64Interval(1, 4), NewInt64Interval(4, 7), NewInt64Interval(3, 10)},
chkIvl: NewInt64Interval(1, 10),
wContains: true,
},
{
ivls: []Interval{},
chkIvl: NewInt64Interval(1, 10),
wContains: false,
},
}
for i, tt := range tests {
ivt := &IntervalTree{}
for _, ivl := range tt.ivls {
ivt.Insert(ivl, struct{}{})
}
if v := ivt.Contains(tt.chkIvl); v != tt.wContains {
t.Errorf("#%d: ivt.Contains got %v, expected %v", i, v, tt.wContains)
}
}
}