Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

tally_test.gno

4.22 Kb · 151 lines
  1package tally
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/nt/uassert/v0"
  7)
  8
  9func keys(entries []Entry) string {
 10	out := ""
 11	for i, e := range entries {
 12		if i > 0 {
 13			out += ","
 14		}
 15		out += e.Key
 16	}
 17	return out
 18}
 19
 20func TestSortOrdersByScoreThenKey(t *testing.T) {
 21	cases := []struct {
 22		name string
 23		in   []Entry
 24		want string
 25	}{
 26		{"already ordered", []Entry{{"a", 3}, {"b", 2}, {"c", 1}}, "a,b,c"},
 27		{"reversed", []Entry{{"c", 1}, {"b", 2}, {"a", 3}}, "a,b,c"},
 28		{"all tied, key decides", []Entry{{"c", 5}, {"a", 5}, {"b", 5}}, "a,b,c"},
 29		{"partial tie", []Entry{{"z", 1}, {"b", 9}, {"a", 9}}, "a,b,z"},
 30		{"negatives sort below", []Entry{{"a", -1}, {"b", 0}, {"c", 1}}, "c,b,a"},
 31		{"single", []Entry{{"only", 7}}, "only"},
 32		{"empty", nil, ""},
 33	}
 34	for _, tc := range cases {
 35		t.Run(tc.name, func(t *testing.T) {
 36			in := make([]Entry, len(tc.in))
 37			copy(in, tc.in)
 38			Sort(in)
 39			uassert.Equal(t, tc.want, keys(in))
 40		})
 41	}
 42}
 43
 44// TestSortIsATotalOrder is the defect this package exists to remove. A
 45// comparator whose last clause repeats the score (rpgroom's third clause) is
 46// not a total order: two equal rows compare false both ways and their order is
 47// decided by the algorithm. Sorting twice from different inputs must agree.
 48func TestSortIsATotalOrder(t *testing.T) {
 49	a := []Entry{{"x", 5}, {"y", 5}, {"z", 5}, {"w", 9}}
 50	b := []Entry{{"z", 5}, {"w", 9}, {"y", 5}, {"x", 5}}
 51	Sort(a)
 52	Sort(b)
 53	uassert.Equal(t, keys(a), keys(b))
 54	uassert.Equal(t, "w,x,y,z", keys(a))
 55}
 56
 57func TestTop(t *testing.T) {
 58	in := []Entry{{"c", 1}, {"a", 3}, {"b", 2}}
 59	uassert.Equal(t, "a,b", keys(Top(in, 2)))
 60	uassert.Equal(t, "a,b,c", keys(Top(in, 10)))
 61	uassert.Equal(t, "", keys(Top(in, 0)))
 62	uassert.Equal(t, "", keys(Top(in, -1)))
 63	uassert.Equal(t, "", keys(Top(nil, 3)))
 64}
 65
 66// TestTopDoesNotDisturbTheCaller pins that Top copies. A realm that renders a
 67// Top(3) and then iterates its own slice must not find it reordered.
 68func TestTopDoesNotDisturbTheCaller(t *testing.T) {
 69	in := []Entry{{"c", 1}, {"a", 3}, {"b", 2}}
 70	_ = Top(in, 2)
 71	uassert.Equal(t, "c,a,b", keys(in))
 72}
 73
 74func TestRank(t *testing.T) {
 75	in := []Entry{{"c", 1}, {"a", 3}, {"b", 2}}
 76	uassert.Equal(t, 1, Rank(in, "a"))
 77	uassert.Equal(t, 2, Rank(in, "b"))
 78	uassert.Equal(t, 3, Rank(in, "c"))
 79	uassert.Equal(t, 0, Rank(in, "absent"))
 80}
 81
 82// TestRankNeverTies is what makes a rendered "#3" mean something: two equal
 83// scores still get 2 and 3, never 2 and 2.
 84func TestRankNeverTies(t *testing.T) {
 85	in := []Entry{{"a", 5}, {"b", 5}, {"c", 5}}
 86	seen := map[int]bool{}
 87	for _, e := range in {
 88		r := Rank(in, e.Key)
 89		if seen[r] {
 90			t.Errorf("rank %d claimed twice", r)
 91		}
 92		seen[r] = true
 93	}
 94	uassert.Equal(t, 3, len(seen))
 95}
 96
 97func TestBoardAddAndScore(t *testing.T) {
 98	b := NewBoard()
 99	uassert.Equal(t, 0, b.Len())
100	uassert.Equal(t, int64(5), b.Add("a", 5))
101	uassert.Equal(t, int64(8), b.Add("a", 3))
102	uassert.Equal(t, int64(6), b.Add("a", -2))
103	uassert.Equal(t, int64(6), b.Score("a"))
104	uassert.Equal(t, int64(0), b.Score("never"))
105	uassert.Equal(t, 1, b.Len())
106}
107
108func TestBoardHasDistinguishesAbsentFromZero(t *testing.T) {
109	b := NewBoard()
110	b.Set("zero", 0)
111	uassert.True(t, b.Has("zero"))
112	uassert.False(t, b.Has("absent"))
113	uassert.Equal(t, int64(0), b.Score("zero"))
114	uassert.Equal(t, int64(0), b.Score("absent"))
115}
116
117func TestBoardRemove(t *testing.T) {
118	b := NewBoard()
119	b.Set("a", 1)
120	uassert.True(t, b.Remove("a"))
121	uassert.False(t, b.Remove("a"))
122	uassert.Equal(t, 0, b.Len())
123}
124
125func TestBoardEntriesAndTop(t *testing.T) {
126	b := NewBoard()
127	b.Set("c", 1)
128	b.Set("a", 3)
129	b.Set("b", 3)
130	uassert.Equal(t, "a,b,c", keys(b.Entries()))
131	uassert.Equal(t, "a,b", keys(b.Top(2)))
132	uassert.Equal(t, 1, b.Rank("a"))
133	uassert.Equal(t, 2, b.Rank("b"))
134	uassert.Equal(t, 0, b.Rank("absent"))
135}
136
137// TestBoardOrderDoesNotDependOnInsertion is the reason Board is backed by a
138// tree and not a map: the same scores inserted in a different order must
139// render identically.
140func TestBoardOrderDoesNotDependOnInsertion(t *testing.T) {
141	one := NewBoard()
142	for _, e := range []Entry{{"a", 5}, {"b", 5}, {"c", 9}} {
143		one.Set(e.Key, e.Score)
144	}
145	two := NewBoard()
146	for _, e := range []Entry{{"c", 9}, {"b", 5}, {"a", 5}} {
147		two.Set(e.Key, e.Score)
148	}
149	uassert.Equal(t, keys(one.Entries()), keys(two.Entries()))
150	uassert.Equal(t, "c,a,b", keys(one.Entries()))
151}