33#include < scratchcpp/input.h>
44#include < scratchcpp/field.h>
55#include < scratchcpp/variable.h>
6+ #include < scratchcpp/sprite.h>
67#include < enginemock.h>
78
89#include " ../common.h"
910#include " blocks/controlblocks.h"
11+ #include " blocks/operatorblocks.h"
1012#include " engine/internal/engine.h"
1113
1214using namespace libscratchcpp ;
@@ -25,6 +27,23 @@ class ControlBlocksTest : public testing::Test
2527 // For any control block
2628 std::shared_ptr<Block> createControlBlock (const std::string &id, const std::string &opcode) const { return std::make_shared<Block>(id, opcode); }
2729
30+ // For control_create_clone_of
31+ std::shared_ptr<Block> createCloneBlock (const std::string &id, const std::string &spriteName, std::shared_ptr<Block> valueBlock = nullptr )
32+ {
33+ auto block = createControlBlock (id, " control_create_clone_of" );
34+
35+ if (valueBlock)
36+ addObscuredInput (block, " CLONE_OPTION" , ControlBlocks::CLONE_OPTION, valueBlock);
37+ else {
38+ auto input = addNullInput (block, " CLONE_OPTION" , ControlBlocks::CLONE_OPTION);
39+ auto menu = createControlBlock (id + " _menu" , " control_create_clone_of_menu" );
40+ input->setValueBlock (menu);
41+ addDropdownField (menu, " CLONE_OPTION" , static_cast <ControlBlocks::Fields>(-1 ), spriteName, static_cast <ControlBlocks::FieldValues>(-1 ));
42+ }
43+
44+ return block;
45+ }
46+
2847 void addSubstackInput (std::shared_ptr<Block> block, const std::string &name, ControlBlocks::Inputs id, std::shared_ptr<Block> valueBlock) const
2948 {
3049 auto input = std::make_shared<Input>(name, Input::Type::NoShadow);
@@ -52,12 +71,14 @@ class ControlBlocksTest : public testing::Test
5271 block->updateInputMap ();
5372 }
5473
55- void addNullInput (std::shared_ptr<Block> block, const std::string &name, ControlBlocks::Inputs id) const
74+ std::shared_ptr<Input> addNullInput (std::shared_ptr<Block> block, const std::string &name, ControlBlocks::Inputs id) const
5675 {
5776 auto input = std::make_shared<Input>(name, Input::Type::Shadow);
5877 input->setInputId (id);
5978 block->addInput (input);
6079 block->updateInputMap ();
80+
81+ return input;
6182 }
6283
6384 void addDropdownField (std::shared_ptr<Block> block, const std::string &name, ControlBlocks::Fields id, const std::string &value, ControlBlocks::FieldValues valueId) const
@@ -130,6 +151,7 @@ TEST_F(ControlBlocksTest, RegisterBlocks)
130151 EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " control_stop" , &ControlBlocks::compileStop)).Times (1 );
131152 EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " control_wait" , &ControlBlocks::compileWait)).Times (1 );
132153 EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " control_wait_until" , &ControlBlocks::compileWaitUntil)).Times (1 );
154+ EXPECT_CALL (m_engineMock, addCompileFunction (m_section.get (), " control_create_clone_of" , &ControlBlocks::compileCreateClone)).Times (1 );
133155
134156 // Inputs
135157 EXPECT_CALL (m_engineMock, addInput (m_section.get (), " SUBSTACK" , ControlBlocks::SUBSTACK));
@@ -138,6 +160,7 @@ TEST_F(ControlBlocksTest, RegisterBlocks)
138160 EXPECT_CALL (m_engineMock, addInput (m_section.get (), " CONDITION" , ControlBlocks::CONDITION));
139161 EXPECT_CALL (m_engineMock, addInput (m_section.get (), " DURATION" , ControlBlocks::DURATION));
140162 EXPECT_CALL (m_engineMock, addInput (m_section.get (), " VALUE" , ControlBlocks::VALUE));
163+ EXPECT_CALL (m_engineMock, addInput (m_section.get (), " CLONE_OPTION" , ControlBlocks::CLONE_OPTION));
141164
142165 // Fields
143166 EXPECT_CALL (m_engineMock, addField (m_section.get (), " STOP_OPTION" , ControlBlocks::STOP_OPTION));
@@ -810,3 +833,96 @@ TEST_F(ControlBlocksTest, WaitUntilImpl)
810833 ASSERT_EQ (vm.registerCount (), 0 );
811834 ASSERT_TRUE (vm.atEnd ());
812835}
836+
837+ TEST_F (ControlBlocksTest, CreateCloneOf)
838+ {
839+ Compiler compiler (&m_engineMock);
840+
841+ // create clone of [Sprite1]
842+ auto block1 = createCloneBlock (" a" , " Sprite1" );
843+
844+ // create clone of [myself]
845+ auto block2 = createCloneBlock (" b" , " _myself_" );
846+
847+ // create clone of (join "" "")
848+ auto joinBlock = std::make_shared<Block>(" d" , " operator_join" );
849+ joinBlock->setCompileFunction (&OperatorBlocks::compileJoin);
850+ auto block3 = createCloneBlock (" c" , " " , joinBlock);
851+
852+ EXPECT_CALL (m_engineMock, findTarget (" Sprite1" )).WillOnce (Return (4 ));
853+ EXPECT_CALL (m_engineMock, functionIndex (&ControlBlocks::createCloneByIndex)).WillOnce (Return (0 ));
854+ EXPECT_CALL (m_engineMock, functionIndex (&ControlBlocks::createCloneOfMyself)).WillOnce (Return (1 ));
855+ EXPECT_CALL (m_engineMock, functionIndex (&ControlBlocks::createClone)).WillOnce (Return (2 ));
856+
857+ compiler.init ();
858+ compiler.setBlock (block1);
859+ ControlBlocks::compileCreateClone (&compiler);
860+ compiler.setBlock (block2);
861+ ControlBlocks::compileCreateClone (&compiler);
862+ compiler.setBlock (block3);
863+ ControlBlocks::compileCreateClone (&compiler);
864+ compiler.end ();
865+
866+ ASSERT_EQ (
867+ compiler.bytecode (),
868+ 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 }));
869+ ASSERT_EQ (compiler.constValues ().size (), 1 );
870+ ASSERT_EQ (compiler.constValues ()[0 ].toDouble (), 4 );
871+ ASSERT_TRUE (compiler.variables ().empty ());
872+ ASSERT_TRUE (compiler.lists ().empty ());
873+ }
874+
875+ TEST_F (ControlBlocksTest, CreateCloneOfImpl)
876+ {
877+ static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0 , vm::OP_EXEC, 0 , vm::OP_HALT };
878+ static unsigned int bytecode2[] = { vm::OP_START, vm::OP_EXEC, 1 , vm::OP_HALT };
879+ static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 1 , vm::OP_EXEC, 2 , vm::OP_HALT };
880+ static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 2 , vm::OP_EXEC, 2 , vm::OP_HALT };
881+ static BlockFunc functions[] = { &ControlBlocks::createCloneByIndex, &ControlBlocks::createCloneOfMyself, &ControlBlocks::createClone };
882+ static Value constValues[] = { 4 , " Sprite1" , " _myself_" };
883+
884+ Sprite sprite;
885+ sprite.setEngine (&m_engineMock);
886+
887+ VirtualMachine vm (&sprite, &m_engineMock, nullptr );
888+ vm.setFunctions (functions);
889+ vm.setConstValues (constValues);
890+
891+ EXPECT_CALL (m_engineMock, targetAt (4 )).WillOnce (Return (&sprite));
892+ EXPECT_CALL (m_engineMock, initClone).Times (1 );
893+
894+ vm.setBytecode (bytecode1);
895+ vm.run ();
896+
897+ ASSERT_EQ (vm.registerCount (), 0 );
898+ ASSERT_EQ (sprite.allChildren ().size (), 1 );
899+
900+ EXPECT_CALL (m_engineMock, initClone).Times (1 );
901+
902+ vm.setBytecode (bytecode2);
903+ vm.run ();
904+
905+ ASSERT_EQ (vm.registerCount (), 0 );
906+ ASSERT_EQ (sprite.allChildren ().size (), 2 );
907+ ASSERT_EQ (sprite.children (), sprite.allChildren ());
908+
909+ EXPECT_CALL (m_engineMock, findTarget).WillOnce (Return (4 ));
910+ EXPECT_CALL (m_engineMock, targetAt (4 )).WillOnce (Return (&sprite));
911+ EXPECT_CALL (m_engineMock, initClone).Times (1 );
912+
913+ vm.setBytecode (bytecode3);
914+ vm.run ();
915+
916+ ASSERT_EQ (vm.registerCount (), 0 );
917+ ASSERT_EQ (sprite.allChildren ().size (), 3 );
918+ ASSERT_EQ (sprite.children (), sprite.allChildren ());
919+
920+ EXPECT_CALL (m_engineMock, initClone).Times (1 );
921+
922+ vm.setBytecode (bytecode4);
923+ vm.run ();
924+
925+ ASSERT_EQ (vm.registerCount (), 0 );
926+ ASSERT_EQ (sprite.allChildren ().size (), 4 );
927+ ASSERT_EQ (sprite.children (), sprite.allChildren ());
928+ }
0 commit comments