11#include < scratchcpp/compiler.h>
22#include < scratchcpp/block.h>
3+ #include < scratchcpp/input.h>
34#include < scratchcpp/field.h>
5+ #include < scratchcpp/sprite.h>
46#include < enginemock.h>
57#include < timermock.h>
68#include < clockmock.h>
79
810#include " ../common.h"
911#include " blocks/sensingblocks.h"
12+ #include " blocks/operatorblocks.h"
1013#include " engine/internal/engine.h"
1114
1215using namespace libscratchcpp ;
@@ -34,6 +37,46 @@ class SensingBlocksTest : public testing::Test
3437 return block;
3538 }
3639
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+
3780 std::unique_ptr<IBlockSection> m_section;
3881 EngineMock m_engineMock;
3982 Engine m_engine;
@@ -54,10 +97,14 @@ TEST_F(SensingBlocksTest, CategoryVisible)
5497TEST_F (SensingBlocksTest, RegisterBlocks)
5598{
5699 // 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));
61108
62109 // Fields
63110 EXPECT_CALL (m_engineMock, addField (m_section.get (), " CURRENTMENU" , SensingBlocks::CURRENTMENU));
@@ -74,6 +121,141 @@ TEST_F(SensingBlocksTest, RegisterBlocks)
74121 m_section->registerBlocks (&m_engineMock);
75122}
76123
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+
77259TEST_F (SensingBlocksTest, Timer)
78260{
79261 Compiler compiler (&m_engineMock);
0 commit comments