1818using namespace libscratchcpp ;
1919
2020using ::testing::Return;
21+ using ::testing::ReturnRef;
22+ using ::testing::SaveArg;
23+ using ::testing::_;
2124
2225class SensingBlocksTest : public testing ::Test
2326{
@@ -92,6 +95,19 @@ class SensingBlocksTest : public testing::Test
9295 ClockMock m_clockMock;
9396};
9497
98+ struct QuestionSpy
99+ {
100+ MOCK_METHOD (void , asked, (const std::string &), ());
101+ };
102+
103+ template <typename T, typename ... U>
104+ size_t getAddress (std::function<T(U...)> f)
105+ {
106+ typedef T (fnType)(U...);
107+ fnType **fnPointer = f.template target <fnType *>();
108+ return (size_t )*fnPointer;
109+ }
110+
95111TEST_F (SensingBlocksTest, Name)
96112{
97113 ASSERT_EQ (m_section->name (), " Sensing" );
@@ -106,6 +122,7 @@ TEST_F(SensingBlocksTest, RegisterBlocks)
106122{
107123 // Blocks
108124 EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_distanceto" , &SensingBlocks::compileDistanceTo));
125+ EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_askandwait" , &SensingBlocks::compileAskAndWait));
109126 EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_keypressed" , &SensingBlocks::compileKeyPressed));
110127 EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_mousedown" , &SensingBlocks::compileMouseDown));
111128 EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " sensing_mousex" , &SensingBlocks::compileMouseX));
@@ -156,7 +173,14 @@ TEST_F(SensingBlocksTest, RegisterBlocks)
156173 EXPECT_CALL (m_engineMock, addFieldValue (m_section.get (), " backdrop #" , SensingBlocks::BackdropNumber));
157174 EXPECT_CALL (m_engineMock, addFieldValue (m_section.get (), " backdrop name" , SensingBlocks::BackdropName));
158175
176+ // Callbacks
177+ std::function<void (const std::string &)> questionAnsweredRef = &SensingBlocks::onAnswer;
178+ std::function<void (const std::string &)> questionAnswered;
179+ EXPECT_CALL (m_engineMock, setQuestionAnswered (_)).WillOnce (SaveArg<0 >(&questionAnswered));
180+
159181 m_section->registerBlocks (&m_engineMock);
182+ ASSERT_TRUE (questionAnswered);
183+ ASSERT_EQ (getAddress (questionAnsweredRef), getAddress (questionAnswered));
160184}
161185
162186TEST_F (SensingBlocksTest, DistanceTo)
@@ -290,6 +314,122 @@ TEST_F(SensingBlocksTest, DistanceToImpl)
290314 ASSERT_EQ (std::round (vm.getInput (0 , 1 )->toDouble () * 10000 ) / 10000 , 261.0096 );
291315}
292316
317+ TEST_F (SensingBlocksTest, AskAndWait)
318+ {
319+ Compiler compiler (&m_engineMock);
320+
321+ // ask "test" and wait
322+ auto block1 = std::make_shared<Block>(" a" , " sensing_askandwait" );
323+ addDropdownInput (block1, " QUESTION" , SensingBlocks::QUESTION, " test" );
324+
325+ // ask (null block) and wait
326+ auto block2 = std::make_shared<Block>(" b" , " sensing_askandwait" );
327+ addDropdownInput (block2, " QUESTION" , SensingBlocks::QUESTION, " " , createNullBlock (" c" ));
328+
329+ compiler.init ();
330+
331+ EXPECT_CALL (m_engineMock, functionIndex (&SensingBlocks::askAndWait)).WillOnce (Return (0 ));
332+ compiler.setBlock (block1);
333+ SensingBlocks::compileAskAndWait (&compiler);
334+
335+ EXPECT_CALL (m_engineMock, functionIndex (&SensingBlocks::askAndWait)).WillOnce (Return (0 ));
336+ compiler.setBlock (block2);
337+ SensingBlocks::compileAskAndWait (&compiler);
338+
339+ compiler.end ();
340+
341+ ASSERT_EQ (compiler.bytecode (), std::vector<unsigned int >({ vm::OP_START, vm::OP_CONST, 0 , vm::OP_EXEC, 0 , vm::OP_NULL, vm::OP_EXEC, 0 , vm::OP_HALT }));
342+ ASSERT_EQ (compiler.constValues ().size (), 1 );
343+ ASSERT_EQ (compiler.constValues ()[0 ].toString (), " test" );
344+ }
345+
346+ TEST_F (SensingBlocksTest, AskAndWaitImpl)
347+ {
348+ static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0 , vm::OP_EXEC, 0 , vm::OP_HALT };
349+ static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1 , vm::OP_EXEC, 0 , vm::OP_HALT };
350+ static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2 , vm::OP_EXEC, 0 , vm::OP_HALT };
351+ static BlockFunc functions[] = { &SensingBlocks::askAndWait };
352+ static Value constValues[] = { " test1" , " test2" , " test3" };
353+
354+ Sprite sprite;
355+ sprite.setBubbleType (Target::BubbleType::Think);
356+ Stage stage;
357+ QuestionSpy spy;
358+ std::function<void (const std::string &)> asked = std::bind (&QuestionSpy::asked, &spy, std::placeholders::_1);
359+
360+ VirtualMachine vm1 (&sprite, &m_engineMock, nullptr );
361+ vm1.setFunctions (functions);
362+ vm1.setConstValues (constValues);
363+
364+ // Ask 3 questions (2 where the sprite is visible and 1 where it's invisible)
365+ EXPECT_CALL (m_engineMock, questionAsked ()).WillOnce (ReturnRef (asked));
366+ EXPECT_CALL (spy, asked (" " ));
367+ sprite.setVisible (true );
368+ vm1.setBytecode (bytecode1);
369+ vm1.run ();
370+
371+ ASSERT_EQ (vm1.registerCount (), 0 );
372+ ASSERT_FALSE (vm1.atEnd ());
373+ ASSERT_EQ (sprite.bubbleType (), Target::BubbleType::Say);
374+ ASSERT_EQ (sprite.bubbleText (), " test1" );
375+
376+ EXPECT_CALL (m_engineMock, questionAsked).Times (0 );
377+ vm1.reset ();
378+ vm1.setBytecode (bytecode2);
379+ vm1.run ();
380+
381+ ASSERT_EQ (vm1.registerCount (), 0 );
382+ ASSERT_FALSE (vm1.atEnd ());
383+ ASSERT_EQ (sprite.bubbleType (), Target::BubbleType::Say);
384+ ASSERT_EQ (sprite.bubbleText (), " test1" );
385+
386+ EXPECT_CALL (m_engineMock, questionAsked).Times (0 );
387+ sprite.setVisible (false );
388+ vm1.reset ();
389+ vm1.setBytecode (bytecode3);
390+ vm1.run ();
391+ sprite.setVisible (true );
392+
393+ ASSERT_EQ (vm1.registerCount (), 0 );
394+ ASSERT_FALSE (vm1.atEnd ());
395+ ASSERT_EQ (sprite.bubbleType (), Target::BubbleType::Say);
396+ ASSERT_EQ (sprite.bubbleText (), " test1" );
397+
398+ // Ask a question from the stage
399+ VirtualMachine vm2 (&stage, &m_engineMock, nullptr );
400+ vm2.setFunctions (functions);
401+ vm2.setConstValues (constValues);
402+
403+ EXPECT_CALL (m_engineMock, questionAsked).Times (0 );
404+ vm2.setBytecode (bytecode2);
405+ vm2.run ();
406+ ASSERT_EQ (vm2.registerCount (), 0 );
407+ ASSERT_FALSE (vm2.atEnd ());
408+ ASSERT_EQ (sprite.bubbleType (), Target::BubbleType::Say);
409+ ASSERT_EQ (sprite.bubbleText (), " test1" );
410+
411+ // Answer the questions
412+ EXPECT_CALL (m_engineMock, questionAsked ()).WillOnce (ReturnRef (asked));
413+ EXPECT_CALL (spy, asked (" " ));
414+ SensingBlocks::onAnswer (" hi" );
415+ ASSERT_EQ (sprite.bubbleType (), Target::BubbleType::Say);
416+ ASSERT_EQ (sprite.bubbleText (), " test2" );
417+
418+ EXPECT_CALL (m_engineMock, questionAsked ()).WillOnce (ReturnRef (asked));
419+ EXPECT_CALL (spy, asked (" test3" ));
420+ SensingBlocks::onAnswer (" hello" );
421+ ASSERT_TRUE (sprite.bubbleText ().empty ());
422+
423+ EXPECT_CALL (m_engineMock, questionAsked ()).WillOnce (ReturnRef (asked));
424+ EXPECT_CALL (spy, asked (" test2" ));
425+ SensingBlocks::onAnswer (" world" );
426+ ASSERT_TRUE (sprite.bubbleText ().empty ());
427+ ASSERT_TRUE (stage.bubbleText ().empty ());
428+
429+ EXPECT_CALL (m_engineMock, questionAsked).Times (0 );
430+ SensingBlocks::onAnswer (" test" );
431+ }
432+
293433TEST_F (SensingBlocksTest, KeyPressed)
294434{
295435 Compiler compiler (&m_engineMock);
0 commit comments