brainfuck_test.gno
6.04 Kb ยท 282 lines
1package brainfuck
2
3import (
4 "bytes"
5 "testing"
6)
7
8func TestBasicOperations(t *testing.T) {
9 tests := []struct {
10 name string
11 code string
12 input string
13 expected string
14 }{
15 {
16 name: "Increment and Output",
17 code: "+++.",
18 input: "",
19 expected: "\x03",
20 },
21 {
22 name: "Decrement and Output",
23 code: "+++--.",
24 input: "",
25 expected: "\x01",
26 },
27 {
28 name: "Pointer Movement",
29 code: ">+++>++>+<<<.>.>.>.",
30 input: "",
31 expected: "\x00\x03\x02\x01",
32 },
33 {
34 name: "Pointer at Zero Boundary",
35 code: "<<<+++.",
36 input: "",
37 expected: "\x03",
38 },
39 }
40
41 for _, tc := range tests {
42 t.Run(tc.name, func(t *testing.T) {
43 output := Run([]byte(tc.code), []byte(tc.input))
44 if !bytes.Equal(output, []byte(tc.expected)) {
45 t.Errorf("Expected %v, got %v", []byte(tc.expected), output)
46 }
47 })
48 }
49}
50
51func TestLoops(t *testing.T) {
52 tests := []struct {
53 name string
54 code string
55 input string
56 expected string
57 }{
58 {
59 name: "Simple Loop",
60 code: "+++[>+<-]>.",
61 input: "",
62 expected: "\x03",
63 },
64 {
65 name: "Nested Loops",
66 code: "++[>++[>++<-]<-]>>.",
67 input: "",
68 expected: "\x08",
69 },
70 {
71 name: "Skip Loop if Zero",
72 code: "[>+++<-]>.",
73 input: "",
74 expected: "\x00",
75 },
76 {
77 name: "Unmatched Opening Bracket",
78 code: "[.",
79 input: "",
80 expected: "",
81 },
82 {
83 name: "Unmatched Closing Bracket",
84 code: "].",
85 input: "",
86 expected: "\x00",
87 },
88 }
89
90 for _, tc := range tests {
91 t.Run(tc.name, func(t *testing.T) {
92 output := Run([]byte(tc.code), []byte(tc.input))
93 if !bytes.Equal(output, []byte(tc.expected)) {
94 t.Errorf("Expected %v, got %v", []byte(tc.expected), output)
95 }
96 })
97 }
98}
99
100func TestInput(t *testing.T) {
101 tests := []struct {
102 name string
103 code string
104 input string
105 expected string
106 }{
107 {
108 name: "Read Single Input",
109 code: ",.",
110 input: "A",
111 expected: "A",
112 },
113 {
114 name: "Read Multiple Inputs",
115 code: ",>,>,.",
116 input: "ABC",
117 expected: "C",
118 },
119 {
120 name: "Input Modification",
121 code: ",+.",
122 input: "A",
123 expected: "B",
124 },
125 {
126 name: "Input Exhaustion",
127 code: ",.,.,.,..",
128 input: "AB",
129 expected: "AB\x00\x00\x00",
130 },
131 {
132 name: "Output Input Stream",
133 code: ",[.,]",
134 input: "hello, world!\n",
135 expected: "hello, world!\n",
136 },
137 }
138
139 for _, tc := range tests {
140 t.Run(tc.name, func(t *testing.T) {
141 output := Run([]byte(tc.code), []byte(tc.input))
142 if !bytes.Equal(output, []byte(tc.expected)) {
143 t.Errorf("Expected %v, got %v", []byte(tc.expected), output)
144 }
145 })
146 }
147}
148
149func TestBsort(t *testing.T) {
150 // [bsort.b -- bubble sort
151 // (c) 2016 Daniel B. Cristofani
152 // http://brainfuck.org/]
153 // [This program sorts the bytes of its input by bubble sort.]
154 code := `
155 >>,[>>,]<<[
156 [<<]>>>>[
157 <<[>+<<+>-]
158 >>[>+<<<<[->]>[<]>>-]
159 <<<[[-]>>[>+<-]>>[<<<+>>>-]]
160 >>[[<+>-]>>]<
161 ]<<[>>+<<-]<<
162 ]>>>>[.>>]`
163 input := "ckduwcomaz"
164 expected := "accdkmouwz"
165
166 output := Run([]byte(code), []byte(input))
167 if string(output) != expected {
168 t.Errorf("Hello World test failed. Expected '%s', got '%s'", expected, string(output))
169 }
170}
171
172func TestHelloWorld(t *testing.T) {
173 // Classic "Hello World!" program from wikipedia
174 code := `
175+++++ +++++ initialize counter (cell #0) to 10
176[ use loop to set 70/100/30/10
177 > +++++ ++ add 7 to cell #1
178 > +++++ +++++ add 10 to cell #2
179 > +++ add 3 to cell #3
180 > + add 1 to cell #4
181<<<< - decrement counter (cell #0)
182]
183> ++ . print 'H'
184> + . print 'e'
185+++++ ++ . print 'l'
186. print 'l'
187+++ . print 'o'
188> ++ . print ' '
189<< +++++ +++++ +++++ . print 'W'
190> . print 'o'
191+++ . print 'r'
192----- - . print 'l'
193----- --- . print 'd'
194> + . print '!'
195> . print 'eol'`
196 expected := "Hello World!\n"
197
198 output := Run([]byte(code), []byte{})
199 if string(output) != expected {
200 t.Errorf("Hello World test failed. Expected '%s', got '%s'", expected, string(output))
201 }
202}
203
204func TestEnsureMemory(t *testing.T) {
205 tests := []struct {
206 name string
207 initialMem []byte
208 pointer int
209 expectedLen int
210 }{
211 {
212 name: "Already Sufficient",
213 initialMem: []byte{0, 0, 0},
214 pointer: 2,
215 expectedLen: 3,
216 },
217 {
218 name: "Extend By One",
219 initialMem: []byte{0, 0, 0},
220 pointer: 3,
221 expectedLen: 4,
222 },
223 {
224 name: "Extend By Many",
225 initialMem: []byte{},
226 pointer: 10,
227 expectedLen: 11,
228 },
229 }
230
231 for _, tc := range tests {
232 t.Run(tc.name, func(t *testing.T) {
233 memory := tc.initialMem
234 ensureMemory(&memory, tc.pointer)
235
236 if len(memory) != tc.expectedLen {
237 t.Errorf("Expected memory length %d, got %d", tc.expectedLen, len(memory))
238 }
239
240 // Check that we can access the pointer without panic
241 _ = memory[tc.pointer]
242 })
243 }
244}
245
246// Test various complex programs to ensure the interpreter works correctly
247func TestComplexPrograms(t *testing.T) {
248 tests := []struct {
249 name string
250 code string
251 input string
252 expected string
253 }{
254 {
255 name: "Multiply Two Numbers",
256 code: ",>,<[>[->+>+<<]>>[-<<+>>]<<<-]>>.",
257 input: "\x03\x04", // 3 * 4 = 12
258 expected: "\x0c", // 12 in ASCII
259 },
260 {
261 name: "Cat Program",
262 code: ",[.,]",
263 input: "Hello!",
264 expected: "Hello!",
265 },
266 {
267 name: "ASCII to Decimal",
268 code: ",>++++++[<-------->-]<.", // ASCII value minus 48
269 input: "5", // ASCII 53
270 expected: "\x05", // 5 in decimal
271 },
272 }
273
274 for _, tc := range tests {
275 t.Run(tc.name, func(t *testing.T) {
276 output := Run([]byte(tc.code), []byte(tc.input))
277 if !bytes.Equal(output, []byte(tc.expected)) {
278 t.Errorf("Expected %v, got %v", []byte(tc.expected), output)
279 }
280 })
281 }
282}