oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1package calc
2
3import "testing"
4
5func TestParseArithmeticPrecedence(t *testing.T) {
6 n, err := Parse("1+2*3")
7 if err != nil {
8 t.Fatal(err)
9 }
10 b, ok := n.(*Binary)
11 if !ok || b.Op != "+" {
12 t.Fatalf("root = %T %v, want Binary +", n, n)
13 }
14 r, ok := b.Right.(*Binary)
15 if !ok || r.Op != "*" {
16 t.Fatal("multiplication did not bind tighter than addition")
17 }
18}
19
20func TestParseAbsoluteAndRelativeRefs(t *testing.T) {
21 n, err := Parse("$A$1")
22 if err != nil {
23 t.Fatal(err)
24 }
25 r := n.(*Ref)
26 if !r.AbsCol || !r.AbsRow {
27 t.Fatal("$A$1 did not parse as fully absolute")
28 }
29 n2, _ := Parse("A1")
30 r2 := n2.(*Ref)
31 if r2.AbsCol || r2.AbsRow {
32 t.Fatal("A1 parsed as absolute")
33 }
34 if r2.ColPos != 0 || r2.RowPos != 0 {
35 t.Fatalf("A1 = col %d row %d, want 0,0", r2.ColPos, r2.RowPos)
36 }
37}
38
39func TestParseRangeAndCall(t *testing.T) {
40 n, err := Parse("SUM(A1:B10)")
41 if err != nil {
42 t.Fatal(err)
43 }
44 c, ok := n.(*Call)
45 if !ok || c.Name != "SUM" {
46 t.Fatalf("root = %T, want Call SUM", n)
47 }
48 if _, ok := c.Args[0].(*Range); !ok {
49 t.Fatalf("arg 0 = %T, want Range", c.Args[0])
50 }
51}
52
53func TestParseRejectsGarbage(t *testing.T) {
54 for _, src := range []string{"1+", "(1", "SUM(", "A1:", "*"} {
55 if _, err := Parse(src); err == nil {
56 t.Errorf("Parse(%q) succeeded, want error", src)
57 }
58 }
59}
60
61func TestParsePowerIsRightAssociative(t *testing.T) {
62 // 2^3^2 must parse as 2^(3^2) = 2^9 = 512, not (2^3)^2 = 64.
63 n, err := Parse("2^3^2")
64 if err != nil {
65 t.Fatal(err)
66 }
67 b, ok := n.(*Binary)
68 if !ok || b.Op != "^" {
69 t.Fatalf("root = %T %v, want Binary ^", n, n)
70 }
71 if _, ok := b.Left.(*Num); !ok {
72 t.Fatalf("left = %T, want Num (right-associative grouping)", b.Left)
73 }
74 r, ok := b.Right.(*Binary)
75 if !ok || r.Op != "^" {
76 t.Fatal("right operand should itself be a ^ binary (right-associative)")
77 }
78}
79
80func TestParseFunctionNameCaseInsensitive(t *testing.T) {
81 n, err := Parse("sum(A1)")
82 if err != nil {
83 t.Fatal(err)
84 }
85 c, ok := n.(*Call)
86 if !ok || c.Name != "SUM" {
87 t.Fatalf("root = %T %v, want Call SUM (normalised to upper case)", n, n)
88 }
89}
90
91func TestParseUnaryBindsTighterThanPower(t *testing.T) {
92 // -2^2 must parse as (-2)^2 = 4, not -(2^2) = -4: unary minus binds
93 // tighter than ^, matching Excel rather than most programming
94 // languages.
95 n, err := Parse("-2^2")
96 if err != nil {
97 t.Fatal(err)
98 }
99 b, ok := n.(*Binary)
100 if !ok || b.Op != "^" {
101 t.Fatalf("root = %T %v, want Binary ^", n, n)
102 }
103 u, ok := b.Left.(*Unary)
104 if !ok || u.Op != "-" {
105 t.Fatalf("left = %T, want Unary - (unary binds tighter than ^)", b.Left)
106 }
107 if _, ok := u.X.(*Num); !ok {
108 t.Fatalf("unary operand = %T, want Num", u.X)
109 }
110 if _, ok := b.Right.(*Num); !ok {
111 t.Fatalf("right = %T, want Num", b.Right)
112 }
113}
114
115func TestParseFunctionNameShapedLikeReference(t *testing.T) {
116 // LOG10 matches the reference shape (<=3 letters followed by digits)
117 // but is followed immediately by '(', so it must lex as a function
118 // call, not a reference.
119 n, err := Parse("LOG10(100)")
120 if err != nil {
121 t.Fatal(err)
122 }
123 c, ok := n.(*Call)
124 if !ok || c.Name != "LOG10" {
125 t.Fatalf("root = %T %v, want Call LOG10", n, n)
126 }
127 if len(c.Args) != 1 {
128 t.Fatalf("args = %d, want 1", len(c.Args))
129 }
130 if _, ok := c.Args[0].(*Num); !ok {
131 t.Fatalf("arg 0 = %T, want Num", c.Args[0])
132 }
133}
134
135func TestParseRefStillLexesWhenNotFollowedByCall(t *testing.T) {
136 // A1 is not followed by '(', so it must still lex as a reference even
137 // though the call-parenthesis lookahead exists for words like LOG10.
138 n, err := Parse("A1")
139 if err != nil {
140 t.Fatal(err)
141 }
142 if _, ok := n.(*Ref); !ok {
143 t.Fatalf("root = %T, want Ref", n)
144 }
145}
146
147func TestParseRejectsZeroRow(t *testing.T) {
148 // A1 notation is one-based; "A0" is malformed input, not row -1.
149 if _, err := Parse("A0"); err == nil {
150 t.Error(`Parse("A0") succeeded, want error`)
151 }
152}
153