blob: fe671e76530fac1f2a441dec003977eb92a52490 [file] [log] [blame]
Sketch🕴️cd59d222026-02-28 19:28:26 +04001package repl
2
3import (
4 "bytes"
5 "strings"
6 "testing"
7)
8
9func TestRun_SingleExpression(t *testing.T) {
10 in := strings.NewReader("1 + 2\n")
11 var out bytes.Buffer
12
13 Run(in, &out)
14
15 got := out.String()
16 if !strings.Contains(got, "3") {
17 t.Errorf("expected output to contain '3', got %q", got)
18 }
19}
20
21func TestRun_MultipleExpressions(t *testing.T) {
22 in := strings.NewReader("1 + 2\n3 * 4\n")
23 var out bytes.Buffer
24
25 Run(in, &out)
26
27 got := out.String()
28 if !strings.Contains(got, "3") {
29 t.Errorf("expected output to contain '3', got %q", got)
30 }
31 if !strings.Contains(got, "12") {
32 t.Errorf("expected output to contain '12', got %q", got)
33 }
34}
35
36func TestRun_ErrorContinues(t *testing.T) {
37 // First line has error, second is valid.
38 in := strings.NewReader("1 +\n2 + 3\n")
39 var out bytes.Buffer
40
41 Run(in, &out)
42
43 got := out.String()
44 if !strings.Contains(got, "error:") {
45 t.Errorf("expected output to contain 'error:', got %q", got)
46 }
47 if !strings.Contains(got, "5") {
48 t.Errorf("expected output to contain '5' after error recovery, got %q", got)
49 }
50}
51
52func TestRun_DivisionByZero(t *testing.T) {
53 in := strings.NewReader("1 / 0\n")
54 var out bytes.Buffer
55
56 Run(in, &out)
57
58 got := out.String()
59 if !strings.Contains(got, "error:") {
60 t.Errorf("expected output to contain 'error:', got %q", got)
61 }
62 if !strings.Contains(got, "division by zero") {
63 t.Errorf("expected 'division by zero' in output, got %q", got)
64 }
65}
66
67func TestRun_EmptyLine(t *testing.T) {
68 // Empty lines should be skipped, not cause errors.
69 in := strings.NewReader("\n1 + 1\n")
70 var out bytes.Buffer
71
72 Run(in, &out)
73
74 got := out.String()
75 if !strings.Contains(got, "2") {
76 t.Errorf("expected output to contain '2', got %q", got)
77 }
78 // Should not contain any error.
79 if strings.Contains(got, "error:") {
80 t.Errorf("empty line should not produce error, got %q", got)
81 }
82}
83
84func TestRun_Prompt(t *testing.T) {
85 in := strings.NewReader("42\n")
86 var out bytes.Buffer
87
88 Run(in, &out)
89
90 got := out.String()
91 if !strings.Contains(got, ">> ") {
92 t.Errorf("expected prompt '>> ' in output, got %q", got)
93 }
94}
95
96func TestRun_FloatResult(t *testing.T) {
97 in := strings.NewReader("7 / 2\n")
98 var out bytes.Buffer
99
100 Run(in, &out)
101
102 got := out.String()
103 if !strings.Contains(got, "3.5") {
104 t.Errorf("expected output to contain '3.5', got %q", got)
105 }
106}
107
108func TestRun_InvalidCharacter(t *testing.T) {
109 in := strings.NewReader("1 @ 2\n")
110 var out bytes.Buffer
111
112 Run(in, &out)
113
114 got := out.String()
115 if !strings.Contains(got, "error:") {
116 t.Errorf("expected output to contain 'error:', got %q", got)
117 }
118}
119
120func TestRun_EmptyInput(t *testing.T) {
121 // No input at all — just EOF.
122 in := strings.NewReader("")
123 var out bytes.Buffer
124
125 Run(in, &out)
126
127 got := out.String()
128 // Should just show the prompt and exit gracefully.
129 if !strings.Contains(got, ">> ") {
130 t.Errorf("expected at least one prompt, got %q", got)
131 }
132}
133
134func TestRun_WholeIntegerNoTrailingZeros(t *testing.T) {
135 // 2 + 3 = 5, should print "5" not "5.000000".
136 in := strings.NewReader("2 + 3\n")
137 var out bytes.Buffer
138
139 Run(in, &out)
140
141 got := out.String()
142 // Result line is "5\n" (between prompts).
143 if !strings.Contains(got, "5\n") {
144 t.Errorf("expected '5\\n' in output, got %q", got)
145 }
146 // Should not contain "5.0"
147 if strings.Contains(got, "5.0") {
148 t.Errorf("expected no trailing zeros, got %q", got)
149 }
150}
Sketch🕴️17f24392026-02-28 20:59:37 +0400151
152// --- Function definition tests ---
153
154func TestRun_DefineFunction(t *testing.T) {
155 in := strings.NewReader("f(x) = x + 1\n")
156 var out bytes.Buffer
157
158 Run(in, &out)
159
160 got := out.String()
161 if !strings.Contains(got, "defined f") {
162 t.Errorf("expected 'defined f' in output, got %q", got)
163 }
164}
165
166func TestRun_DefineAndCallFunction(t *testing.T) {
167 in := strings.NewReader("f(x) = x + 1\nf(5)\n")
168 var out bytes.Buffer
169
170 Run(in, &out)
171
172 got := out.String()
173 if !strings.Contains(got, "defined f") {
174 t.Errorf("expected 'defined f' in output, got %q", got)
175 }
176 if !strings.Contains(got, "6") {
177 t.Errorf("expected output to contain '6', got %q", got)
178 }
179}
180
181func TestRun_DefineMultiParamAndCall(t *testing.T) {
182 in := strings.NewReader("add(x, y) = x + y\nadd(3, 4)\n")
183 var out bytes.Buffer
184
185 Run(in, &out)
186
187 got := out.String()
188 if !strings.Contains(got, "defined add") {
189 t.Errorf("expected 'defined add' in output, got %q", got)
190 }
191 if !strings.Contains(got, "7") {
192 t.Errorf("expected output to contain '7', got %q", got)
193 }
194}
195
196func TestRun_RedefineError(t *testing.T) {
197 in := strings.NewReader("f(x) = x\nf(x) = x + 1\n")
198 var out bytes.Buffer
199
200 Run(in, &out)
201
202 got := out.String()
203 if !strings.Contains(got, "defined f") {
204 t.Errorf("expected 'defined f' in output, got %q", got)
205 }
206 if !strings.Contains(got, "error:") {
207 t.Errorf("expected 'error:' for redefinition, got %q", got)
208 }
209 if !strings.Contains(got, "already defined") {
210 t.Errorf("expected 'already defined' in error, got %q", got)
211 }
212}
213
214func TestRun_UndefinedFunctionError(t *testing.T) {
215 in := strings.NewReader("f(1)\n")
216 var out bytes.Buffer
217
218 Run(in, &out)
219
220 got := out.String()
221 if !strings.Contains(got, "error:") {
222 t.Errorf("expected 'error:' for undefined function, got %q", got)
223 }
224 if !strings.Contains(got, "undefined function") {
225 t.Errorf("expected 'undefined function' in error, got %q", got)
226 }
227}
228
229func TestRun_FuncCallInExpression(t *testing.T) {
230 in := strings.NewReader("f(x) = x * 2\nf(3) + 1\n")
231 var out bytes.Buffer
232
233 Run(in, &out)
234
235 got := out.String()
236 if !strings.Contains(got, "7") {
237 t.Errorf("expected output to contain '7', got %q", got)
238 }
239}
240
241func TestRun_CrossFunctionCalls(t *testing.T) {
242 in := strings.NewReader("double(x) = x * 2\nquad(x) = double(double(x))\nquad(3)\n")
243 var out bytes.Buffer
244
245 Run(in, &out)
246
247 got := out.String()
248 if !strings.Contains(got, "12") {
249 t.Errorf("expected output to contain '12', got %q", got)
250 }
251}