1
1
#include < scratchcpp/compiler.h>
2
2
#include < scratchcpp/block.h>
3
+ #include < scratchcpp/input.h>
3
4
#include < scratchcpp/field.h>
5
+ #include < scratchcpp/sprite.h>
4
6
#include < enginemock.h>
5
7
#include < timermock.h>
6
8
#include < clockmock.h>
7
9
8
10
#include " ../common.h"
9
11
#include " blocks/sensingblocks.h"
12
+ #include " blocks/operatorblocks.h"
10
13
#include " engine/internal/engine.h"
11
14
12
15
using namespace libscratchcpp ;
@@ -34,6 +37,46 @@ class SensingBlocksTest : public testing::Test
34
37
return block;
35
38
}
36
39
40
+ void addObscuredInput (std::shared_ptr<Block> block, const std::string &name, SensingBlocks::Inputs id, std::shared_ptr<Block> valueBlock) const
41
+ {
42
+ auto input = std::make_shared<Input>(name, Input::Type::ObscuredShadow);
43
+ input->setValueBlock (valueBlock);
44
+ input->setInputId (id);
45
+ block->addInput (input);
46
+ block->updateInputMap ();
47
+ }
48
+
49
+ std::shared_ptr<Input> addNullInput (std::shared_ptr<Block> block, const std::string &name, SensingBlocks::Inputs id) const
50
+ {
51
+ auto input = std::make_shared<Input>(name, Input::Type::Shadow);
52
+ input->setInputId (id);
53
+ block->addInput (input);
54
+ block->updateInputMap ();
55
+
56
+ return input;
57
+ }
58
+
59
+ void addDropdownInput (std::shared_ptr<Block> block, const std::string &name, SensingBlocks::Inputs id, const std::string &selectedValue, std::shared_ptr<Block> valueBlock = nullptr ) const
60
+ {
61
+ if (valueBlock)
62
+ addObscuredInput (block, name, id, valueBlock);
63
+ else {
64
+ auto input = addNullInput (block, name, id);
65
+ auto menu = std::make_shared<Block>(block->id () + " _menu" , block->opcode () + " _menu" );
66
+ input->setValueBlock (menu);
67
+ addDropdownField (menu, name, static_cast <SensingBlocks::Fields>(-1 ), selectedValue, static_cast <SensingBlocks::FieldValues>(-1 ));
68
+ }
69
+ }
70
+
71
+ void addDropdownField (std::shared_ptr<Block> block, const std::string &name, SensingBlocks::Fields id, const std::string &value, SensingBlocks::FieldValues valueId) const
72
+ {
73
+ auto field = std::make_shared<Field>(name, value);
74
+ field->setFieldId (id);
75
+ field->setSpecialValueId (valueId);
76
+ block->addField (field);
77
+ block->updateFieldMap ();
78
+ }
79
+
37
80
std::unique_ptr<IBlockSection> m_section;
38
81
EngineMock m_engineMock;
39
82
Engine m_engine;
@@ -54,10 +97,14 @@ TEST_F(SensingBlocksTest, CategoryVisible)
54
97
TEST_F (SensingBlocksTest, RegisterBlocks)
55
98
{
56
99
// Blocks
57
- EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_timer" , &SensingBlocks::compileTimer)).Times (1 );
58
- EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_resettimer" , &SensingBlocks::compileResetTimer)).Times (1 );
59
- EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_current" , &SensingBlocks::compileCurrent)).Times (1 );
60
- EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_dayssince2000" , &SensingBlocks::compileDaysSince2000)).Times (1 );
100
+ EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_distanceto" , &SensingBlocks::compileDistanceTo));
101
+ EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_timer" , &SensingBlocks::compileTimer));
102
+ EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_resettimer" , &SensingBlocks::compileResetTimer));
103
+ EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_current" , &SensingBlocks::compileCurrent));
104
+ EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_dayssince2000" , &SensingBlocks::compileDaysSince2000));
105
+
106
+ // Inputs
107
+ EXPECT_CALL (m_engineMock, addInput (m_section.get (), " DISTANCETOMENU" , SensingBlocks::DISTANCETOMENU));
61
108
62
109
// Fields
63
110
EXPECT_CALL (m_engineMock, addField (m_section.get (), " CURRENTMENU" , SensingBlocks::CURRENTMENU));
@@ -74,6 +121,141 @@ TEST_F(SensingBlocksTest, RegisterBlocks)
74
121
m_section->registerBlocks (&m_engineMock);
75
122
}
76
123
124
+ TEST_F (SensingBlocksTest, DistanceTo)
125
+ {
126
+ Compiler compiler (&m_engineMock);
127
+
128
+ // distance to (Sprite2)
129
+ auto block1 = std::make_shared<Block>(" a" , " sensing_distanceto" );
130
+ addDropdownInput (block1, " DISTANCETOMENU" , SensingBlocks::DISTANCETOMENU, " Sprite2" );
131
+
132
+ // distance to (mouse-pointer)
133
+ auto block2 = std::make_shared<Block>(" b" , " sensing_distanceto" );
134
+ addDropdownInput (block2, " DISTANCETOMENU" , SensingBlocks::DISTANCETOMENU, " _mouse_" );
135
+
136
+ // distance to (join "" "")
137
+ auto joinBlock = std::make_shared<Block>(" d" , " operator_join" );
138
+ joinBlock->setCompileFunction (&OperatorBlocks::compileJoin);
139
+ auto block3 = std::make_shared<Block>(" c" , " sensing_distanceto" );
140
+ addDropdownInput (block3, " DISTANCETOMENU" , SensingBlocks::DISTANCETOMENU, " " , joinBlock);
141
+
142
+ compiler.init ();
143
+
144
+ EXPECT_CALL (m_engineMock, findTarget (" Sprite2" )).WillOnce (Return (5 ));
145
+ EXPECT_CALL (m_engineMock, functionIndex (&SensingBlocks::distanceToByIndex)).WillOnce (Return (0 ));
146
+ compiler.setBlock (block1);
147
+ SensingBlocks::compileDistanceTo (&compiler);
148
+
149
+ EXPECT_CALL (m_engineMock, functionIndex (&SensingBlocks::distanceToMousePointer)).WillOnce (Return (1 ));
150
+ compiler.setBlock (block2);
151
+ SensingBlocks::compileDistanceTo (&compiler);
152
+
153
+ EXPECT_CALL (m_engineMock, functionIndex (&SensingBlocks::distanceTo)).WillOnce (Return (2 ));
154
+ compiler.setBlock (block3);
155
+ SensingBlocks::compileDistanceTo (&compiler);
156
+
157
+ compiler.end ();
158
+
159
+ ASSERT_EQ (
160
+ compiler.bytecode (),
161
+ std::vector<unsigned int >({ vm::OP_START, vm::OP_CONST, 0 , vm::OP_EXEC, 0 , vm::OP_EXEC, 1 , vm::OP_NULL, vm::OP_NULL, vm::OP_STR_CONCAT, vm::OP_EXEC, 2 , vm::OP_HALT }));
162
+ ASSERT_EQ (compiler.constValues ().size (), 1 );
163
+ ASSERT_EQ (compiler.constValues ()[0 ].toDouble (), 5 );
164
+ }
165
+
166
+ TEST_F (SensingBlocksTest, DistanceToImpl)
167
+ {
168
+ static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0 , vm::OP_EXEC, 0 , vm::OP_HALT };
169
+ static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1 , vm::OP_EXEC, 0 , vm::OP_HALT };
170
+ static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2 , vm::OP_EXEC, 0 , vm::OP_HALT };
171
+ static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 3 , vm::OP_EXEC, 1 , vm::OP_HALT };
172
+ static unsigned int bytecode5[] = { vm::OP_START, vm::OP_CONST, 4 , vm::OP_EXEC, 1 , vm::OP_HALT };
173
+ static unsigned int bytecode6[] = { vm::OP_START, vm::OP_CONST, 5 , vm::OP_EXEC, 1 , vm::OP_HALT };
174
+ static unsigned int bytecode7[] = { vm::OP_START, vm::OP_CONST, 6 , vm::OP_EXEC, 1 , vm::OP_HALT };
175
+ static unsigned int bytecode8[] = { vm::OP_START, vm::OP_EXEC, 2 , vm::OP_HALT };
176
+ static BlockFunc functions[] = { &SensingBlocks::distanceTo, &SensingBlocks::distanceToByIndex, &SensingBlocks::distanceToMousePointer };
177
+ static Value constValues[] = { " Sprite2" , " _mouse_" , " " , 0 , 1 , -1 , 2 };
178
+
179
+ Sprite sprite1;
180
+ sprite1.setX (-50.35 );
181
+ sprite1.setY (33.04 );
182
+
183
+ Sprite sprite2;
184
+ sprite2.setX (108.564 );
185
+ sprite2.setY (-168.452 );
186
+
187
+ VirtualMachine vm (&sprite1, &m_engineMock, nullptr );
188
+ vm.setFunctions (functions);
189
+ vm.setConstValues (constValues);
190
+
191
+ EXPECT_CALL (m_engineMock, findTarget (" Sprite2" )).WillOnce (Return (3 ));
192
+ EXPECT_CALL (m_engineMock, targetAt (3 )).WillOnce (Return (&sprite2));
193
+ vm.setBytecode (bytecode1);
194
+ vm.run ();
195
+
196
+ ASSERT_EQ (vm.registerCount (), 1 );
197
+ ASSERT_EQ (std::round (vm.getInput (0 , 1 )->toDouble () * 10000 ) / 10000 , 256.6178 );
198
+
199
+ EXPECT_CALL (m_engineMock, mouseX ()).WillOnce (Return (-239.98 ));
200
+ EXPECT_CALL (m_engineMock, mouseY ()).WillOnce (Return (-86.188 ));
201
+ vm.setBytecode (bytecode2);
202
+ vm.reset ();
203
+ vm.run ();
204
+
205
+ ASSERT_EQ (vm.registerCount (), 1 );
206
+ ASSERT_EQ (std::round (vm.getInput (0 , 1 )->toDouble () * 10000 ) / 10000 , 223.9974 );
207
+
208
+ EXPECT_CALL (m_engineMock, findTarget (" " )).WillOnce (Return (-1 ));
209
+ EXPECT_CALL (m_engineMock, targetAt (-1 )).WillOnce (Return (nullptr ));
210
+ vm.setBytecode (bytecode3);
211
+ vm.reset ();
212
+ vm.run ();
213
+
214
+ ASSERT_EQ (vm.registerCount (), 1 );
215
+ ASSERT_EQ (vm.getInput (0 , 1 )->toDouble (), 10000 );
216
+
217
+ EXPECT_CALL (m_engineMock, targetAt (0 )).WillOnce (Return (&sprite1));
218
+ vm.setBytecode (bytecode4);
219
+ vm.reset ();
220
+ vm.run ();
221
+
222
+ ASSERT_EQ (vm.registerCount (), 1 );
223
+ ASSERT_EQ (vm.getInput (0 , 1 )->toDouble (), 0 );
224
+
225
+ EXPECT_CALL (m_engineMock, targetAt (1 )).WillOnce (Return (&sprite2));
226
+ vm.setBytecode (bytecode5);
227
+ vm.reset ();
228
+ vm.run ();
229
+
230
+ ASSERT_EQ (vm.registerCount (), 1 );
231
+ ASSERT_EQ (std::round (vm.getInput (0 , 1 )->toDouble () * 10000 ) / 10000 , 256.6178 );
232
+
233
+ EXPECT_CALL (m_engineMock, targetAt (-1 )).WillOnce (Return (nullptr ));
234
+ vm.setBytecode (bytecode6);
235
+ vm.reset ();
236
+ vm.run ();
237
+
238
+ ASSERT_EQ (vm.registerCount (), 1 );
239
+ ASSERT_EQ (vm.getInput (0 , 1 )->toDouble (), 10000 );
240
+
241
+ EXPECT_CALL (m_engineMock, targetAt (2 )).WillOnce (Return (nullptr ));
242
+ vm.setBytecode (bytecode7);
243
+ vm.reset ();
244
+ vm.run ();
245
+
246
+ ASSERT_EQ (vm.registerCount (), 1 );
247
+ ASSERT_EQ (vm.getInput (0 , 1 )->toDouble (), 10000 );
248
+
249
+ EXPECT_CALL (m_engineMock, mouseX ()).WillOnce (Return (168.087 ));
250
+ EXPECT_CALL (m_engineMock, mouseY ()).WillOnce (Return (175.908 ));
251
+ vm.setBytecode (bytecode8);
252
+ vm.reset ();
253
+ vm.run ();
254
+
255
+ ASSERT_EQ (vm.registerCount (), 1 );
256
+ ASSERT_EQ (std::round (vm.getInput (0 , 1 )->toDouble () * 10000 ) / 10000 , 261.0096 );
257
+ }
258
+
77
259
TEST_F (SensingBlocksTest, Timer)
78
260
{
79
261
Compiler compiler (&m_engineMock);
0 commit comments