Skip to content

Commit 26da8ad

Browse files
committed
Add tests for script compiling
1 parent a2fa133 commit 26da8ad

File tree

10 files changed

+383
-0
lines changed

10 files changed

+383
-0
lines changed

test/compiler/compiler_test.cpp

Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
11
#include "scratchproject.h"
22
#include "scratch/stage.h"
33
#include "scratch/sprite.h"
4+
#include "engine/compiler.h"
5+
#include "internal/scratch3reader.h"
46
#include "../common.h"
57

8+
#define INIT_COMPILER(engineName, compilerName) \
9+
Engine engineName; \
10+
Compiler compilerName(&engine);
11+
12+
#define LOAD_PROJECT(fileName, engineName) \
13+
Engine engineName; \
14+
{ \
15+
Scratch3Reader reader; \
16+
reader.setFileName(fileName); \
17+
ASSERT_TRUE(reader.load()); \
18+
ASSERT_TRUE(reader.isValid()); \
19+
engineName.setTargets(reader.targets()); \
20+
engineName.setBroadcasts(reader.broadcasts()); \
21+
engineName.setExtensions(reader.extensions()); \
22+
}
23+
624
using namespace libscratchcpp;
725

826
TEST(CompilerTest, EmptyProject)
@@ -92,3 +110,368 @@ TEST(CompilerTest, ResolveIds)
92110
ASSERT_EQ(block->nextId(), "");
93111
ASSERT_FALSE(block->next());
94112
}
113+
114+
TEST(CompilerTest, AddInstruction)
115+
{
116+
INIT_COMPILER(engine, compiler);
117+
compiler.addInstruction(vm::OP_START);
118+
compiler.addInstruction(vm::OP_NULL);
119+
compiler.addInstruction(vm::OP_CONST, { 0 });
120+
compiler.addInstruction(vm::OP_ADD);
121+
compiler.addInstruction(vm::OP_PRINT);
122+
compiler.addInstruction(vm::OP_HALT);
123+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_NULL, vm::OP_CONST, 0, vm::OP_ADD, vm::OP_PRINT, vm::OP_HALT }));
124+
}
125+
126+
void compileTestBlock(Compiler *compiler)
127+
{
128+
compiler->addInstruction(vm::OP_CHECKPOINT); // doesn't do anything, but is used in tests
129+
compiler->addInstruction(vm::OP_NULL);
130+
}
131+
132+
TEST(CompilerTest, AddShadowInput)
133+
{
134+
INIT_COMPILER(engine, compiler);
135+
compiler.addInstruction(vm::OP_START);
136+
137+
Input input("TEST_INPUT", Input::Type::Shadow);
138+
input.setPrimaryValue("test");
139+
compiler.addInput(&input);
140+
compiler.addInstruction(vm::OP_PRINT);
141+
142+
compiler.addInstruction(vm::OP_HALT);
143+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_PRINT, vm::OP_HALT }));
144+
}
145+
146+
TEST(CompilerTest, AddObscuredInput)
147+
{
148+
INIT_COMPILER(engine, compiler);
149+
compiler.addInstruction(vm::OP_START);
150+
151+
Input input1("TEST_INPUT1", Input::Type::ObscuredShadow);
152+
std::shared_ptr<Block> block1 = std::make_shared<Block>("", "test_block1");
153+
block1->setCompileFunction(&compileTestBlock);
154+
input1.setValueBlock(block1);
155+
compiler.addInput(&input1);
156+
compiler.addInstruction(vm::OP_PRINT);
157+
158+
Input input2("TEST_INPUT2", Input::Type::ObscuredShadow);
159+
compiler.addInput(&input2);
160+
compiler.addInstruction(vm::OP_PRINT);
161+
162+
Input input3("TEST_INPUT3", Input::Type::ObscuredShadow);
163+
std::shared_ptr<Block> block2 = std::make_shared<Block>("", "test_block2");
164+
input3.setValueBlock(block2);
165+
compiler.addInput(&input3);
166+
compiler.addInstruction(vm::OP_PRINT);
167+
168+
compiler.addInstruction(vm::OP_HALT);
169+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CHECKPOINT, vm::OP_NULL, vm::OP_PRINT, vm::OP_CONST, 0, vm::OP_PRINT, vm::OP_NULL, vm::OP_PRINT, vm::OP_HALT }));
170+
}
171+
172+
TEST(CompilerTest, AddNoShadowInput)
173+
{
174+
INIT_COMPILER(engine, compiler);
175+
compiler.addInstruction(vm::OP_START);
176+
177+
Input input1("TEST_INPUT1", Input::Type::NoShadow);
178+
std::shared_ptr<Block> block1 = std::make_shared<Block>("", "test_block1");
179+
block1->setCompileFunction(&compileTestBlock);
180+
input1.setValueBlock(block1);
181+
compiler.addInput(&input1);
182+
compiler.addInstruction(vm::OP_PRINT);
183+
184+
Input input2("TEST_INPUT2", Input::Type::NoShadow);
185+
EXPECT_DEATH({ compiler.addInput(&input2); }, "(Assertion)([ ]+)(.+)([ ]+)(failed)");
186+
187+
Input input3("TEST_INPUT3", Input::Type::NoShadow);
188+
std::shared_ptr<Block> block2 = std::make_shared<Block>("", "test_block2");
189+
input3.setValueBlock(block2);
190+
compiler.addInput(&input3);
191+
compiler.addInstruction(vm::OP_PRINT);
192+
193+
compiler.addInstruction(vm::OP_HALT);
194+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CHECKPOINT, vm::OP_NULL, vm::OP_PRINT, vm::OP_NULL, vm::OP_PRINT, vm::OP_HALT }));
195+
}
196+
197+
unsigned int testFunction1(VirtualMachine *vm)
198+
{
199+
return 0;
200+
}
201+
202+
unsigned int testFunction2(VirtualMachine *vm)
203+
{
204+
std::cout << vm->getInput(0, 1)->toString() << std::endl;
205+
return 1;
206+
}
207+
208+
TEST(CompilerTest, AddFunctionCall)
209+
{
210+
INIT_COMPILER(engine, compiler);
211+
compiler.addInstruction(vm::OP_START);
212+
compiler.addFunctionCall(&testFunction1);
213+
compiler.addFunctionCall(&testFunction2);
214+
compiler.addInstruction(vm::OP_HALT);
215+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_EXEC, 0, vm::OP_EXEC, 1, vm::OP_HALT }));
216+
ASSERT_EQ(engine.functionIndex(&testFunction1), 0);
217+
ASSERT_EQ(engine.functionIndex(&testFunction2), 1);
218+
}
219+
220+
TEST(CompilerTest, Warp)
221+
{
222+
INIT_COMPILER(engine, compiler);
223+
compiler.addInstruction(vm::OP_START);
224+
compiler.warp();
225+
compiler.addInstruction(vm::OP_HALT);
226+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_WARP, vm::OP_HALT }));
227+
}
228+
229+
TEST(CompilerTest, RepeatLoop)
230+
{
231+
LOAD_PROJECT("repeat10.sb3", engine);
232+
engine.resolveIds();
233+
Compiler compiler(&engine);
234+
compiler.compile(engine.targetAt(0)->greenFlagBlocks().at(0));
235+
ASSERT_EQ(
236+
compiler.bytecode(),
237+
std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_SET_VAR, 0, vm::OP_CONST, 1, vm::OP_REPEAT_LOOP, vm::OP_CONST, 2, vm::OP_CHANGE_VAR, 0, vm::OP_LOOP_END, vm::OP_HALT }));
238+
ASSERT_EQ(compiler.variablePtrs().size(), 1);
239+
ASSERT_EQ(compiler.variablePtrs()[0]->toString(), "test");
240+
ASSERT_EQ(compiler.lists().size(), 0);
241+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 0, 10, 1 }));
242+
}
243+
244+
TEST(CompilerTest, ForeverLoop)
245+
{
246+
LOAD_PROJECT("forever_loop.sb3", engine);
247+
engine.resolveIds();
248+
Compiler compiler(&engine);
249+
compiler.compile(engine.targetAt(0)->greenFlagBlocks().at(0));
250+
ASSERT_EQ(
251+
compiler.bytecode(),
252+
std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_SET_VAR, 0, vm::OP_FOREVER_LOOP, vm::OP_CONST, 1, vm::OP_CHANGE_VAR, 0, vm::OP_LOOP_END, vm::OP_HALT }));
253+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 0, 1 }));
254+
}
255+
256+
TEST(CompilerTest, RepeatUntilLoop)
257+
{
258+
LOAD_PROJECT("repeat_until.sb3", engine);
259+
engine.resolveIds();
260+
Compiler compiler(&engine);
261+
compiler.compile(engine.targetAt(0)->greenFlagBlocks().at(0));
262+
ASSERT_EQ(
263+
compiler.bytecode(),
264+
std::vector<unsigned int>(
265+
{ vm::OP_START,
266+
vm::OP_CONST,
267+
0,
268+
vm::OP_SET_VAR,
269+
0,
270+
vm::OP_UNTIL_LOOP,
271+
vm::OP_NULL,
272+
vm::OP_NOT,
273+
vm::OP_BEGIN_UNTIL_LOOP,
274+
vm::OP_CONST,
275+
1,
276+
vm::OP_CHANGE_VAR,
277+
0,
278+
vm::OP_LOOP_END,
279+
vm::OP_HALT }));
280+
}
281+
282+
TEST(CompilerTest, RepeatWhileLoop)
283+
{
284+
LOAD_PROJECT("repeat_while.sb3", engine);
285+
engine.resolveIds();
286+
Compiler compiler(&engine);
287+
compiler.compile(engine.targetAt(0)->greenFlagBlocks().at(0));
288+
ASSERT_EQ(
289+
compiler.bytecode(),
290+
std::vector<unsigned int>(
291+
{ vm::OP_START,
292+
vm::OP_CONST,
293+
0,
294+
vm::OP_SET_VAR,
295+
0,
296+
vm::OP_UNTIL_LOOP,
297+
vm::OP_NULL,
298+
vm::OP_NOT,
299+
vm::OP_NOT,
300+
vm::OP_BEGIN_UNTIL_LOOP,
301+
vm::OP_CONST,
302+
1,
303+
vm::OP_CHANGE_VAR,
304+
0,
305+
vm::OP_LOOP_END,
306+
vm::OP_HALT }));
307+
}
308+
309+
TEST(CompilerTest, RepeatForEachLoop)
310+
{
311+
LOAD_PROJECT("repeat_for_each.sb3", engine);
312+
engine.resolveIds();
313+
Compiler compiler(&engine);
314+
compiler.compile(engine.targetAt(0)->greenFlagBlocks().at(0));
315+
ASSERT_EQ(
316+
compiler.bytecode(),
317+
std::vector<unsigned int>(
318+
{ vm::OP_START,
319+
vm::OP_CONST,
320+
0,
321+
vm::OP_SET_VAR,
322+
0,
323+
vm::OP_CONST,
324+
1,
325+
vm::OP_REPEAT_LOOP,
326+
vm::OP_REPEAT_LOOP_INDEX1,
327+
vm::OP_SET_VAR,
328+
1,
329+
vm::OP_CONST,
330+
2,
331+
vm::OP_CHANGE_VAR,
332+
0,
333+
vm::OP_LOOP_END,
334+
vm::OP_HALT }));
335+
}
336+
337+
TEST(CompilerTest, IfStatement)
338+
{
339+
LOAD_PROJECT("if.sb3", engine);
340+
engine.resolveIds();
341+
Compiler compiler(&engine);
342+
compiler.compile(engine.targetAt(0)->greenFlagBlocks().at(0));
343+
ASSERT_EQ(
344+
compiler.bytecode(),
345+
std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_SET_VAR, 0, vm::OP_NULL, vm::OP_NOT, vm::OP_IF, vm::OP_CONST, 1, vm::OP_CHANGE_VAR, 0, vm::OP_ENDIF, vm::OP_HALT }));
346+
}
347+
348+
TEST(CompilerTest, IfElseStatement)
349+
{
350+
LOAD_PROJECT("if_else.sb3", engine);
351+
engine.resolveIds();
352+
Compiler compiler(&engine);
353+
compiler.compile(engine.targetAt(0)->greenFlagBlocks().at(0));
354+
ASSERT_EQ(
355+
compiler.bytecode(),
356+
std::vector<unsigned int>(
357+
{ vm::OP_START,
358+
vm::OP_CONST,
359+
0,
360+
vm::OP_SET_VAR,
361+
0,
362+
vm::OP_NULL,
363+
vm::OP_NOT,
364+
vm::OP_IF,
365+
vm::OP_CONST,
366+
1,
367+
vm::OP_CHANGE_VAR,
368+
0,
369+
vm::OP_ELSE,
370+
vm::OP_CONST,
371+
2,
372+
vm::OP_CHANGE_VAR,
373+
0,
374+
vm::OP_ENDIF,
375+
vm::OP_HALT }));
376+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 0, 1, -1 }));
377+
}
378+
379+
TEST(CompilerTest, NestedStatements)
380+
{
381+
LOAD_PROJECT("nested_statements.sb3", engine);
382+
engine.resolveIds();
383+
Compiler compiler(&engine);
384+
compiler.compile(engine.targetAt(0)->greenFlagBlocks().at(0));
385+
ASSERT_EQ(
386+
compiler.bytecode(),
387+
std::vector<unsigned int>(
388+
{ vm::OP_START,
389+
vm::OP_CONST,
390+
0,
391+
vm::OP_SET_VAR,
392+
0,
393+
vm::OP_NULL,
394+
vm::OP_NOT,
395+
vm::OP_IF,
396+
vm::OP_CONST,
397+
1,
398+
vm::OP_CHANGE_VAR,
399+
0,
400+
vm::OP_CONST,
401+
2,
402+
vm::OP_REPEAT_LOOP,
403+
vm::OP_NULL,
404+
vm::OP_IF,
405+
vm::OP_CONST,
406+
3,
407+
vm::OP_CHANGE_VAR,
408+
0,
409+
vm::OP_ENDIF,
410+
vm::OP_LOOP_END,
411+
vm::OP_ELSE,
412+
vm::OP_CONST,
413+
4,
414+
vm::OP_REPEAT_LOOP,
415+
vm::OP_CONST,
416+
5,
417+
vm::OP_CHANGE_VAR,
418+
0,
419+
vm::OP_NULL,
420+
vm::OP_NOT,
421+
vm::OP_IF,
422+
vm::OP_CONST,
423+
6,
424+
vm::OP_CHANGE_VAR,
425+
0,
426+
vm::OP_ENDIF,
427+
vm::OP_LOOP_END,
428+
vm::OP_ENDIF,
429+
vm::OP_HALT }));
430+
ASSERT_EQ(compiler.constValues(), std::vector<Value>({ 0, 1, 10, 2, 10, -1, 1 }));
431+
}
432+
433+
TEST(CompilerTest, CustomBlocks)
434+
{
435+
LOAD_PROJECT("custom_blocks.sb3", engine);
436+
engine.resolveIds();
437+
Compiler compiler(&engine);
438+
439+
auto stage = engine.targetAt(0);
440+
auto whenFlagClicked = stage->greenFlagBlocks().at(0);
441+
compiler.compile(whenFlagClicked);
442+
ASSERT_EQ(
443+
compiler.bytecode(),
444+
std::vector<unsigned int>(
445+
{ vm::OP_START,
446+
vm::OP_INIT_PROCEDURE,
447+
vm::OP_CONST,
448+
0,
449+
vm::OP_ADD_ARG,
450+
vm::OP_NULL,
451+
vm::OP_NOT,
452+
vm::OP_ADD_ARG,
453+
vm::OP_CALL_PROCEDURE,
454+
0,
455+
vm::OP_INIT_PROCEDURE,
456+
vm::OP_CALL_PROCEDURE,
457+
1,
458+
vm::OP_HALT }));
459+
460+
std::shared_ptr<Block> definition = nullptr;
461+
for (auto block : stage->blocks()) {
462+
if (block->opcode() == "procedures_prototype" && block->mutationPrototype()->procCode() == "test %s %b")
463+
definition = block->parent();
464+
}
465+
ASSERT_TRUE(definition);
466+
compiler.compile(definition);
467+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_WARP, vm::OP_READ_ARG, 0, vm::OP_SET_VAR, 0, vm::OP_READ_ARG, 1, vm::OP_SET_VAR, 1, vm::OP_HALT }));
468+
469+
definition = nullptr;
470+
for (auto block : stage->blocks()) {
471+
if (block->opcode() == "procedures_prototype" && block->mutationPrototype()->procCode() == "no warp test")
472+
definition = block->parent();
473+
}
474+
ASSERT_TRUE(definition);
475+
compiler.compile(definition);
476+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 1, vm::OP_SET_VAR, 2, vm::OP_HALT }));
477+
}

test/custom_blocks.sb3

1.91 KB
Binary file not shown.

test/forever_loop.sb3

1.16 KB
Binary file not shown.

test/if.sb3

1.17 KB
Binary file not shown.

test/if_else.sb3

1.21 KB
Binary file not shown.

test/nested_statements.sb3

1.43 KB
Binary file not shown.

test/repeat10.sb3

1.17 KB
Binary file not shown.

test/repeat_for_each.sb3

1009 Bytes
Binary file not shown.

test/repeat_until.sb3

1.21 KB
Binary file not shown.

test/repeat_while.sb3

1.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)