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 / steps_test.go
steps_test.go 3.1K
1package sheet23import (4 "strings"5 "testing"6)78// shown writes what Steps shows: values in [], the next part in _.9func shown(st *Steps) string {10 var out strings.Builder11 in := false12 for _, sg := range st.Text() {13 if sg.Next != in {14 out.WriteByte('_')15 in = sg.Next16 }17 t := sg.Text18 if sg.Value {19 t = "[" + t + "]"20 }21 out.WriteString(t)22 }23 if in {24 out.WriteByte('_')25 }26 return out.String()27}2829// walk steps through the formula at a, returning what it shows before30// each step and at the end.31func walk(t *testing.T, s *Sheet, a string) []string {32 t.Helper()33 st := s.EvaluateSteps(at(a))34 if st == nil {35 t.Fatalf("%s: no steps", a)36 }37 var out []string38 for !st.Done() {39 out = append(out, shown(st))40 st.Step()41 }42 return append(out, shown(st))43}4445func TestSteps(t *testing.T) {46 s := sheetOf(t, map[string]string{47 "A1": "5", "A2": "2", "A3": "x",48 "B1": "=A1+A2*3",49 "B2": "=IF(A1>3, A1*2, A2+1)",50 "B3": "=(A1+A2)*2",51 "B4": "=SUM(A1:A2*2)",52 "B5": "=LET(x, A1, x*2)",53 "B6": "=A3&D9",54 "B7": "=IFERROR(1/0, A2)",55 "B8": "=SEQUENCE(2)",56 })57 for _, tc := range []struct {58 cell string59 want string60 }{61 {"B1", "=_A1_+A2*3 | =[5]+_A2_*3 | =[5]+_[2]*3_ | =_[5]+[6]_ | [11]"},62 // The branch IF doesn't take is never computed.63 {"B2", "=IF(_A1_>3,A1*2,A2+1) | =IF(_[5]>3_,A1*2,A2+1) | =IF([TRUE],_A1_*2,A2+1) | =IF([TRUE],_[5]*2_,A2+1) | =_IF([TRUE],[10],A2+1)_ | [10]"},64 // The part computed next keeps its parentheses.65 {"B3", "=(_A1_+A2)*2 | =([5]+_A2_)*2 | =_([5]+[2])_*2 | =_[7]*2_ | [14]"},66 // In an array context a part computes an array.67 {"B4", "=SUM(_A1:A2*2_) | =_SUM([{10;4}])_ | [14]"},68 {"B5", "=LET(x,_A1_,x*2) | =LET(x,[5],_x*2_) | =_LET(x,[5],[10])_ | [10]"},69 {"B6", `=_A3_&D9 | =["x"]&_D9_ | =_["x"]&[0]_ | ["x"]`},70 {"B7", "=IFERROR(_1/0_,A2) | =IFERROR([#DIV/0!],_A2_) | =_IFERROR([#DIV/0!],[2])_ | [2]"},71 {"B8", "=_SEQUENCE(2)_ | [{1;2}]"},72 } {73 if got := strings.Join(walk(t, s, tc.cell), " | "); got != tc.want {74 t.Errorf("%s:\n got %s\nwant %s", tc.cell, got, tc.want)75 }76 }77 if s.EvaluateSteps(at("A1")) != nil {78 t.Error("steps for a number")79 }80}8182func TestStepsDecimal(t *testing.T) {83 s := sheetOf(t, map[string]string{"A1": "0.1", "B1": "=A1+0.2"})84 s.SetDecimal(true)85 if got := strings.Join(walk(t, s, "B1"), " | "); got != "=_A1_+0.2 | =_[0.1]+0.2_ | [0.3]" {86 t.Errorf("decimal: %s", got)87 }88}8990func TestStepsInto(t *testing.T) {91 s := sheetOf(t, map[string]string{"A1": "4", "B1": "=A1*2", "C1": "=B1+Rate"})92 s.DefineName("Rate", rect("A1"))93 st := s.EvaluateSteps(at("C1"))94 if expr, v, _ := st.Next(); expr != "B1" || v != "8" {95 t.Errorf("next %q = %q", expr, v)96 }97 in := st.Into()98 if in == nil {99 t.Fatal("no stepping into B1")100 }101 if _, a := in.Cell(); a != at("B1") || shown(in) != "=_A1_*2" {102 t.Errorf("into %v: %s", a, shown(in))103 }104 if in.Into() != nil {105 t.Error("stepped into a number")106 }107 st.Step()108 if expr, v, _ := st.Next(); expr != "Rate" || v != "4" {109 t.Errorf("a name: %q = %q", expr, v)110 }111 if done, total := st.Progress(); done != 1 || total != 3 {112 t.Errorf("progress %d of %d", done, total)113 }114 st.Restart()115 if shown(st) != "=_B1_+Rate" {116 t.Errorf("restart: %s", shown(st))117 }118}