Skip to content

Commit 7b44e36

Browse files
committed
Add load project test
1 parent 7283b45 commit 7b44e36

File tree

4 files changed

+275
-0
lines changed

4 files changed

+275
-0
lines changed

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ enable_testing()
1515

1616
include(GoogleTest)
1717
add_subdirectory(zip)
18+
add_subdirectory(load_project)

test/load_project/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
add_executable(
2+
load_project_test
3+
load_project_test.cpp
4+
)
5+
6+
target_link_libraries(
7+
load_project_test
8+
GTest::gtest_main
9+
scratchcpp
10+
)
11+
12+
gtest_discover_tests(load_project_test)
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
#include "internal/scratch3reader.h"
2+
#include "scratch/stage.h"
3+
#include "scratch/sprite.h"
4+
#include "engine/engine.h"
5+
#include "../common.h"
6+
7+
#define ASSERT_VAR(target, varName) \
8+
ASSERT_NE(target->findVariable(varName), -1); \
9+
ASSERT_TRUE(target->variableAt(target->findVariable(varName)))
10+
#define GET_VAR(target, varName) target->variableAt(target->findVariable(varName))
11+
12+
#define ASSERT_LIST(target, listName) \
13+
ASSERT_NE(target->findList(listName), -1); \
14+
ASSERT_TRUE(target->listAt(target->findList(listName)))
15+
#define GET_LIST(target, listName) target->listAt(target->findList(listName))
16+
17+
#define ASSERT_INPUT(block, inputName) \
18+
ASSERT_NE(block->findInput(inputName), -1); \
19+
ASSERT_TRUE(block->inputAt(block->findInput(inputName)))
20+
#define GET_INPUT(block, inputName) block->inputAt(block->findInput(inputName))
21+
22+
#define ASSERT_FIELD(block, fieldName) \
23+
ASSERT_NE(block->findField(fieldName), -1); \
24+
ASSERT_TRUE(block->fieldAt(block->findField(fieldName)))
25+
#define GET_FIELD(block, fieldName) block->fieldAt(block->findField(fieldName))
26+
27+
using namespace libscratchcpp;
28+
29+
static Scratch3Reader s3reader;
30+
static const std::vector<IProjectReader *> readers = { &s3reader };
31+
static const std::unordered_map<IProjectReader *, std::string> fileExtensions = { { &s3reader, ".sb3" } };
32+
33+
TEST(LoadProjectTest, EmptyProject)
34+
{
35+
for (auto reader : readers) {
36+
reader->clear();
37+
reader->setFileName("empty_project" + fileExtensions.at(reader));
38+
39+
ASSERT_TRUE(reader->load());
40+
ASSERT_TRUE(reader->isValid());
41+
ASSERT_EQ(reader->targets().size(), 1);
42+
ASSERT_EQ(reader->extensions().size(), 0);
43+
ASSERT_EQ(reader->broadcasts().size(), 0);
44+
45+
std::shared_ptr<Stage> stage = std::reinterpret_pointer_cast<Stage>(reader->targets().at(0));
46+
ASSERT_TRUE(stage->isStage());
47+
ASSERT_EQ(stage->name(), "Stage");
48+
ASSERT_EQ(stage->variables().size(), 0);
49+
ASSERT_EQ(stage->lists().size(), 0);
50+
ASSERT_EQ(stage->blocks().size(), 0);
51+
ASSERT_EQ(stage->costumes().size(), 1);
52+
// TODO: Add comments
53+
ASSERT_EQ(stage->currentCostume(), 0);
54+
ASSERT_EQ(stage->sounds().size(), 0);
55+
ASSERT_EQ(stage->layerOrder(), 0);
56+
ASSERT_EQ(stage->volume(), 100);
57+
ASSERT_EQ(stage->tempo(), 60);
58+
ASSERT_EQ(stage->videoState(), Stage::VideoState::On);
59+
ASSERT_EQ(stage->videoTransparency(), 50);
60+
ASSERT_TRUE(stage->textToSpeechLanguage().empty());
61+
62+
auto backdrop = stage->costumeAt(0);
63+
ASSERT_EQ(backdrop.name(), "backdrop1");
64+
ASSERT_FALSE(backdrop.assetId().empty());
65+
ASSERT_EQ(backdrop.md5ext(), backdrop.assetId() + ".svg");
66+
ASSERT_EQ(backdrop.dataFormat(), "svg");
67+
}
68+
}
69+
70+
TEST(LoadProjectTest, LoadTestProject)
71+
{
72+
for (auto reader : readers) {
73+
reader->clear();
74+
reader->setFileName("load_test" + fileExtensions.at(reader));
75+
76+
ASSERT_TRUE(reader->load());
77+
ASSERT_TRUE(reader->isValid());
78+
ASSERT_EQ(reader->targets().size(), 3);
79+
ASSERT_EQ(reader->extensions().size(), 0);
80+
ASSERT_EQ(reader->broadcasts().size(), 1);
81+
Engine engine;
82+
engine.setTargets(reader->targets());
83+
engine.setBroadcasts(reader->broadcasts());
84+
engine.setExtensions(reader->extensions());
85+
engine.compile();
86+
87+
// Stage
88+
ASSERT_NE(engine.findTarget("Stage"), -1);
89+
Stage *stage = dynamic_cast<Stage *>(engine.targetAt(engine.findTarget("Stage")));
90+
ASSERT_EQ(stage->variables().size(), 2);
91+
ASSERT_EQ(stage->lists().size(), 1);
92+
93+
// Stage: variables
94+
ASSERT_VAR(stage, "var1");
95+
ASSERT_EQ(GET_VAR(stage, "var1")->value().toString(), "Hello, world!");
96+
ASSERT_VAR(stage, "var3");
97+
ASSERT_EQ(GET_VAR(stage, "var3")->value().toDouble(), std::pow(10, 50));
98+
99+
// Stage: lists
100+
ASSERT_LIST(stage, "list1");
101+
ASSERT_EQ(GET_LIST(stage, "list1")->toString(), "3 1 4 8 7 6 7 7 2 4");
102+
103+
// Sprite1
104+
ASSERT_NE(engine.findTarget("Sprite1"), -1);
105+
Sprite *sprite1 = dynamic_cast<Sprite *>(engine.targetAt(engine.findTarget("Sprite1")));
106+
ASSERT_FALSE(sprite1->isStage());
107+
ASSERT_EQ(sprite1->name(), "Sprite1");
108+
ASSERT_EQ(sprite1->variables().size(), 1);
109+
ASSERT_EQ(sprite1->lists().size(), 1);
110+
ASSERT_EQ(sprite1->blocks().size(), 18);
111+
ASSERT_EQ(sprite1->costumes().size(), 2);
112+
ASSERT_EQ(sprite1->sounds().size(), 1);
113+
ASSERT_TRUE(sprite1->visible());
114+
ASSERT_EQ(sprite1->x(), 0);
115+
ASSERT_EQ(sprite1->y(), 0);
116+
ASSERT_EQ(sprite1->size(), 100);
117+
ASSERT_EQ(sprite1->direction(), 90);
118+
ASSERT_FALSE(sprite1->draggable());
119+
ASSERT_EQ(sprite1->rotationStyleStr(), "all around");
120+
ASSERT_EQ(sprite1->rotationStyle(), Sprite::RotationStyle::AllAround);
121+
122+
// Sprite1: sounds
123+
auto sound = sprite1->soundAt(0);
124+
ASSERT_EQ(sound.name(), "Meow");
125+
ASSERT_FALSE(sound.assetId().empty());
126+
ASSERT_EQ(sound.md5ext(), sound.assetId() + ".wav");
127+
ASSERT_EQ(sound.dataFormat(), "wav");
128+
129+
// Sprite1: variables
130+
ASSERT_VAR(sprite1, "var2");
131+
ASSERT_EQ(GET_VAR(sprite1, "var2")->value().toDouble(), 0.5);
132+
133+
// Sprite1: lists
134+
ASSERT_LIST(sprite1, "list2");
135+
ASSERT_EQ(GET_LIST(sprite1, "list2")->toString(), "9 6 3 8 0 4 4 2 9 7");
136+
137+
// Sprite1: blocks
138+
ASSERT_EQ(sprite1->greenFlagBlocks().size(), 1);
139+
ASSERT_TRUE(sprite1->greenFlagBlocks()[0]);
140+
ASSERT_EQ(sprite1->greenFlagBlocks()[0]->opcode(), "event_whenflagclicked");
141+
auto block = sprite1->greenFlagBlocks()[0]->next();
142+
ASSERT_TRUE(block);
143+
ASSERT_EQ(block->parent(), sprite1->greenFlagBlocks()[0]);
144+
ASSERT_EQ(block->opcode(), "control_forever");
145+
ASSERT_INPUT(block, "SUBSTACK");
146+
block = GET_INPUT(block, "SUBSTACK")->valueBlock();
147+
ASSERT_TRUE(block);
148+
149+
ASSERT_EQ(block->opcode(), "motion_goto");
150+
ASSERT_INPUT(block, "TO");
151+
auto inputBlock = GET_INPUT(block, "TO")->valueBlock();
152+
ASSERT_TRUE(inputBlock);
153+
ASSERT_EQ(inputBlock->opcode(), "motion_goto_menu");
154+
ASSERT_FIELD(inputBlock, "TO");
155+
ASSERT_EQ(GET_FIELD(inputBlock, "TO")->value().toString(), "_random_");
156+
block = block->next();
157+
ASSERT_TRUE(block);
158+
159+
ASSERT_EQ(block->opcode(), "data_setvariableto");
160+
ASSERT_FIELD(block, "VARIABLE");
161+
ASSERT_EQ(GET_FIELD(block, "VARIABLE")->valuePtr(), GET_VAR(stage, "var1"));
162+
ASSERT_INPUT(block, "VALUE");
163+
ASSERT_EQ(GET_INPUT(block, "VALUE")->primaryValue()->value().toInt(), 0);
164+
block = block->next();
165+
ASSERT_TRUE(block);
166+
167+
ASSERT_INPUT(block, "VALUE");
168+
inputBlock = GET_INPUT(block, "VALUE")->valueBlock();
169+
ASSERT_TRUE(inputBlock);
170+
ASSERT_EQ(inputBlock->opcode(), "operator_random");
171+
ASSERT_INPUT(inputBlock, "FROM");
172+
ASSERT_EQ(GET_INPUT(inputBlock, "FROM")->primaryValue()->value().toInt(), 1);
173+
ASSERT_INPUT(inputBlock, "TO");
174+
ASSERT_EQ(GET_INPUT(inputBlock, "TO")->primaryValue()->value().toInt(), 10);
175+
block = block->next();
176+
ASSERT_TRUE(block);
177+
178+
ASSERT_EQ(block->opcode(), "procedures_call");
179+
auto prototype = block->mutationPrototype();
180+
ASSERT_TRUE(prototype->warp());
181+
ASSERT_EQ(prototype->argumentIds().size(), 2);
182+
ASSERT_EQ(prototype->procCode(), "custom block %s %b");
183+
ASSERT_INPUT(block, prototype->argumentIds()[0]);
184+
ASSERT_EQ(GET_INPUT(block, prototype->argumentIds()[0])->primaryValue()->value().toString(), "test");
185+
ASSERT_INPUT(block, prototype->argumentIds()[1]);
186+
ASSERT_TRUE(GET_INPUT(block, prototype->argumentIds()[1])->valueBlock());
187+
188+
ASSERT_FALSE(block->next());
189+
190+
auto blocks = sprite1->blocks();
191+
std::shared_ptr<Block> defineBlock = nullptr;
192+
for (auto b : blocks) {
193+
if (b->opcode() == "procedures_definition") {
194+
defineBlock = b;
195+
break;
196+
}
197+
}
198+
ASSERT_TRUE(defineBlock);
199+
ASSERT_INPUT(defineBlock, "custom_block");
200+
auto blockPrototype = GET_INPUT(defineBlock, "custom_block")->valueBlock();
201+
ASSERT_TRUE(blockPrototype);
202+
prototype = blockPrototype->mutationPrototype();
203+
ASSERT_TRUE(prototype->warp());
204+
ASSERT_EQ(prototype->argumentIds().size(), 2);
205+
ASSERT_EQ(prototype->argumentNames(), std::vector<std::string>({ "num or text", "bool" }));
206+
ASSERT_EQ(prototype->argumentTypes(), std::vector<BlockPrototype::ArgType>({ BlockPrototype::ArgType::StringNum, BlockPrototype::ArgType::Bool }));
207+
ASSERT_EQ(prototype->argumentDefaults(), std::vector<Value>({ 0, false }));
208+
ASSERT_EQ(prototype->procCode(), "custom block %s %b");
209+
ASSERT_INPUT(blockPrototype, prototype->argumentIds()[0]);
210+
auto argBlock = GET_INPUT(blockPrototype, prototype->argumentIds()[0])->valueBlock();
211+
ASSERT_TRUE(argBlock);
212+
ASSERT_EQ(argBlock->opcode(), "argument_reporter_string_number");
213+
ASSERT_INPUT(blockPrototype, prototype->argumentIds()[1]);
214+
argBlock = GET_INPUT(blockPrototype, prototype->argumentIds()[1])->valueBlock();
215+
ASSERT_TRUE(argBlock);
216+
ASSERT_EQ(argBlock->opcode(), "argument_reporter_boolean");
217+
block = defineBlock->next();
218+
ASSERT_TRUE(block);
219+
220+
ASSERT_EQ(block->opcode(), "data_addtolist");
221+
ASSERT_FIELD(block, "LIST");
222+
ASSERT_EQ(GET_FIELD(block, "LIST")->valuePtr(), GET_LIST(stage, "list1"));
223+
ASSERT_INPUT(block, "ITEM");
224+
argBlock = GET_INPUT(block, "ITEM")->valueBlock();
225+
ASSERT_TRUE(argBlock);
226+
ASSERT_EQ(argBlock->opcode(), "argument_reporter_string_number");
227+
228+
// Balloon1
229+
ASSERT_NE(engine.findTarget("Balloon1"), -1);
230+
Sprite *sprite2 = dynamic_cast<Sprite *>(engine.targetAt(engine.findTarget("Balloon1")));
231+
ASSERT_FALSE(sprite2->isStage());
232+
ASSERT_EQ(sprite2->name(), "Balloon1");
233+
ASSERT_EQ(sprite2->variables().size(), 1);
234+
ASSERT_EQ(sprite2->lists().size(), 1);
235+
ASSERT_EQ(sprite2->blocks().size(), 5);
236+
ASSERT_EQ(sprite2->costumes().size(), 3);
237+
ASSERT_EQ(sprite2->sounds().size(), 1);
238+
ASSERT_FALSE(sprite2->visible());
239+
ASSERT_EQ(std::round(sprite2->x()), 143);
240+
ASSERT_EQ(std::round(sprite2->y()), 13);
241+
ASSERT_EQ(sprite2->size(), 75);
242+
ASSERT_EQ(sprite2->direction(), 45);
243+
ASSERT_TRUE(sprite2->draggable());
244+
ASSERT_EQ(sprite2->rotationStyleStr(), "don't rotate");
245+
ASSERT_EQ(sprite2->rotationStyle(), Sprite::RotationStyle::DoNotRotate);
246+
247+
// Balloon1: sounds
248+
sound = sprite2->soundAt(0);
249+
ASSERT_EQ(sound.name(), "Pop");
250+
ASSERT_FALSE(sound.assetId().empty());
251+
ASSERT_EQ(sound.md5ext(), sound.assetId() + ".wav");
252+
ASSERT_EQ(sound.dataFormat(), "wav");
253+
254+
// Balloon1: variables
255+
ASSERT_VAR(sprite2, "var2");
256+
ASSERT_TRUE(GET_VAR(sprite2, "var2")->value().toBool());
257+
258+
// Balloon1: lists
259+
ASSERT_LIST(sprite2, "list2");
260+
ASSERT_EQ(GET_LIST(sprite2, "list2")->toString(), "0 4 3 4 1 5 6 9 4 8");
261+
}
262+
}

test/load_test.sb3

47.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)