2021-12-04 16:42:11 +00:00

682 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package stringset
import (
"reflect"
"testing"
)
// testValues contains an ordered sequence of ten set keys used for testing.
// The order of the keys must reflect the expected order of key listings.
var testValues = [10]string{
"eight",
"five",
"four",
"nine",
"one",
"seven",
"six",
"ten",
"three",
"two",
}
func testKeys(ixs ...int) (keys []string) {
for _, i := range ixs {
keys = append(keys, testValues[i])
}
return
}
func testSet(ixs ...int) Set { return New(testKeys(ixs...)...) }
func keyPos(key string) int {
for i, v := range testValues {
if v == key {
return i
}
}
return -1
}
func TestEmptiness(t *testing.T) {
var s Set
if !s.Empty() {
t.Errorf("nil Set is not reported empty: %v", s)
}
s = New()
if !s.Empty() {
t.Errorf("Empty Set is not reported empty: %v", s)
}
if s == nil {
t.Error("New() unexpectedly returned nil")
}
if s := testSet(0); s.Empty() {
t.Errorf("Nonempty Set is reported empty: %v", s)
}
}
func TestClone(t *testing.T) {
a := New(testValues[:]...)
b := testSet(1, 8, 5)
c := a.Clone()
c.Remove(b)
if c.Equals(a) {
t.Errorf("Unexpected equality: %v == %v", a, c)
} else {
t.Logf("%v.Clone().Remove(%v) == %v", a, b, c)
}
c.Update(b)
if !c.Equals(a) {
t.Errorf("Unexpected inequality: %v != %v", a, c)
}
var s Set
if got := s.Clone(); got != nil {
t.Errorf("Clone of nil set: got %v, want nil", got)
}
}
func TestUniqueness(t *testing.T) {
// Sets should not contain duplicates. Obviously this is impossible with
// the map implementation, but other representations are viable.
s := testSet(0, 5, 1, 2, 1, 3, 8, 4, 9, 4, 4, 6, 7, 2, 0, 0, 1, 4, 8, 4, 9)
if got, want := s.Len(), len(testValues); got != want {
t.Errorf("s.Len(): got %d, want %d [%v]", got, want, s)
}
// Keys should come out sorted.
if got := s.Elements(); !reflect.DeepEqual(got, testValues[:]) {
t.Errorf("s.Elements():\n got %+v,\nwant %+v", got, testValues)
}
}
func TestMembership(t *testing.T) {
s := testSet(0, 1, 2, 3, 4)
for i, v := range testValues {
if got, want := s.ContainsAny(v), i < 5; got != want {
t.Errorf("s.ContainsAny(%v): got %v, want %v", v, got, want)
}
}
// Test non-mutating selection.
if got, ok := s.Choose(func(s string) bool {
return s == testValues[0]
}); !ok {
t.Error("Choose(0): missing element")
} else {
t.Logf("Found %v for element 0", got)
}
if got, ok := s.Choose(func(string) bool { return false }); ok {
t.Errorf(`Choose(impossible): got %v, want ""`, got)
}
if got, ok := New().Choose(nil); ok {
t.Errorf(`Choose(nil): got %v, want ""`, got)
}
// Test mutating selection.
if got, ok := s.Pop(func(s string) bool {
return s == testValues[1]
}); !ok {
t.Error("Pop(1): missing element")
} else {
t.Logf("Found %v for element 1", got)
}
// A popped item is removed from the set.
if len(s) != 4 {
t.Errorf("Length after pop: got %d, want %d", len(s), 4)
}
// Pop of a nonexistent key returns not-found.
if got, ok := s.Pop(func(string) bool { return false }); ok {
t.Errorf(`Pop(impossible): got %v, want ""`, got)
}
// Pop from an empty set returns not-found.
if got, ok := New().Pop(nil); ok {
t.Errorf(`Pop(nil) on empty: got %v, want ""`, got)
}
}
func TestContainsAny(t *testing.T) {
set := New(testValues[2:]...)
tests := []struct {
keys []string
want bool
}{
{nil, false},
{[]string{}, false},
{testKeys(0), false},
{testKeys(1), false},
{testKeys(0, 1), false},
{testKeys(7), true},
{testKeys(8, 3, 4, 9), true},
{testKeys(0, 7, 1, 0), true},
}
t.Logf("Test set: %v", set)
for _, test := range tests {
got := set.ContainsAny(test.keys...)
if got != test.want {
t.Errorf("ContainsAny(%+v): got %v, want %v", test.keys, got, test.want)
}
}
}
func TestContainsAll(t *testing.T) {
//set := New("a", "e", "i", "y")
set := New(testValues[2:]...)
tests := []struct {
keys []string
want bool
}{
{nil, true},
{[]string{}, true},
{testKeys(2, 4, 6), true},
{testKeys(1, 3, 5, 7), false},
{testKeys(0), false},
{testKeys(5, 5, 5), true},
}
t.Logf("Test set: %v", set)
for _, test := range tests {
got := set.Contains(test.keys...)
if got != test.want {
t.Errorf("Contains(%+v): got %v, want %v", test.keys, got, test.want)
}
}
}
func TestIsSubset(t *testing.T) {
var empty Set
key := testSet(0, 2, 6, 7, 9)
for _, test := range [][]string{
{}, testKeys(2, 6), testKeys(0, 7, 9),
} {
probe := New(test...)
if !probe.IsSubset(key) {
t.Errorf("IsSubset %+v ⊂ %+v is false", probe, key)
}
if !empty.IsSubset(probe) { // ø is a subset of everything, including itself.
t.Errorf("IsSubset ø ⊂ %+v is false", probe)
}
}
}
func TestNotSubset(t *testing.T) {
tests := []struct {
probe, key Set
}{
{testSet(0), New()},
{testSet(0), testSet(1)},
{testSet(0, 1), testSet(1)},
{testSet(0, 2, 1), testSet(0, 2, 3)},
}
for _, test := range tests {
if test.probe.IsSubset(test.key) {
t.Errorf("IsSubset %+v ⊂ %+v is true", test.probe, test.key)
}
}
}
func TestEquality(t *testing.T) {
nat := New(testValues[:]...)
odd := testSet(1, 3, 4, 5, 8)
tests := []struct {
left, right Set
eq bool
}{
{nil, nil, true},
{nat, nat, true}, // Equality with the same value
{testSet(0), testSet(0), true}, // Equality with Different values
{testSet(0), nil, false},
{nat, odd, false},
{nil, testSet(0), false},
{testSet(0), testSet(1), false},
// Various set operations...
{nat.Intersect(odd), odd, true},
{odd, nat.Intersect(odd), true},
{odd.Intersect(nat), odd, true},
{odd, odd.Intersect(nat), true},
{nat.Intersect(nat), nat, true},
{nat, nat.Intersect(nat), true},
{nat.Union(odd), nat, true},
{nat, nat.Union(odd), true},
{odd.Diff(nat), odd, false},
{odd, odd.Diff(nat), false},
{odd.Diff(nat), nil, true},
{nil, odd.Diff(nat), true},
{testSet(0, 1, 2).Diff(testSet(2, 5, 6)), testSet(1).Union(testSet(0)), true},
}
for _, test := range tests {
if got := test.left.Equals(test.right); got != test.eq {
t.Errorf("%v.Equals(%v): got %v, want %v", test.left, test.right, got, test.eq)
}
}
}
func TestUnion(t *testing.T) {
vkeys := testKeys(0, 4)
vowels := testSet(4, 0)
consonants := testSet(1, 2, 3, 5, 6, 7, 8, 9)
if got := vowels.Union(nil).Elements(); !reflect.DeepEqual(got, vkeys) {
t.Errorf("Vowels ø: got %+v, want %+v", got, vkeys)
}
if got := New().Union(vowels).Elements(); !reflect.DeepEqual(got, vkeys) {
t.Errorf( Vowels: got %+v, want %+v", got, vkeys)
}
if got, want := vowels.Union(consonants).Elements(), testValues[:]; !reflect.DeepEqual(got, want) {
t.Errorf("Vowels Consonants: got %+v, want %+v", got, want)
}
}
func TestIntersect(t *testing.T) {
empty := New()
nat := New(testValues[:]...)
odd := testSet(1, 3, 5, 7, 9)
prime := testSet(2, 3, 5, 7)
tests := []struct {
left, right Set
want []string
}{
{empty, empty, nil},
{empty, nat, nil},
{nat, empty, nil},
{nat, nat, testValues[:]},
{nat, odd, testKeys(1, 3, 5, 7, 9)},
{odd, nat, testKeys(1, 3, 5, 7, 9)},
{odd, prime, testKeys(3, 5, 7)},
{prime, nat, testKeys(2, 3, 5, 7)},
}
for _, test := range tests {
got := test.left.Intersect(test.right).Elements()
if !reflect.DeepEqual(got, test.want) {
t.Errorf("%v ∩ %v: got %+v, want %+v", test.left, test.right, got, test.want)
} else if want, ok := len(test.want) != 0, test.left.Intersects(test.right); ok != want {
t.Errorf("%+v.Intersects(%+v): got %v, want %v", test.left, test.right, ok, want)
}
}
}
func TestDiff(t *testing.T) {
empty := New()
nat := New(testValues[:]...)
odd := testSet(1, 3, 5, 7, 9)
prime := testSet(2, 3, 5, 7)
tests := []struct {
left, right Set
want []string
}{
{empty, empty, nil},
{empty, nat, nil},
{nat, empty, testValues[:]},
{nat, nat, nil},
{nat, odd, testKeys(0, 2, 4, 6, 8)},
{odd, nat, nil},
{odd, prime, testKeys(1, 9)},
{prime, nat, nil},
}
for _, test := range tests {
got := test.left.Diff(test.right).Elements()
if !reflect.DeepEqual(got, test.want) {
t.Errorf("%v \\ %v: got %+q, want %+q", test.left, test.right, got, test.want)
}
}
}
func TestSymDiff(t *testing.T) {
a := testSet(0, 1, 2, 3, 4)
b := testSet(0, 4, 5, 6, 7)
c := testSet(3, 4, 8, 9)
empty := New()
tests := []struct {
left, right Set
want []string
}{
{empty, empty, nil},
{empty, a, a.Elements()},
{b, empty, b.Elements()},
{a, a, nil},
{a, b, testKeys(1, 2, 3, 5, 6, 7)},
{b, a, testKeys(1, 2, 3, 5, 6, 7)},
{a, c, testKeys(0, 1, 2, 8, 9)},
{c, a, testKeys(0, 1, 2, 8, 9)},
{c, b, testKeys(0, 3, 5, 6, 7, 8, 9)},
}
for _, test := range tests {
got := test.left.SymDiff(test.right).Elements()
if !reflect.DeepEqual(got, test.want) {
t.Errorf("%v ∆ %v: got %+v, want %+v", test.left, test.right, got, test.want)
}
}
}
func TestUpdate(t *testing.T) {
tests := []struct {
before, update Set
want []string
changed bool
}{
{nil, nil, nil, false},
{nil, testSet(0), testKeys(0), true},
{testSet(1), nil, testKeys(1), false},
{testSet(2, 3), testSet(4, 4, 3), testKeys(2, 3, 4), true},
}
for _, test := range tests {
ok := test.before.Update(test.update)
if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) {
t.Errorf("Update %v: got %+v, want %+q", test.before, got, test.want)
}
if ok != test.changed {
t.Errorf("Update %v reported change=%v, want %v", test.before, ok, test.changed)
}
}
}
func TestAdd(t *testing.T) {
tests := []struct {
before Set
update, want []string
changed bool
}{
{nil, nil, nil, false},
{nil, testKeys(0), testKeys(0), true},
{testSet(1), nil, testKeys(1), false},
{testSet(0, 1), testKeys(2, 2, 1), testKeys(0, 1, 2), true},
}
for _, test := range tests {
ok := test.before.Add(test.update...)
if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) {
t.Errorf("Add %v: got %+v, want %+v", test.before, got, test.want)
}
if ok != test.changed {
t.Errorf("Add %v reported change=%v, want %v", test.before, ok, test.changed)
}
}
}
func TestRemove(t *testing.T) {
tests := []struct {
before, update Set
want []string
changed bool
}{
{nil, nil, nil, false},
{nil, testSet(0), nil, false},
{testSet(5), nil, testKeys(5), false},
{testSet(3, 9), testSet(5, 1, 9), testKeys(3), true},
{testSet(0, 1, 2), testSet(4, 6), testKeys(0, 1, 2), false},
}
for _, test := range tests {
ok := test.before.Remove(test.update)
if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) {
t.Errorf("Remove %v: got %+v, want %+v", test.before, got, test.want)
}
if ok != test.changed {
t.Errorf("Remove %v reported change=%v, want %v", test.before, ok, test.changed)
}
}
}
func TestDiscard(t *testing.T) {
tests := []struct {
before Set
update, want []string
changed bool
}{
{nil, nil, nil, false},
{nil, testKeys(0), nil, false},
{testSet(1), nil, testKeys(1), false},
{testSet(0, 1), testKeys(2, 2, 1), testKeys(0), true},
{testSet(0, 1, 2), testKeys(3, 4), testKeys(0, 1, 2), false},
}
for _, test := range tests {
ok := test.before.Discard(test.update...)
if got := test.before.Elements(); !reflect.DeepEqual(got, test.want) {
t.Errorf("Discard %v: got %+v, want %+v", test.before, got, test.want)
}
if ok != test.changed {
t.Errorf("Discard %v reported change=%v, want %v", test.before, ok, test.changed)
}
}
}
func TestMap(t *testing.T) {
in := New(testValues[:]...)
got := make([]string, len(testValues))
out := in.Map(func(s string) string {
if p := keyPos(s); p < 0 {
t.Errorf("Unknown input key %v", s)
} else {
got[p] = s
}
return s
})
if !reflect.DeepEqual(got, testValues[:]) {
t.Errorf("Incomplete mapping:\n got %+v\nwant %+v", got, testValues)
}
if !out.Equals(in) {
t.Errorf("Incorrect mapping:\n got %v\nwant %v", out, in)
}
}
func TestEach(t *testing.T) {
in := New(testValues[:]...)
saw := make(map[string]int)
in.Each(func(name string) {
saw[name]++
})
for want := range in {
if saw[want] != 1 {
t.Errorf("Saw [%v] %d times, wanted 1", want, saw[want])
}
}
for got, n := range saw {
if _, ok := in[got]; !ok {
t.Errorf("Saw [%v] %d times, wanted 0", got, n)
}
}
}
func TestSelection(t *testing.T) {
in := New(testValues[:]...)
want := testSet(0, 2, 4, 6, 8)
if got := in.Select(func(s string) bool {
pos := keyPos(s)
return pos >= 0 && pos%2 == 0
}); !got.Equals(want) {
t.Errorf("%v.Select(evens): got %v, want %v", in, got, want)
}
if got := New().Select(func(string) bool { return true }); !got.Empty() {
t.Errorf("%v.Select(true): got %v, want empty", New(), got)
}
if got := in.Select(func(string) bool { return false }); !got.Empty() {
t.Errorf("%v.Select(false): got %v, want empty", in, got)
}
}
func TestPartition(t *testing.T) {
in := New(testValues[:]...)
tests := []struct {
in, left, right Set
f func(string) bool
desc string
}{
{testSet(0, 1), testSet(0, 1), nil,
func(string) bool { return true },
"all true",
},
{testSet(0, 1), nil, testSet(0, 1),
func(string) bool { return false },
"all false",
},
{in,
testSet(0, 1, 2, 3, 4),
testSet(5, 6, 7, 8, 9),
func(s string) bool { return keyPos(s) < 5 },
"pos(s) < 5",
},
{in,
testSet(1, 3, 5, 7, 9), // odd
testSet(0, 2, 4, 6, 8), // even
func(s string) bool { return keyPos(s)%2 == 1 },
"odd/even",
},
}
for _, test := range tests {
gotLeft, gotRight := test.in.Partition(test.f)
if !gotLeft.Equals(test.left) {
t.Errorf("Partition %s left: got %v, want %v", test.desc, gotLeft, test.left)
}
if !gotRight.Equals(test.right) {
t.Errorf("Partition %s right: got %v, want %v", test.desc, gotRight, test.right)
}
t.Logf("Partition %v %s\n\t left: %v\n\tright: %v", test.in, test.desc, gotLeft, gotRight)
}
}
func TestIndex(t *testing.T) {
tests := []struct {
needle string
keys []string
want int
}{
{testValues[0], nil, -1},
{testValues[1], []string{}, -1},
{testValues[2], testKeys(0, 1), -1},
{testValues[0], testKeys(0, 1), 0},
{testValues[1], testKeys(0, 1), 1},
{testValues[2], testKeys(0, 2, 1, 2), 1},
{testValues[9], testKeys(0, 2, 1, 9, 6), 3},
{testValues[4], testKeys(0, 2, 4, 9, 4), 2},
}
for _, test := range tests {
got := Index(test.needle, test.keys)
if got != test.want {
t.Errorf("Index(%+v, %+v): got %d, want %d", test.needle, test.keys, got, test.want)
}
}
}
type keyer []string
func (k keyer) Keys() []string {
p := make([]string, len(k))
copy(p, k)
return p
}
type uniq int
func TestFromValues(t *testing.T) {
tests := []struct {
input interface{}
want []string
}{
{nil, nil},
{map[float64]string{}, nil},
{map[int]string{1: testValues[1], 2: testValues[2], 3: testValues[2]}, testKeys(1, 2)},
{map[string]string{"foo": testValues[4], "baz": testValues[4]}, testKeys(4)},
{map[int]uniq{1: uniq(2), 3: uniq(4), 5: uniq(6)}, nil},
{map[*int]string{nil: testValues[0]}, testKeys(0)},
}
for _, test := range tests {
got := FromValues(test.input)
want := New(test.want...)
if !got.Equals(want) {
t.Errorf("MapValues %v: got %v, want %v", test.input, got, want)
}
}
}
func TestFromKeys(t *testing.T) {
tests := []struct {
input interface{}
want Set
}{
{3.5, nil}, // unkeyable type
{map[uniq]uniq{1: 1}, nil}, // unkeyable type
{nil, nil}, // empty
{[]string{}, nil}, // empty
{map[string]float64{}, nil}, // empty
{testValues[0], testSet(0)},
{testKeys(0, 1, 0, 0), testSet(0, 1)},
{map[string]int{testValues[0]: 1, testValues[1]: 2}, testSet(0, 1)},
{keyer(testValues[:3]), testSet(0, 1, 2)},
{testSet(4, 7, 8), testSet(4, 7, 8)},
{map[string]struct{}{testValues[2]: {}, testValues[7]: {}}, testSet(2, 7)},
}
for _, test := range tests {
got := FromKeys(test.input)
if !got.Equals(test.want) {
t.Errorf("FromKeys %v: got %v, want %v", test.input, got, test.want)
}
}
}
func TestContainsFunc(t *testing.T) {
tests := []struct {
input interface{}
needle string
want bool
}{
{[]string(nil), testValues[0], false},
{[]string{}, testValues[0], false},
{testKeys(0), testValues[0], true},
{testKeys(1), testValues[0], false},
{testKeys(0, 1, 9, 2), testValues[0], true},
{map[string]int(nil), testValues[2], false},
{map[string]int{}, testValues[2], false},
{map[string]int{testValues[2]: 1}, testValues[2], true},
{map[string]int{testValues[3]: 3}, testValues[2], false},
{map[string]float32{testValues[2]: 1, testValues[4]: 2}, testValues[2], true},
{map[string]float32{testValues[5]: 0, testValues[6]: 1, testValues[7]: 2, testValues[8]: 3}, testValues[2], false},
{Set(nil), testValues[3], false},
{New(), testValues[3], false},
{New(testValues[3]), testValues[3], true},
{New(testValues[5]), testValues[3], false},
{testSet(0, 1), testValues[3], false},
{testSet(0, 3, 1), testValues[3], true},
{keyer(nil), testValues[9], false},
{keyer{}, testValues[9], false},
{keyer{testValues[9]}, testValues[9], true},
{keyer{testValues[0]}, testValues[9], false},
{keyer(testKeys(0, 6, 9)), testValues[9], true},
{keyer(testKeys(0, 6, 7)), testValues[9], false},
}
for _, test := range tests {
got := Contains(test.input, test.needle)
if got != test.want {
t.Errorf("Contains(%+v, %v): got %v, want %v", test.input, test.needle, got, test.want)
}
}
}
func TestFromIndexed(t *testing.T) {
tests := []struct {
input []int
want Set
}{
{nil, nil},
{[]int{}, nil},
{[]int{0}, testSet(0)},
{[]int{1, 8, 2, 9}, testSet(1, 2, 8, 9)},
{[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, New(testValues[:]...)},
}
for _, test := range tests {
got := FromIndexed(len(test.input), func(i int) string {
return testValues[test.input[i]]
})
if !got.Equals(test.want) {
t.Errorf("FromIndexed(%d, <...>): got %v, want %v", len(test.input), got, test.want)
}
}
}