Skip to content

Commit f66b384

Browse files
committed
Add custom blocks test
1 parent e1afd90 commit f66b384

File tree

2 files changed

+192
-0
lines changed

2 files changed

+192
-0
lines changed

test/blocks/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,19 @@ target_link_libraries(
7777
)
7878

7979
gtest_discover_tests(control_blocks_test)
80+
81+
# custom_blocks_test
82+
add_executable(
83+
custom_blocks_test
84+
custom_blocks_test.cpp
85+
)
86+
87+
target_link_libraries(
88+
custom_blocks_test
89+
GTest::gtest_main
90+
GTest::gmock_main
91+
scratchcpp
92+
scratchcpp_mocks
93+
)
94+
95+
gtest_discover_tests(custom_blocks_test)

test/blocks/custom_blocks_test.cpp

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#include <scratchcpp/compiler.h>
2+
#include <scratchcpp/block.h>
3+
#include <scratchcpp/input.h>
4+
#include <scratchcpp/field.h>
5+
#include <enginemock.h>
6+
7+
#include "../common.h"
8+
#include "blocks/customblocks.h"
9+
#include "engine/engine.h"
10+
11+
using namespace libscratchcpp;
12+
13+
using ::testing::Return;
14+
15+
class CustomBlocksTest : public testing::Test
16+
{
17+
public:
18+
void SetUp() override
19+
{
20+
m_section = std::make_unique<CustomBlocks>();
21+
m_section->registerBlocks(&m_engine);
22+
}
23+
24+
void addPrototypeInput(std::shared_ptr<Block> definitionBlock, std::shared_ptr<Block> prototypeBlock) const
25+
{
26+
auto input = std::make_shared<Input>("custom_block", Input::Type::NoShadow);
27+
input->setValueBlock(prototypeBlock);
28+
input->setInputId(CustomBlocks::CUSTOM_BLOCK);
29+
definitionBlock->addInput(input);
30+
definitionBlock->updateInputMap();
31+
}
32+
33+
void addArgumentInput(std::shared_ptr<Block> block, const std::string &argId, const Value &value) const
34+
{
35+
auto input = std::make_shared<Input>(argId, Input::Type::Shadow);
36+
input->setPrimaryValue(value);
37+
block->addInput(input);
38+
}
39+
40+
std::unique_ptr<IBlockSection> m_section;
41+
EngineMock m_engineMock;
42+
Engine m_engine;
43+
};
44+
45+
TEST_F(CustomBlocksTest, Name)
46+
{
47+
ASSERT_EQ(m_section->name(), "Custom blocks");
48+
}
49+
50+
TEST_F(CustomBlocksTest, CategoryVisible)
51+
{
52+
ASSERT_TRUE(m_section->categoryVisible());
53+
}
54+
55+
TEST_F(CustomBlocksTest, RegisterBlocks)
56+
{
57+
// Blocks
58+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "procedures_definition", &CustomBlocks::compileDefinition)).Times(1);
59+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "procedures_call", &CustomBlocks::compileCall)).Times(1);
60+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "argument_reporter_boolean", &CustomBlocks::compileArgument)).Times(1);
61+
EXPECT_CALL(m_engineMock, addCompileFunction(m_section.get(), "argument_reporter_string_number", &CustomBlocks::compileArgument)).Times(1);
62+
63+
// Inputs
64+
EXPECT_CALL(m_engineMock, addInput(m_section.get(), "custom_block", CustomBlocks::CUSTOM_BLOCK));
65+
66+
// Fields
67+
EXPECT_CALL(m_engineMock, addField(m_section.get(), "VALUE", CustomBlocks::VALUE));
68+
69+
m_section->registerBlocks(&m_engineMock);
70+
}
71+
72+
TEST_F(CustomBlocksTest, CustomBlocks)
73+
{
74+
Compiler compiler(&m_engine);
75+
76+
// define [ test (string or number) (boolean) ]
77+
// print (boolean)
78+
// print (invalid) - to test non-existent arguments (arguments can be dragged from another custom block definition)
79+
auto block1 = std::make_shared<Block>("a", "procedures_definition");
80+
auto prototypeBlock = std::make_shared<Block>("b", "procedures_prototype");
81+
auto prototype1 = prototypeBlock->mutationPrototype();
82+
prototype1->setProcCode("test %s %b");
83+
prototype1->setArgumentNames({ "string or number", "boolean" });
84+
prototype1->setArgumentIds({ "c", "d" });
85+
prototype1->setWarp(false);
86+
addPrototypeInput(block1, prototypeBlock);
87+
block1->setCompileFunction(&CustomBlocks::compileDefinition);
88+
89+
auto testBlock = std::make_shared<Block>("e", "some_block");
90+
91+
auto input = std::make_shared<Input>("test_input", Input::Type::ObscuredShadow);
92+
auto argBlock = std::make_shared<Block>("f", "argument_reporter_boolean");
93+
argBlock->setCompileFunction(&CustomBlocks::compileArgument);
94+
auto valueField = std::make_shared<Field>("VALUE", "boolean");
95+
valueField->setFieldId(CustomBlocks::VALUE);
96+
argBlock->addField(valueField);
97+
argBlock->updateFieldMap();
98+
input->setValueBlock(argBlock);
99+
input->setInputId(-100);
100+
testBlock->addInput(input);
101+
102+
input = std::make_shared<Input>("test_input2", Input::Type::ObscuredShadow);
103+
argBlock = std::make_shared<Block>("g", "argument_reporter_boolean");
104+
argBlock->setCompileFunction(&CustomBlocks::compileArgument);
105+
valueField = std::make_shared<Field>("VALUE", "invalid");
106+
valueField->setFieldId(CustomBlocks::VALUE);
107+
argBlock->addField(valueField);
108+
argBlock->updateFieldMap();
109+
input->setValueBlock(argBlock);
110+
input->setInputId(-101);
111+
testBlock->addInput(input);
112+
113+
testBlock->updateInputMap();
114+
testBlock->setCompileFunction([](Compiler *compiler) {
115+
compiler->addInput(-100);
116+
compiler->addInstruction(vm::OP_PRINT);
117+
compiler->addInput(-101);
118+
compiler->addInstruction(vm::OP_PRINT);
119+
});
120+
testBlock->setParent(block1);
121+
block1->setNext(testBlock);
122+
123+
// define [ (a) + (b) ]
124+
// (run without screen refresh)
125+
auto block2 = std::make_shared<Block>("h", "procedures_definition");
126+
prototypeBlock = std::make_shared<Block>("i", "procedures_prototype");
127+
auto prototype2 = prototypeBlock->mutationPrototype();
128+
prototype2->setProcCode("%s + %s");
129+
prototype2->setArgumentNames({ "a", "b" });
130+
prototype2->setArgumentIds({ "j", "k" });
131+
prototype2->setWarp(true);
132+
addPrototypeInput(block2, prototypeBlock);
133+
block2->setCompileFunction(&CustomBlocks::compileDefinition);
134+
135+
// call [ 5 + (null) ]
136+
auto block3 = std::make_shared<Block>("l", "procedures_call");
137+
auto prototype3 = block3->mutationPrototype();
138+
prototype3->setProcCode(prototype2->procCode());
139+
prototype3->setArgumentNames(prototype2->argumentNames());
140+
prototype3->setArgumentIds(prototype2->argumentIds());
141+
addArgumentInput(block3, "j", 5);
142+
block3->setCompileFunction(&CustomBlocks::compileCall);
143+
144+
compiler.compile(block1);
145+
146+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_READ_ARG, 1, vm::OP_PRINT, vm::OP_NULL, vm::OP_PRINT, vm::OP_HALT }));
147+
ASSERT_EQ(compiler.procedureArgIndex("test %s %b", "string or number"), 0);
148+
ASSERT_EQ(compiler.procedureArgIndex("test %s %b", "boolean"), 1);
149+
ASSERT_EQ(compiler.procedureArgIndex("test %s %b", "invalid"), -1);
150+
ASSERT_EQ(compiler.procedureArgIndex("test %s %s", "boolean"), -1);
151+
ASSERT_TRUE(compiler.constValues().empty());
152+
ASSERT_TRUE(compiler.variables().empty());
153+
ASSERT_TRUE(compiler.lists().empty());
154+
155+
compiler.compile(block2);
156+
157+
ASSERT_EQ(compiler.bytecode(), std::vector<unsigned int>({ vm::OP_START, vm::OP_WARP, vm::OP_HALT }));
158+
ASSERT_EQ(compiler.procedureArgIndex("%s + %s", "a"), 0);
159+
ASSERT_EQ(compiler.procedureArgIndex("%s + %s", "b"), 1);
160+
ASSERT_EQ(compiler.procedureArgIndex("%s + %s", "invalid"), -1);
161+
ASSERT_EQ(compiler.procedureArgIndex("test %s %s", "a"), -1);
162+
ASSERT_TRUE(compiler.constValues().empty());
163+
ASSERT_TRUE(compiler.variables().empty());
164+
ASSERT_TRUE(compiler.lists().empty());
165+
166+
compiler.compile(block3);
167+
168+
ASSERT_EQ(
169+
compiler.bytecode(),
170+
std::vector<unsigned int>({ vm::OP_START, vm::OP_INIT_PROCEDURE, vm::OP_CONST, 0, vm::OP_ADD_ARG, vm::OP_NULL, vm::OP_ADD_ARG, vm::OP_CALL_PROCEDURE, 0, vm::OP_HALT }));
171+
ASSERT_EQ(compiler.procedureIndex("%s + %s"), 0);
172+
ASSERT_EQ(compiler.constValues().size(), 1);
173+
ASSERT_EQ(compiler.constValues(), std::vector<Value>{ 5 });
174+
ASSERT_TRUE(compiler.variables().empty());
175+
ASSERT_TRUE(compiler.lists().empty());
176+
}

0 commit comments

Comments
 (0)