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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}