cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

main
012 / internal / sheet / vast_test.go
vast_test.go 3.3K
1package sheet23import (4 "fmt"5 "math/rand/v2"6 "slices"7 "testing"89 "src.rest/cat/012/internal/formula"10)1112// A filter over whole columns tests the rows with data and treats the13// blank rows past them as one: hiding them, counting them and stepping14// over them cost nothing per row.15func TestFilterWholeColumns(t *testing.T) {16 s := New()17 for i, in := range []string{"h", "1", "", "3", "x"} {18 if in != "" {19 s.Set(Addr{Row: i}, in)20 }21 }22 s.CreateFilter(Rect{To: Addr{Col: 1, Row: MaxRows - 1}})23 s.FilterColumn(0, Criteria{Cond: Condition{CondNotEmpty, ""}})24 if n := s.HiddenRows(); n != MaxRows-4 {25 t.Errorf("HiddenRows = %d, want %d", n, MaxRows-4)26 }27 if r, ok := s.NextShownRow(3, 1); !ok || r != 4 {28 t.Errorf("NextShownRow(3, 1) = %d, %v", r, ok)29 }30 if r, ok := s.NextShownRow(4, 1); ok {31 t.Errorf("NextShownRow(4, 1) = %d, want none", r)32 }33 if r, ok := s.NextShownRow(MaxRows, -1); !ok || r != 4 {34 t.Errorf("NextShownRow from the bottom = %d, %v", r, ok)35 }36 if got := s.Edge(at("A5"), 0, 1); got != at("A5") {37 t.Errorf("Edge down from the last shown row = %v", got)38 }39 if got := s.Edge(at("B1"), 0, 1); got != at("B5") {40 t.Errorf("Edge down column B = %v, want B5", got)41 }42 vals := s.FilterValues(1)43 if len(vals) != 1 || vals[0].Text != "" || vals[0].Count != 3 {44 t.Errorf("FilterValues(B) = %+v", vals)45 }46 s.FilterColumn(0, Criteria{})47 if vals := s.FilterValues(0); vals[len(vals)-1].Text != "" || vals[len(vals)-1].Count != MaxRows-4 {48 t.Errorf("blanks in FilterValues(A) = %+v", vals[len(vals)-1])49 }50}5152// Sorting moves only the rows with cells, and lands every row where a53// stable sort of all the range's rows (blank rows included) would.54func TestSortMatchesDenseSort(t *testing.T) {55 for seed := range 20 {56 rng := rand.New(rand.NewPCG(uint64(seed), 3))57 s := New()58 fill := []string{"", "", "", "3", "1", "b", "A", "TRUE", "=1/0", "=A1+1", `=""`}59 for row := range 30 {60 if rng.IntN(4) == 0 {61 continue // a blank row62 }63 for col := range 3 {64 if in := fill[rng.IntN(len(fill))]; in != "" {65 s.Set(Addr{Col: col, Row: row}, in)66 }67 }68 }69 r := Rect{From: Addr{Row: 2}, To: Addr{Col: 2, Row: MaxRows - 1}}70 keys := []SortKey{{Col: rng.IntN(3), Desc: seed%2 == 1}, {Col: rng.IntN(3)}}71 want := denseSort(s, r, keys)72 s.SortRange(r, keys)73 if got := inputs(s); fmt.Sprint(got) != fmt.Sprint(want) {74 t.Errorf("seed %d, keys %v:\n got %v\nwant %v", seed, keys, got, want)75 }76 }77}7879// denseSort is what sorting did before it went by rows with data: a80// stable sort of every row of r (clipped to the used range), returning81// the inputs it leaves.82func denseSort(s *Sheet, r Rect, keys []SortKey) map[string]string {83 used, _ := s.UsedRange()84 r.To.Row, r.To.Col = min(r.To.Row, used.To.Row), min(r.To.Col, used.To.Col)85 order := make([]int, r.To.Row-r.From.Row+1)86 for i := range order {87 order[i] = r.From.Row + i88 }89 slices.SortStableFunc(order, func(i, j int) int { return s.compareRows(keys, i, j) })90 out := map[string]string{}91 for a, c := range s.cells.all() {92 if !r.Contains(a) {93 out[a.String()] = c.Input94 }95 }96 for k, src := range order {97 for col := r.From.Col; col <= r.To.Col; col++ {98 if c := s.cells.get(Addr{Col: col, Row: src}); c != nil {99 to := Addr{Col: col, Row: r.From.Row + k}100 out[to.String()] = c.rewritten(formula.Shift(0, to.Row-src)).Input101 }102 }103 }104 return out105}