diff --git a/include/scratchcpp/dev/executablecode.h b/include/scratchcpp/dev/executablecode.h index ba5d03e4..8136e40e 100644 --- a/include/scratchcpp/dev/executablecode.h +++ b/include/scratchcpp/dev/executablecode.h @@ -10,7 +10,7 @@ namespace libscratchcpp { class ExecutionContext; -class Target; +class Thread; /*! \brief The ExecutableCode class represents the code of a compiled Scratch script. */ class LIBSCRATCHCPP_EXPORT ExecutableCode @@ -31,7 +31,7 @@ class LIBSCRATCHCPP_EXPORT ExecutableCode virtual bool isFinished(ExecutionContext *context) const = 0; /*! Creates an execution context for the given Target. */ - virtual std::shared_ptr createExecutionContext(Target *target) const = 0; + virtual std::shared_ptr createExecutionContext(Thread *thread) const = 0; }; } // namespace libscratchcpp diff --git a/include/scratchcpp/dev/executioncontext.h b/include/scratchcpp/dev/executioncontext.h index 4651d079..2f41ea65 100644 --- a/include/scratchcpp/dev/executioncontext.h +++ b/include/scratchcpp/dev/executioncontext.h @@ -8,7 +8,8 @@ namespace libscratchcpp { -class Target; +class Thread; +class IEngine; class Promise; class ExecutionContextPrivate; @@ -16,11 +17,12 @@ class ExecutionContextPrivate; class LIBSCRATCHCPP_EXPORT ExecutionContext { public: - ExecutionContext(Target *target); + ExecutionContext(Thread *thread); ExecutionContext(const ExecutionContext &) = delete; virtual ~ExecutionContext() { } - Target *target() const; + Thread *thread() const; + IEngine *engine() const; std::shared_ptr promise() const; void setPromise(std::shared_ptr promise); diff --git a/include/scratchcpp/dev/test/scriptbuilder.h b/include/scratchcpp/dev/test/scriptbuilder.h index 532f5a67..65a3383c 100644 --- a/include/scratchcpp/dev/test/scriptbuilder.h +++ b/include/scratchcpp/dev/test/scriptbuilder.h @@ -4,8 +4,7 @@ #include -#include "../../global.h" -#include "../../spimpl.h" +#include "../../inputvalue.h" namespace libscratchcpp { @@ -44,6 +43,11 @@ class LIBSCRATCHCPP_EXPORT ScriptBuilder void addDropdownInput(const std::string &name, const std::string &selectedValue); void addDropdownField(const std::string &name, const std::string &selectedValue); + void addEntityInput(const std::string &name, const std::string &entityName, InputValue::Type entityType, std::shared_ptr entity); + void addEntityField(const std::string &name, std::shared_ptr entity); + + std::shared_ptr currentBlock(); + void build(); void run(); @@ -51,6 +55,7 @@ class LIBSCRATCHCPP_EXPORT ScriptBuilder private: void addBlock(std::shared_ptr block); + void build(std::shared_ptr target); spimpl::unique_impl_ptr impl; }; diff --git a/src/dev/blocks/eventblocks.cpp b/src/dev/blocks/eventblocks.cpp index 07477984..4189bcfa 100644 --- a/src/dev/blocks/eventblocks.cpp +++ b/src/dev/blocks/eventblocks.cpp @@ -1,5 +1,14 @@ // SPDX-License-Identifier: Apache-2.0 +#include +#include +#include +#include +#include +#include +#include +#include + #include "eventblocks.h" using namespace libscratchcpp; @@ -16,4 +25,105 @@ std::string libscratchcpp::EventBlocks::description() const void EventBlocks::registerBlocks(IEngine *engine) { + engine->addCompileFunction(this, "event_whentouchingobject", &compileWhenTouchingObject); + engine->addCompileFunction(this, "event_whenflagclicked", &compileWhenFlagClicked); + engine->addCompileFunction(this, "event_whenthisspriteclicked", &compileWhenThisSpriteClicked); + engine->addCompileFunction(this, "event_whenstageclicked", &compileWhenStageClicked); + engine->addCompileFunction(this, "event_whenbroadcastreceived", &compileWhenBroadcastReceived); + engine->addCompileFunction(this, "event_whenbackdropswitchesto", &compileWhenBackdropSwitchesTo); + engine->addCompileFunction(this, "event_whengreaterthan", &compileWhenGreaterThan); + engine->addCompileFunction(this, "event_broadcast", &compileBroadcast); + engine->addCompileFunction(this, "event_broadcastandwait", &compileBroadcastAndWait); + engine->addCompileFunction(this, "event_whenkeypressed", &compileWhenKeyPressed); +} + +CompilerValue *EventBlocks::compileWhenTouchingObject(Compiler *compiler) +{ + compiler->engine()->addWhenTouchingObjectScript(compiler->block()); + return nullptr; +} + +CompilerValue *EventBlocks::compileWhenFlagClicked(Compiler *compiler) +{ + compiler->engine()->addGreenFlagScript(compiler->block()); + return nullptr; +} + +CompilerValue *EventBlocks::compileWhenThisSpriteClicked(Compiler *compiler) +{ + compiler->engine()->addTargetClickScript(compiler->block()); + return nullptr; +} + +CompilerValue *EventBlocks::compileWhenStageClicked(Compiler *compiler) +{ + compiler->engine()->addTargetClickScript(compiler->block()); + return nullptr; +} + +CompilerValue *EventBlocks::compileWhenBroadcastReceived(Compiler *compiler) +{ + auto block = compiler->block(); + Field *field = compiler->field("BROADCAST_OPTION"); + + if (field) { + auto broadcast = std::static_pointer_cast(field->valuePtr()); + compiler->engine()->addBroadcastScript(block, field, broadcast.get()); + } + + return nullptr; +} + +CompilerValue *EventBlocks::compileWhenBackdropSwitchesTo(Compiler *compiler) +{ + auto block = compiler->block(); + Field *field = compiler->field("BACKDROP"); + + if (field) + compiler->engine()->addBackdropChangeScript(block, field); + + return nullptr; +} + +CompilerValue *EventBlocks::compileWhenGreaterThan(Compiler *compiler) +{ + compiler->engine()->addWhenGreaterThanScript(compiler->block()); + return nullptr; +} + +CompilerValue *EventBlocks::compileBroadcast(Compiler *compiler) +{ + auto input = compiler->addInput("BROADCAST_INPUT"); + auto wait = compiler->addConstValue(false); + compiler->addFunctionCallWithCtx("event_broadcast", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::Bool }, { input, wait }); + return nullptr; +} + +CompilerValue *EventBlocks::compileBroadcastAndWait(Compiler *compiler) +{ + auto input = compiler->addInput("BROADCAST_INPUT"); + auto wait = compiler->addConstValue(true); + compiler->addFunctionCallWithCtx("event_broadcast", Compiler::StaticType::Void, { Compiler::StaticType::String, Compiler::StaticType::Bool }, { input, wait }); + return nullptr; +} + +CompilerValue *EventBlocks::compileWhenKeyPressed(Compiler *compiler) +{ + auto block = compiler->block(); + Field *field = compiler->field("KEY_OPTION"); + + if (field) + compiler->engine()->addKeyPressScript(block, field); + + return nullptr; +} + +extern "C" void event_broadcast(ExecutionContext *ctx, const char *name, bool wait) +{ + Thread *thread = ctx->thread(); + IEngine *engine = thread->engine(); + std::vector broadcasts = engine->findBroadcasts(name); + + for (int index : broadcasts) + engine->broadcast(index, thread, wait); } diff --git a/src/dev/blocks/eventblocks.h b/src/dev/blocks/eventblocks.h index c395c3de..34454ebc 100644 --- a/src/dev/blocks/eventblocks.h +++ b/src/dev/blocks/eventblocks.h @@ -14,6 +14,18 @@ class EventBlocks : public IExtension std::string description() const override; void registerBlocks(IEngine *engine) override; + + private: + static CompilerValue *compileWhenTouchingObject(Compiler *compiler); + static CompilerValue *compileWhenFlagClicked(Compiler *compiler); + static CompilerValue *compileWhenThisSpriteClicked(Compiler *compiler); + static CompilerValue *compileWhenStageClicked(Compiler *compiler); + static CompilerValue *compileWhenBroadcastReceived(Compiler *compiler); + static CompilerValue *compileWhenBackdropSwitchesTo(Compiler *compiler); + static CompilerValue *compileWhenGreaterThan(Compiler *compiler); + static CompilerValue *compileBroadcast(Compiler *compiler); + static CompilerValue *compileBroadcastAndWait(Compiler *compiler); + static CompilerValue *compileWhenKeyPressed(Compiler *compiler); }; } // namespace libscratchcpp diff --git a/src/dev/engine/executioncontext.cpp b/src/dev/engine/executioncontext.cpp index 81f8cb06..b0e74926 100644 --- a/src/dev/engine/executioncontext.cpp +++ b/src/dev/engine/executioncontext.cpp @@ -1,21 +1,28 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include "executioncontext_p.h" using namespace libscratchcpp; /*! Constructs ExecutionContext. */ -ExecutionContext::ExecutionContext(Target *target) : - impl(spimpl::make_unique_impl(target)) +ExecutionContext::ExecutionContext(Thread *thread) : + impl(spimpl::make_unique_impl(thread)) { } -/*! Returns the Target of this context. */ -Target *ExecutionContext::target() const +/*! Returns the thread of this context. */ +Thread *ExecutionContext::thread() const { - return impl->target; + return impl->thread; +} + +/*! Returns the engine of the project. */ +IEngine *ExecutionContext::engine() const +{ + return impl->thread->engine(); } /*! Returns the script promise. */ diff --git a/src/dev/engine/executioncontext_p.cpp b/src/dev/engine/executioncontext_p.cpp index f2efa1e1..bb42caac 100644 --- a/src/dev/engine/executioncontext_p.cpp +++ b/src/dev/engine/executioncontext_p.cpp @@ -4,7 +4,7 @@ using namespace libscratchcpp; -ExecutionContextPrivate::ExecutionContextPrivate(Target *target) : - target(target) +ExecutionContextPrivate::ExecutionContextPrivate(Thread *thread) : + thread(thread) { } diff --git a/src/dev/engine/executioncontext_p.h b/src/dev/engine/executioncontext_p.h index 19a22e96..19322c13 100644 --- a/src/dev/engine/executioncontext_p.h +++ b/src/dev/engine/executioncontext_p.h @@ -7,14 +7,15 @@ namespace libscratchcpp { +class Thread; class Target; class Promise; struct ExecutionContextPrivate { - ExecutionContextPrivate(Target *target); + ExecutionContextPrivate(Thread *thread); - Target *target = nullptr; + Thread *thread = nullptr; std::shared_ptr promise; }; diff --git a/src/dev/engine/internal/llvm/llvmexecutablecode.cpp b/src/dev/engine/internal/llvm/llvmexecutablecode.cpp index 944e5d44..20aa9dfc 100644 --- a/src/dev/engine/internal/llvm/llvmexecutablecode.cpp +++ b/src/dev/engine/internal/llvm/llvmexecutablecode.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -64,7 +65,7 @@ void LLVMExecutableCode::run(ExecutionContext *context) ctx->setFinished(done); } else { - Target *target = ctx->target(); + Target *target = ctx->thread()->target(); void *handle = m_mainFunction(context, target, target->variableData(), target->listData()); if (!handle) @@ -95,9 +96,9 @@ bool LLVMExecutableCode::isFinished(ExecutionContext *context) const return getContext(context)->finished(); } -std::shared_ptr LLVMExecutableCode::createExecutionContext(Target *target) const +std::shared_ptr LLVMExecutableCode::createExecutionContext(Thread *thread) const { - return std::make_shared(target); + return std::make_shared(thread); } uint64_t LLVMExecutableCode::lookupFunction(const std::string &name) diff --git a/src/dev/engine/internal/llvm/llvmexecutablecode.h b/src/dev/engine/internal/llvm/llvmexecutablecode.h index ecdc09c3..6d293b3d 100644 --- a/src/dev/engine/internal/llvm/llvmexecutablecode.h +++ b/src/dev/engine/internal/llvm/llvmexecutablecode.h @@ -25,7 +25,7 @@ class LLVMExecutableCode : public ExecutableCode bool isFinished(ExecutionContext *context) const override; - std::shared_ptr createExecutionContext(Target *target) const override; + std::shared_ptr createExecutionContext(Thread *thread) const override; private: uint64_t lookupFunction(const std::string &name); diff --git a/src/dev/engine/internal/llvm/llvmexecutioncontext.cpp b/src/dev/engine/internal/llvm/llvmexecutioncontext.cpp index 5934a90a..97696b34 100644 --- a/src/dev/engine/internal/llvm/llvmexecutioncontext.cpp +++ b/src/dev/engine/internal/llvm/llvmexecutioncontext.cpp @@ -4,8 +4,8 @@ using namespace libscratchcpp; -LLVMExecutionContext::LLVMExecutionContext(Target *target) : - ExecutionContext(target) +LLVMExecutionContext::LLVMExecutionContext(Thread *thread) : + ExecutionContext(thread) { } diff --git a/src/dev/engine/internal/llvm/llvmexecutioncontext.h b/src/dev/engine/internal/llvm/llvmexecutioncontext.h index c6357819..c3c952fc 100644 --- a/src/dev/engine/internal/llvm/llvmexecutioncontext.h +++ b/src/dev/engine/internal/llvm/llvmexecutioncontext.h @@ -10,7 +10,7 @@ namespace libscratchcpp class LLVMExecutionContext : public ExecutionContext { public: - LLVMExecutionContext(Target *target); + LLVMExecutionContext(Thread *thread); void *coroutineHandle() const; void setCoroutineHandle(void *newCoroutineHandle); diff --git a/src/dev/test/scriptbuilder.cpp b/src/dev/test/scriptbuilder.cpp index 8cd1e037..97d3aac2 100644 --- a/src/dev/test/scriptbuilder.cpp +++ b/src/dev/test/scriptbuilder.cpp @@ -3,10 +3,11 @@ #include #include #include +#include #include #include #include -#include +#include #include #include @@ -121,7 +122,7 @@ void ScriptBuilder::addNullObscuredInput(const std::string &name) block->setCompileFunction([](Compiler *compiler) -> CompilerValue * { return compiler->addConstValue(Value()); }); input->setValueBlock(block); impl->inputBlocks.push_back(block); - impl->blocks.back()->addInput(input); + impl->lastBlock->addInput(input); } /*! Adds a dropdown menu input to the current block. */ @@ -130,11 +131,10 @@ void ScriptBuilder::addDropdownInput(const std::string &name, const std::string if (!impl->lastBlock) return; - auto block = impl->blocks.back(); auto input = std::make_shared(name, Input::Type::Shadow); - block->addInput(input); + impl->lastBlock->addInput(input); - auto menu = std::make_shared(std::to_string(impl->blockId++), block->opcode() + "_menu"); + auto menu = std::make_shared(std::to_string(impl->blockId++), impl->lastBlock->opcode() + "_menu"); menu->setShadow(true); impl->inputBlocks.push_back(menu); input->setValueBlock(menu); @@ -150,28 +150,54 @@ void ScriptBuilder::addDropdownField(const std::string &name, const std::string return; auto field = std::make_shared(name, selectedValue); - impl->blocks.back()->addField(field); + impl->lastBlock->addField(field); } -/*! Builds and compiles the script. */ -void ScriptBuilder::build() +/*! Adds an input pointing to an entity (variable, list, broadcast, etc.) */ +void ScriptBuilder::addEntityInput(const std::string &name, const std::string &entityName, InputValue::Type entityType, std::shared_ptr entity) { - if (impl->target->blocks().empty()) { - for (auto block : impl->blocks) - impl->target->addBlock(block); + if (!impl->lastBlock) + return; - for (auto block : impl->inputBlocks) - impl->target->addBlock(block); - } + entity->setId(std::to_string(impl->blockId++)); + auto input = std::make_shared(name, Input::Type::Shadow); + input->setPrimaryValue(entityName); + input->primaryValue()->setValuePtr(entity); + input->primaryValue()->setType(entityType); + impl->lastBlock->addInput(input); +} - std::vector> targets = impl->engine->targets(); +/*! Adds a field pointing to an entity (variable, list, broadcast, etc.) */ +void ScriptBuilder::addEntityField(const std::string &name, std::shared_ptr entity) +{ + if (!impl->lastBlock) + return; - if (std::find(targets.begin(), targets.end(), impl->target) == targets.end()) { - targets.push_back(impl->target); - impl->engine->setTargets({ impl->target }); - } + entity->setId(std::to_string(impl->blockId++)); + auto field = std::make_shared(name, Value(), entity); + impl->lastBlock->addField(field); +} - impl->engine->compile(); +/*! + * Returns the current block (can be used e. g. with a custom Compiler instance).\n + * The script is automatically built to set the compile function of the block. + * \note This method is not intended for building scripts, use build() for that. + */ +std::shared_ptr ScriptBuilder::currentBlock() +{ + if (!impl->lastBlock) + return nullptr; + + if (!impl->lastBlock->compileFunction()) + build(std::make_shared()); + + return impl->lastBlock; +} + +/*! Builds and compiles the script. */ +void ScriptBuilder::build() +{ + build(impl->target); } /*! Runs the built script. */ @@ -196,3 +222,23 @@ void ScriptBuilder::addBlock(std::shared_ptr block) impl->blocks.push_back(block); } + +void ScriptBuilder::build(std::shared_ptr target) +{ + if (target->blocks().empty()) { + for (auto block : impl->blocks) + target->addBlock(block); + + for (auto block : impl->inputBlocks) + target->addBlock(block); + } + + std::vector> targets = impl->engine->targets(); + + if (std::find(targets.begin(), targets.end(), target) == targets.end()) { + targets.push_back(target); + impl->engine->setTargets({ target }); + } + + impl->engine->compile(); +} diff --git a/src/engine/internal/engine.cpp b/src/engine/internal/engine.cpp index 748797ce..1be628aa 100644 --- a/src/engine/internal/engine.cpp +++ b/src/engine/internal/engine.cpp @@ -1526,10 +1526,12 @@ std::shared_ptr Engine::getVariable(const std::string &id, Target *tar int index; // Check stage - index = stage->findVariableById(id); + if (stage) { + index = stage->findVariableById(id); - if (index != -1) - return stage->variableAt(index); + if (index != -1) + return stage->variableAt(index); + } // Check currently compiled target if (target != stage) { @@ -1562,10 +1564,12 @@ std::shared_ptr Engine::getList(const std::string &id, Target *target) int index; // Check stage - index = stage->findListById(id); + if (stage) { + index = stage->findListById(id); - if (index != -1) - return stage->listAt(index); + if (index != -1) + return stage->listAt(index); + } // Check currently compiled target if (target != stage) { diff --git a/src/engine/thread.cpp b/src/engine/thread.cpp index 3c1cbfc5..bf34cdab 100644 --- a/src/engine/thread.cpp +++ b/src/engine/thread.cpp @@ -18,8 +18,12 @@ Thread::Thread(Target *target, IEngine *engine, Script *script) : { impl->vm = std::make_unique(target, engine, script, this); #ifdef USE_LLVM - impl->code = impl->script->code(); - impl->executionContext = impl->code->createExecutionContext(target); + if (impl->script) { + impl->code = impl->script->code(); + + if (impl->code) + impl->executionContext = impl->code->createExecutionContext(this); + } #endif } diff --git a/test/dev/blocks/event_blocks_test.cpp b/test/dev/blocks/event_blocks_test.cpp index ab8370a6..470ccaf0 100644 --- a/test/dev/blocks/event_blocks_test.cpp +++ b/test/dev/blocks/event_blocks_test.cpp @@ -1,15 +1,245 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include "../common.h" #include "dev/blocks/eventblocks.h" using namespace libscratchcpp; +using namespace libscratchcpp::test; + +using ::testing::Return; class EventBlocksTest : public testing::Test { public: - void SetUp() override { m_extension = std::make_unique(); } + void SetUp() override + { + m_extension = std::make_unique(); + m_engine = m_project.engine().get(); + m_extension->registerBlocks(m_engine); + } std::unique_ptr m_extension; + Project m_project; + IEngine *m_engine = nullptr; EngineMock m_engineMock; }; + +// TODO: Add test for when touching object hat predicate + +TEST_F(EventBlocksTest, WhenTouchingObject) +{ + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_whentouchingobject"); + builder.addDropdownInput("TOUCHINGOBJECTMENU", "Sprite1"); + auto block = builder.currentBlock(); + + Compiler compiler(&m_engineMock, target.get()); + EXPECT_CALL(m_engineMock, addWhenTouchingObjectScript(block)); + compiler.compile(block); +} + +TEST_F(EventBlocksTest, WhenFlagClicked) +{ + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_whenflagclicked"); + auto block = builder.currentBlock(); + + Compiler compiler(&m_engineMock, target.get()); + EXPECT_CALL(m_engineMock, addGreenFlagScript(block)); + compiler.compile(block); +} + +TEST_F(EventBlocksTest, WhenThisSpriteClicked) +{ + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_whenthisspriteclicked"); + auto block = builder.currentBlock(); + + Compiler compiler(&m_engineMock, target.get()); + EXPECT_CALL(m_engineMock, addTargetClickScript(block)); + compiler.compile(block); +} + +TEST_F(EventBlocksTest, WhenStageClicked) +{ + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_whenstageclicked"); + auto block = builder.currentBlock(); + + Compiler compiler(&m_engineMock, target.get()); + EXPECT_CALL(m_engineMock, addTargetClickScript(block)); + compiler.compile(block); +} + +TEST_F(EventBlocksTest, WhenBroadcastReceived) +{ + auto broadcast = std::make_shared("", ""); + m_engine->setBroadcasts({ broadcast }); + + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_whenbroadcastreceived"); + builder.addEntityField("BROADCAST_OPTION", broadcast); + auto block = builder.currentBlock(); + + Compiler compiler(&m_engineMock, target.get()); + EXPECT_CALL(m_engineMock, addBroadcastScript(block, block->fieldAt(0).get(), broadcast.get())); + compiler.compile(block); +} + +TEST_F(EventBlocksTest, WhenBackdropSwitchesTo) +{ + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_whenbackdropswitchesto"); + builder.addDropdownField("BACKDROP", "backdrop2"); + auto block = builder.currentBlock(); + + Compiler compiler(&m_engineMock, target.get()); + EXPECT_CALL(m_engineMock, addBackdropChangeScript(block, block->fieldAt(0).get())); + compiler.compile(block); +} + +// TODO: Add test for when greater than hat predicate + +TEST_F(EventBlocksTest, WhenGreaterThan) +{ + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_whengreaterthan"); + builder.addDropdownField("WHENGREATERTHANMENU", "LOUDNESS"); + builder.addValueInput("VALUE", 8.9); + auto block = builder.currentBlock(); + + Compiler compiler(&m_engineMock, target.get()); + EXPECT_CALL(m_engineMock, addWhenGreaterThanScript(block)); + compiler.compile(block); +} + +TEST_F(EventBlocksTest, Broadcast) +{ + auto broadcast = std::make_shared("", "test"); + m_engine->setBroadcasts({ broadcast }); + + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_broadcast"); + builder.addEntityInput("BROADCAST_INPUT", "test", InputValue::Type::Broadcast, broadcast); + auto block1 = builder.currentBlock(); + + builder.addBlock("event_broadcast"); + builder.addNullObscuredInput("BROADCAST_INPUT"); + auto block2 = builder.currentBlock(); + + block1->setNext(nullptr); + block2->setParent(nullptr); + + { + Compiler compiler(&m_engineMock, target.get()); + auto code = compiler.compile(block1); + Script script(target.get(), block1, &m_engineMock); + script.setCode(code); + Thread thread(target.get(), &m_engineMock, &script); + + EXPECT_CALL(m_engineMock, findBroadcasts("test")).WillOnce(Return(std::vector({ 1, 4 }))); + EXPECT_CALL(m_engineMock, broadcast(1, &thread, false)); + EXPECT_CALL(m_engineMock, broadcast(4, &thread, false)); + thread.run(); + } + + { + Compiler compiler(&m_engineMock, target.get()); + auto code = compiler.compile(block2); + Script script(target.get(), block2, &m_engineMock); + script.setCode(code); + Thread thread(target.get(), &m_engineMock, &script); + + EXPECT_CALL(m_engineMock, findBroadcasts("0")).WillOnce(Return(std::vector({ 5, 7, 8 }))); + EXPECT_CALL(m_engineMock, broadcast(5, &thread, false)); + EXPECT_CALL(m_engineMock, broadcast(7, &thread, false)); + EXPECT_CALL(m_engineMock, broadcast(8, &thread, false)); + thread.run(); + } +} + +TEST_F(EventBlocksTest, BroadcastAndWait) +{ + auto broadcast = std::make_shared("", "test"); + m_engine->setBroadcasts({ broadcast }); + + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_broadcastandwait"); + builder.addEntityInput("BROADCAST_INPUT", "test", InputValue::Type::Broadcast, broadcast); + auto block1 = builder.currentBlock(); + + builder.addBlock("event_broadcastandwait"); + builder.addNullObscuredInput("BROADCAST_INPUT"); + auto block2 = builder.currentBlock(); + + block1->setNext(nullptr); + block2->setParent(nullptr); + + { + Compiler compiler(&m_engineMock, target.get()); + auto code = compiler.compile(block1); + Script script(target.get(), block1, &m_engineMock); + script.setCode(code); + Thread thread(target.get(), &m_engineMock, &script); + + EXPECT_CALL(m_engineMock, findBroadcasts("test")).WillOnce(Return(std::vector({ 1, 4 }))); + EXPECT_CALL(m_engineMock, broadcast(1, &thread, true)); + EXPECT_CALL(m_engineMock, broadcast(4, &thread, true)); + thread.run(); + } + + { + Compiler compiler(&m_engineMock, target.get()); + auto code = compiler.compile(block2); + Script script(target.get(), block2, &m_engineMock); + script.setCode(code); + Thread thread(target.get(), &m_engineMock, &script); + + EXPECT_CALL(m_engineMock, findBroadcasts("0")).WillOnce(Return(std::vector({ 5, 7, 8 }))); + EXPECT_CALL(m_engineMock, broadcast(5, &thread, true)); + EXPECT_CALL(m_engineMock, broadcast(7, &thread, true)); + EXPECT_CALL(m_engineMock, broadcast(8, &thread, true)); + thread.run(); + } +} + +TEST_F(EventBlocksTest, WhenKeyPressed) +{ + auto target = std::make_shared(); + ScriptBuilder builder(m_extension.get(), m_engine, target); + + builder.addBlock("event_whenkeypressed"); + builder.addDropdownField("KEY_OPTION", "a"); + auto block = builder.currentBlock(); + + Compiler compiler(&m_engineMock, target.get()); + EXPECT_CALL(m_engineMock, addKeyPressScript(block, block->fieldAt(0).get())); + compiler.compile(block); +} diff --git a/test/dev/executioncontext/CMakeLists.txt b/test/dev/executioncontext/CMakeLists.txt index ef216403..ace0b73c 100644 --- a/test/dev/executioncontext/CMakeLists.txt +++ b/test/dev/executioncontext/CMakeLists.txt @@ -6,7 +6,9 @@ add_executable( target_link_libraries( executioncontext_test GTest::gtest_main + GTest::gmock_main scratchcpp + scratchcpp_mocks ) gtest_discover_tests(executioncontext_test) diff --git a/test/dev/executioncontext/executioncontext_test.cpp b/test/dev/executioncontext/executioncontext_test.cpp index d1145373..38122f6f 100644 --- a/test/dev/executioncontext/executioncontext_test.cpp +++ b/test/dev/executioncontext/executioncontext_test.cpp @@ -1,22 +1,25 @@ #include -#include +#include #include +#include + #include "../../common.h" using namespace libscratchcpp; TEST(ExecutionContextTest, Constructor) { - Target target; - ExecutionContext ctx(&target); - ASSERT_EQ(ctx.target(), &target); + EngineMock engine; + Thread thread(nullptr, &engine, nullptr); + ExecutionContext ctx(&thread); + ASSERT_EQ(ctx.thread(), &thread); + ASSERT_EQ(ctx.engine(), &engine); } TEST(ExecutionContextTest, Promise) { - Target target; - ExecutionContext ctx(&target); + ExecutionContext ctx(nullptr); ASSERT_EQ(ctx.promise(), nullptr); auto promise = std::make_shared(); diff --git a/test/dev/llvm/llvmcodebuilder_test.cpp b/test/dev/llvm/llvmcodebuilder_test.cpp index e624fb9c..a51729d6 100644 --- a/test/dev/llvm/llvmcodebuilder_test.cpp +++ b/test/dev/llvm/llvmcodebuilder_test.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -243,7 +245,10 @@ class LLVMCodeBuilderTest : public testing::Test std::string expected = str + str; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); @@ -269,7 +274,10 @@ class LLVMCodeBuilderTest : public testing::Test std::string expected = str + str; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); @@ -296,7 +304,10 @@ class LLVMCodeBuilderTest : public testing::Test std::string expected = str + str; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); @@ -342,7 +353,10 @@ TEST_F(LLVMCodeBuilderTest, FunctionCalls) { v, v1, v2 }); m_builder->addTargetFunctionCall("test_function_1_arg", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); std::stringstream s; s << ctx.get(); @@ -393,7 +407,10 @@ TEST_F(LLVMCodeBuilderTest, ConstCasting) m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); static const std::string expected = "5.2\n" @@ -479,7 +496,10 @@ TEST_F(LLVMCodeBuilderTest, RawValueCasting) m_builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); static const std::string expected = "5.2\n" @@ -1384,7 +1404,11 @@ TEST_F(LLVMCodeBuilderTest, WriteVariable) m_builder->createVariableWrite(localVar3.get(), v); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_EQ(globalVar1->value(), 5); @@ -1463,7 +1487,11 @@ TEST_F(LLVMCodeBuilderTest, Select) auto code = m_builder->finalize(); testing::internal::CaptureStdout(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } @@ -1519,7 +1547,11 @@ TEST_F(LLVMCodeBuilderTest, ReadVariable) expected += localVar3->value().toString() + '\n'; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -1587,7 +1619,11 @@ TEST_F(LLVMCodeBuilderTest, ClearList) m_builder->createListClear(localList2.get()); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_TRUE(globalList1->empty()); @@ -1634,7 +1670,11 @@ TEST_F(LLVMCodeBuilderTest, RemoveFromList) m_builder->createListRemove(localList.get(), v); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_EQ(globalList->toString(), "13"); @@ -1690,7 +1730,11 @@ TEST_F(LLVMCodeBuilderTest, AppendToList) m_builder->createListAppend(localList.get(), v); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_EQ(globalList->toString(), "1 2 3 1 test"); @@ -1752,7 +1796,11 @@ TEST_F(LLVMCodeBuilderTest, InsertToList) m_builder->createListInsert(localList.get(), v1, v2); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_EQ(globalList->toString(), "1 2 1 test 3"); @@ -1808,7 +1856,11 @@ TEST_F(LLVMCodeBuilderTest, ListReplace) m_builder->createListReplace(localList.get(), v1, v2); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_EQ(globalList->toString(), "1 test 1"); @@ -1854,7 +1906,11 @@ TEST_F(LLVMCodeBuilderTest, GetListContents) "Lorem ipsum dolor sit\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -1923,7 +1979,11 @@ TEST_F(LLVMCodeBuilderTest, GetListItem) "sit\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -1968,7 +2028,11 @@ TEST_F(LLVMCodeBuilderTest, GetListSize) "4\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -2059,7 +2123,11 @@ TEST_F(LLVMCodeBuilderTest, GetListItemIndex) "-1\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -2150,7 +2218,11 @@ TEST_F(LLVMCodeBuilderTest, ListContainsItem) "false\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -2192,7 +2264,10 @@ TEST_F(LLVMCodeBuilderTest, Yield) build(); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); static const std::string expected1 = "no_args\n" @@ -2221,7 +2296,7 @@ TEST_F(LLVMCodeBuilderTest, Yield) createBuilder(true); build(); code = m_builder->finalize(); - ctx = code->createExecutionContext(&m_target); + ctx = code->createExecutionContext(&thread); static const std::string expected = "no_args\n" @@ -2273,7 +2348,11 @@ TEST_F(LLVMCodeBuilderTest, VariablesAfterSuspend) "-4.8\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_FALSE(code->isFinished(ctx.get())); @@ -2361,7 +2440,11 @@ TEST_F(LLVMCodeBuilderTest, ListsAfterSuspend) "hello\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_FALSE(code->isFinished(ctx.get())); @@ -2523,7 +2606,10 @@ TEST_F(LLVMCodeBuilderTest, IfStatement) m_builder->endIf(); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); static const std::string expected = "no_args\n" @@ -2634,7 +2720,11 @@ TEST_F(LLVMCodeBuilderTest, IfStatementVariables) "true\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -2771,7 +2861,11 @@ TEST_F(LLVMCodeBuilderTest, IfStatementLists) "false\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -2842,7 +2936,10 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop) m_builder->endLoop(); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); static const std::string expected = "no_args\n" @@ -2882,7 +2979,7 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop) m_builder->endLoop(); code = m_builder->finalize(); - ctx = code->createExecutionContext(&m_target); + ctx = code->createExecutionContext(&thread); static const std::string expected1 = "no_args\n"; @@ -2908,7 +3005,7 @@ TEST_F(LLVMCodeBuilderTest, RepeatLoop) m_builder->endLoop(); code = m_builder->finalize(); - ctx = code->createExecutionContext(&m_target); + ctx = code->createExecutionContext(&thread); code->run(ctx.get()); ASSERT_TRUE(code->isFinished(ctx.get())); } @@ -2976,7 +3073,10 @@ TEST_F(LLVMCodeBuilderTest, WhileLoop) m_builder->endLoop(); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); static const std::string expected = "1_arg 0\n" @@ -3001,7 +3101,7 @@ TEST_F(LLVMCodeBuilderTest, WhileLoop) m_builder->endLoop(); code = m_builder->finalize(); - ctx = code->createExecutionContext(&m_target); + ctx = code->createExecutionContext(&thread); static const std::string expected1 = "no_args\n"; @@ -3081,7 +3181,10 @@ TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop) m_builder->endLoop(); auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&m_target); + Script script(&m_target, nullptr, nullptr); + script.setCode(code); + Thread thread(&m_target, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); static const std::string expected = "1_arg 0\n" @@ -3106,7 +3209,7 @@ TEST_F(LLVMCodeBuilderTest, RepeatUntilLoop) m_builder->endLoop(); code = m_builder->finalize(); - ctx = code->createExecutionContext(&m_target); + ctx = code->createExecutionContext(&thread); static const std::string expected1 = "no_args\n"; @@ -3244,7 +3347,11 @@ TEST_F(LLVMCodeBuilderTest, LoopVariables) "true\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); @@ -3434,7 +3541,11 @@ TEST_F(LLVMCodeBuilderTest, LoopLists) "false\n"; auto code = m_builder->finalize(); - auto ctx = code->createExecutionContext(&sprite); + Script script(&sprite, nullptr, nullptr); + script.setCode(code); + ; + Thread thread(&sprite, nullptr, &script); + auto ctx = code->createExecutionContext(&thread); testing::internal::CaptureStdout(); code->run(ctx.get()); ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); diff --git a/test/dev/llvm/llvmexecutablecode_test.cpp b/test/dev/llvm/llvmexecutablecode_test.cpp index def0f985..581a3a58 100644 --- a/test/dev/llvm/llvmexecutablecode_test.cpp +++ b/test/dev/llvm/llvmexecutablecode_test.cpp @@ -3,11 +3,13 @@ #include #include #include +#include +#include #include #include #include #include -#include +#include #include "testmock.h" #include "testfunctions.h" @@ -23,6 +25,8 @@ class LLVMExecutableCodeTest : public testing::Test m_builder = std::make_unique>(m_ctx); test_function(nullptr, nullptr, nullptr, nullptr, nullptr); // force dependency + m_script = std::make_unique