| /* |
| * Copyright 2025 Rive |
| */ |
| |
| #include "rive/animation/state_machine_input_instance.hpp" |
| #include "rive/animation/state_machine_instance.hpp" |
| #include "rive/command_queue.hpp" |
| #include "rive/command_server.hpp" |
| #include "rive/file.hpp" |
| #include "common/render_context_null.hpp" |
| #include <fstream> |
| |
| namespace rive |
| { |
| bool operator==(const ViewModelEnum& left, const ViewModelEnum& right) |
| { |
| if (left.name != right.name || |
| left.enumerants.size() != right.enumerants.size()) |
| return false; |
| |
| for (int i = 0; i < left.enumerants.size(); ++i) |
| { |
| if (left.enumerants[i] != right.enumerants[i]) |
| return false; |
| } |
| |
| return true; |
| } |
| bool operator!=(const ViewModelEnum& left, const ViewModelEnum& right) |
| { |
| return !(left == right); |
| } |
| bool operator==(const PropertyData& l, const PropertyData& r) |
| { |
| return l.name == r.name && l.type == r.type; |
| } |
| bool operator==(const CommandQueue::FileListener::ViewModelPropertyData& l, |
| const CommandQueue::FileListener::ViewModelPropertyData& r) |
| { |
| return l.name == r.name && l.type == r.type && l.metaData == r.metaData; |
| } |
| bool operator!=(const CommandQueue::FileListener::ViewModelPropertyData& l, |
| const CommandQueue::FileListener::ViewModelPropertyData& r) |
| { |
| return !(l == r); |
| } |
| } // namespace rive |
| |
| template <typename t, size_t arraySize> |
| bool operator==(const std::vector<t>& left, |
| const std::array<t, arraySize>& right) |
| { |
| if (left.size() != arraySize) |
| return false; |
| |
| for (int i = 0; i < left.size(); ++i) |
| { |
| if (left[i] != right[i]) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| template <typename t> |
| bool operator==(const std::vector<t>& left, const std::vector<t>& right) |
| { |
| if (left.size() != right.size()) |
| return false; |
| |
| for (int i = 0; i < left.size(); ++i) |
| { |
| if (left[i] != right[i]) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| #include "catch.hpp" |
| |
| using namespace rive; |
| |
| static void server_thread(rcp<CommandQueue> commandQueue) |
| { |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| CommandServer server(std::move(commandQueue), nullContext.get()); |
| server.serveUntilDisconnect(); |
| } |
| |
| static void wait_for_server(CommandQueue* commandQueue) |
| { |
| std::mutex mutex; |
| std::condition_variable cv; |
| bool complete = false; |
| std::unique_lock<std::mutex> lock(mutex); |
| commandQueue->runOnce([&](CommandServer*) { |
| std::unique_lock<std::mutex> serverLock(mutex); |
| complete = true; |
| cv.notify_one(); |
| }); |
| while (!complete) |
| cv.wait(lock); |
| } |
| |
| class TestPODStream : public RefCnt<TestPODStream> |
| { |
| public: |
| static constexpr int MAG_NUMBER = 0x99; |
| int m_number = MAG_NUMBER; |
| }; |
| |
| TEST_CASE("POD Stream RCP", "[PODStream]") |
| { |
| PODStream stream; |
| rcp<TestPODStream> t1 = make_rcp<TestPODStream>(); |
| TestPODStream* orig = t1.get(); |
| stream << t1; |
| |
| CHECK(t1.get() != nullptr); |
| CHECK(t1.get() == orig); |
| |
| rcp<TestPODStream> t2; |
| stream >> t2; |
| CHECK(t2.get() == orig); |
| CHECK(t2->m_number == TestPODStream::MAG_NUMBER); |
| |
| rcp<TestPODStream> t3; |
| stream << t3; |
| rcp<TestPODStream> t4; |
| stream >> t4; |
| CHECK(t4.get() == nullptr); |
| } |
| |
| TEST_CASE("artboard management", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/two_artboards.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| commandQueue->runOnce([fileHandle](CommandServer* server) { |
| CHECK(fileHandle != RIVE_NULL_HANDLE); |
| CHECK(server->getFile(fileHandle) != nullptr); |
| }); |
| |
| ArtboardHandle artboardHandle1 = |
| commandQueue->instantiateArtboardNamed(fileHandle, "One"); |
| ArtboardHandle artboardHandle2 = |
| commandQueue->instantiateArtboardNamed(fileHandle, "Two"); |
| ArtboardHandle artboardHandle3 = |
| commandQueue->instantiateArtboardNamed(fileHandle, "Three"); |
| commandQueue->runOnce([artboardHandle1, artboardHandle2, artboardHandle3]( |
| CommandServer* server) { |
| CHECK(artboardHandle1 != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(artboardHandle1) != nullptr); |
| CHECK(artboardHandle2 != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(artboardHandle2) != nullptr); |
| // An artboard named "Three" doesn't exist. |
| CHECK(artboardHandle3 != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(artboardHandle3) == nullptr); |
| }); |
| |
| // Deleting an invalid handle has no effect. |
| commandQueue->deleteArtboard(artboardHandle3); |
| commandQueue->deleteArtboard(artboardHandle2); |
| commandQueue->runOnce([artboardHandle1, artboardHandle2, artboardHandle3]( |
| CommandServer* server) { |
| CHECK(server->getArtboardInstance(artboardHandle1) != nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle2) == nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle3) == nullptr); |
| }); |
| |
| // Deleting the file first should now delete the artboard as well. |
| commandQueue->deleteFile(fileHandle); |
| commandQueue->runOnce([fileHandle](CommandServer* server) { |
| CHECK(server->getFile(fileHandle) == nullptr); |
| }); |
| |
| commandQueue->deleteArtboard(artboardHandle1); |
| commandQueue->runOnce([artboardHandle1](CommandServer* server) { |
| CHECK(server->getArtboardInstance(artboardHandle1) == nullptr); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("state machine management", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/multiple_state_machines.riv", |
| std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| ArtboardHandle artboardHandle = |
| commandQueue->instantiateDefaultArtboard(fileHandle); |
| commandQueue->runOnce([fileHandle, artboardHandle](CommandServer* server) { |
| CHECK(fileHandle != RIVE_NULL_HANDLE); |
| CHECK(server->getFile(fileHandle) != nullptr); |
| CHECK(artboardHandle != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(artboardHandle) != nullptr); |
| }); |
| |
| StateMachineHandle sm1 = |
| commandQueue->instantiateStateMachineNamed(artboardHandle, "one"); |
| StateMachineHandle sm2 = |
| commandQueue->instantiateStateMachineNamed(artboardHandle, "two"); |
| StateMachineHandle sm3 = |
| commandQueue->instantiateStateMachineNamed(artboardHandle, "blahblah"); |
| commandQueue->runOnce([sm1, sm2, sm3](CommandServer* server) { |
| CHECK(sm1 != RIVE_NULL_HANDLE); |
| CHECK(server->getStateMachineInstance(sm1) != nullptr); |
| CHECK(sm2 != RIVE_NULL_HANDLE); |
| CHECK(server->getStateMachineInstance(sm2) != nullptr); |
| // A state machine named "blahblah" doesn't exist. |
| CHECK(sm3 != RIVE_NULL_HANDLE); |
| CHECK(server->getStateMachineInstance(sm3) == nullptr); |
| }); |
| |
| commandQueue->deleteFile(fileHandle); |
| commandQueue->deleteArtboard(artboardHandle); |
| commandQueue->deleteStateMachine(sm1); |
| commandQueue->runOnce( |
| [fileHandle, artboardHandle, sm1, sm2](CommandServer* server) { |
| CHECK(server->getFile(fileHandle) == nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle) == nullptr); |
| CHECK(server->getStateMachineInstance(sm1) == nullptr); |
| // Because of the dependencies, this should delete the statemachine |
| // as well. |
| CHECK(server->getStateMachineInstance(sm2) == nullptr); |
| }); |
| |
| commandQueue->deleteStateMachine(sm2); |
| commandQueue->runOnce([sm2](CommandServer* server) { |
| CHECK(server->getStateMachineInstance(sm2) == nullptr); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("default artboard & state machine", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| ArtboardHandle artboardHandle = |
| commandQueue->instantiateDefaultArtboard(fileHandle); |
| StateMachineHandle smHandle = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle); |
| commandQueue->runOnce([artboardHandle, smHandle](CommandServer* server) { |
| rive::ArtboardInstance* artboard = |
| server->getArtboardInstance(artboardHandle); |
| REQUIRE(artboard != nullptr); |
| CHECK(artboard->name() == "New Artboard"); |
| rive::StateMachineInstance* sm = |
| server->getStateMachineInstance(smHandle); |
| REQUIRE(sm != nullptr); |
| CHECK(sm->name() == "State Machine 1"); |
| }); |
| |
| // Using an empty string is the same as requesting the default. |
| ArtboardHandle artboardHandle2 = |
| commandQueue->instantiateArtboardNamed(fileHandle, ""); |
| StateMachineHandle smHandle2 = |
| commandQueue->instantiateStateMachineNamed(artboardHandle2, ""); |
| commandQueue->runOnce([artboardHandle2, smHandle2](CommandServer* server) { |
| rive::ArtboardInstance* artboard = |
| server->getArtboardInstance(artboardHandle2); |
| REQUIRE(artboard != nullptr); |
| CHECK(artboard->name() == "New Artboard"); |
| rive::StateMachineInstance* sm = |
| server->getStateMachineInstance(smHandle2); |
| REQUIRE(sm != nullptr); |
| CHECK(sm->name() == "State Machine 1"); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("invalid handles", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| FileHandle goodFile = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| FileHandle badFile = |
| commandQueue->loadFile(std::vector<uint8_t>(100 * 1024, 0)); |
| commandQueue->runOnce([goodFile, badFile](CommandServer* server) { |
| CHECK(goodFile != RIVE_NULL_HANDLE); |
| CHECK(server->getFile(goodFile) != nullptr); |
| CHECK(badFile != RIVE_NULL_HANDLE); |
| CHECK(server->getFile(badFile) == nullptr); |
| }); |
| |
| ArtboardHandle goodArtboard = |
| commandQueue->instantiateArtboardNamed(goodFile, "New Artboard"); |
| ArtboardHandle badArtboard1 = |
| commandQueue->instantiateDefaultArtboard(badFile); |
| ArtboardHandle badArtboard2 = |
| commandQueue->instantiateArtboardNamed(badFile, "New Artboard"); |
| ArtboardHandle badArtboard3 = |
| commandQueue->instantiateArtboardNamed(goodFile, "blahblahblah"); |
| commandQueue->runOnce( |
| [goodArtboard, badArtboard1, badArtboard2, badArtboard3]( |
| CommandServer* server) { |
| CHECK(goodArtboard != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(goodArtboard) != nullptr); |
| CHECK(badArtboard1 != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(badArtboard1) == nullptr); |
| CHECK(badArtboard2 != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(badArtboard2) == nullptr); |
| CHECK(badArtboard3 != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(badArtboard3) == nullptr); |
| }); |
| |
| StateMachineHandle goodSM = |
| commandQueue->instantiateStateMachineNamed(goodArtboard, |
| "State Machine 1"); |
| StateMachineHandle badSM1 = |
| commandQueue->instantiateStateMachineNamed(badArtboard2, |
| "State Machine 1"); |
| StateMachineHandle badSM2 = |
| commandQueue->instantiateStateMachineNamed(goodArtboard, |
| "blahblahblah"); |
| StateMachineHandle badSM3 = |
| commandQueue->instantiateDefaultStateMachine(badArtboard3); |
| commandQueue->runOnce( |
| [goodSM, badSM1, badSM2, badSM3](CommandServer* server) { |
| CHECK(goodSM != RIVE_NULL_HANDLE); |
| CHECK(server->getStateMachineInstance(goodSM) != nullptr); |
| CHECK(badSM1 != RIVE_NULL_HANDLE); |
| CHECK(server->getStateMachineInstance(badSM1) == nullptr); |
| CHECK(badSM2 != RIVE_NULL_HANDLE); |
| CHECK(server->getStateMachineInstance(badSM2) == nullptr); |
| CHECK(badSM3 != RIVE_NULL_HANDLE); |
| CHECK(server->getStateMachineInstance(badSM3) == nullptr); |
| }); |
| |
| commandQueue->deleteStateMachine(badSM3); |
| commandQueue->deleteStateMachine(badSM2); |
| commandQueue->deleteStateMachine(badSM1); |
| commandQueue->deleteArtboard(badArtboard3); |
| commandQueue->deleteArtboard(badArtboard2); |
| commandQueue->deleteArtboard(badArtboard1); |
| commandQueue->deleteFile(badFile); |
| commandQueue->runOnce( |
| [goodFile, goodArtboard, goodSM](CommandServer* server) { |
| CHECK(server->getFile(goodFile) != nullptr); |
| CHECK(server->getArtboardInstance(goodArtboard) != nullptr); |
| CHECK(server->getStateMachineInstance(goodSM) != nullptr); |
| }); |
| |
| commandQueue->deleteStateMachine(goodSM); |
| commandQueue->deleteArtboard(goodArtboard); |
| commandQueue->deleteFile(goodFile); |
| commandQueue->runOnce( |
| [goodFile, goodArtboard, goodSM](CommandServer* server) { |
| CHECK(server->getFile(goodFile) == nullptr); |
| CHECK(server->getArtboardInstance(goodArtboard) == nullptr); |
| CHECK(server->getStateMachineInstance(goodSM) == nullptr); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("draw loops", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::atomic_uint64_t frameNumber1 = 0, frameNumber2 = 0; |
| std::atomic_uint64_t lastFrameNumber1, lastFrameNumber2; |
| auto drawLoop1 = [&frameNumber1](DrawKey, CommandServer*) { |
| ++frameNumber1; |
| }; |
| auto drawLoop2 = [&frameNumber2](DrawKey, CommandServer*) { |
| ++frameNumber2; |
| }; |
| DrawKey loopHandle1 = commandQueue->createDrawKey(); |
| DrawKey loopHandle2 = commandQueue->createDrawKey(); |
| |
| commandQueue->runOnce([&](CommandServer*) { |
| CHECK(frameNumber1 == 0); |
| CHECK(frameNumber2 == 0); |
| lastFrameNumber1 = frameNumber1.load(); |
| lastFrameNumber2 = frameNumber2.load(); |
| }); |
| |
| commandQueue->draw(loopHandle1, drawLoop1); |
| commandQueue->draw(loopHandle2, drawLoop2); |
| |
| do |
| { |
| wait_for_server(commandQueue.get()); |
| } while (frameNumber1 == lastFrameNumber1 || |
| frameNumber2 == lastFrameNumber2); |
| |
| commandQueue->runOnce([&](CommandServer*) { |
| CHECK(frameNumber1 > lastFrameNumber1); |
| CHECK(frameNumber2 > lastFrameNumber2); |
| }); |
| |
| commandQueue->runOnce([&](CommandServer*) { |
| lastFrameNumber1 = frameNumber1.load(); |
| lastFrameNumber2 = frameNumber2.load(); |
| }); |
| |
| commandQueue->draw(loopHandle2, drawLoop2); |
| |
| do |
| { |
| wait_for_server(commandQueue.get()); |
| } while (frameNumber2 == lastFrameNumber2); |
| commandQueue->runOnce([&](CommandServer*) { |
| CHECK(frameNumber1 == lastFrameNumber1); |
| CHECK(frameNumber2 > lastFrameNumber2); |
| }); |
| |
| commandQueue->runOnce([&](CommandServer*) { |
| lastFrameNumber1 = frameNumber1.load(); |
| lastFrameNumber2 = frameNumber2.load(); |
| }); |
| |
| commandQueue->draw(loopHandle1, drawLoop1); |
| |
| do |
| { |
| wait_for_server(commandQueue.get()); |
| } while (frameNumber1 == lastFrameNumber1); |
| commandQueue->runOnce([&](CommandServer*) { |
| CHECK(frameNumber1 > lastFrameNumber1); |
| CHECK(frameNumber2 == lastFrameNumber2); |
| }); |
| |
| commandQueue->runOnce([&](CommandServer*) { |
| lastFrameNumber1 = frameNumber1.load(); |
| lastFrameNumber2 = frameNumber2.load(); |
| }); |
| |
| for (int i = 0; i < 10; ++i) |
| { |
| wait_for_server(commandQueue.get()); |
| } |
| commandQueue->runOnce([&](CommandServer*) { |
| CHECK(frameNumber1 == lastFrameNumber1); |
| CHECK(frameNumber2 == lastFrameNumber2); |
| }); |
| |
| commandQueue->draw(loopHandle1, drawLoop1); |
| commandQueue->draw(loopHandle2, drawLoop2); |
| |
| do |
| { |
| wait_for_server(commandQueue.get()); |
| } while (frameNumber1 == lastFrameNumber1 || |
| frameNumber2 == lastFrameNumber2); |
| commandQueue->runOnce([&](CommandServer*) { |
| CHECK(frameNumber1 > lastFrameNumber1); |
| CHECK(frameNumber2 > lastFrameNumber2); |
| }); |
| |
| // Leave the draw loops running; test tearing down the command buffer with |
| // active draw loops. |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("wait for server race condition", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| auto lambda = [](CommandServer*) { |
| std::this_thread::sleep_for(std::chrono::microseconds(10)); |
| }; |
| auto drawLambda = [](DrawKey, CommandServer*) { |
| std::this_thread::sleep_for(std::chrono::microseconds(10)); |
| }; |
| |
| commandQueue->runOnce(lambda); |
| |
| commandQueue->draw(reinterpret_cast<DrawKey>(0), drawLambda); |
| commandQueue->draw(reinterpret_cast<DrawKey>(1), drawLambda); |
| commandQueue->draw(reinterpret_cast<DrawKey>(2), drawLambda); |
| commandQueue->draw(reinterpret_cast<DrawKey>(3), drawLambda); |
| commandQueue->draw(reinterpret_cast<DrawKey>(4), drawLambda); |
| |
| for (size_t i = 0; i < 100; ++i) |
| |
| { |
| commandQueue->draw(reinterpret_cast<DrawKey>(i), drawLambda); |
| commandQueue->runOnce(lambda); |
| } |
| for (size_t i = 0; i < 10; ++i) |
| |
| { |
| wait_for_server(commandQueue.get()); |
| } |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("stopMesssages command", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| CommandServer server(commandQueue, nullContext.get()); |
| |
| int test = 0; |
| |
| commandQueue->runOnce([&](CommandServer*) { ++test; }); |
| commandQueue->testing_commandLoopBreak(); |
| |
| for (int i = 0; i < 10; i++) |
| { |
| commandQueue->runOnce([&](CommandServer*) { ++test; }); |
| if (i == 5) |
| commandQueue->testing_commandLoopBreak(); |
| } |
| |
| server.processCommands(); |
| |
| CHECK(test == 1); |
| server.processCommands(); |
| CHECK(test == 7); |
| server.processCommands(); |
| CHECK(test == 11); |
| |
| commandQueue->disconnect(); |
| } |
| |
| TEST_CASE("draw happens once per poll", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| CommandServer server(commandQueue, nullContext.get()); |
| int test = 0; |
| |
| auto drawLamda = [&test](DrawKey, CommandServer*) { ++test; }; |
| |
| commandQueue->draw(0, drawLamda); |
| commandQueue->draw(0, drawLamda); |
| commandQueue->draw(0, drawLamda); |
| commandQueue->draw(0, drawLamda); |
| commandQueue->draw(0, drawLamda); |
| |
| server.processCommands(); |
| |
| CHECK(test == 1); |
| commandQueue->draw(0, drawLamda); |
| server.processCommands(); |
| CHECK(test == 2); |
| server.processCommands(); |
| CHECK(test == 2); |
| |
| commandQueue->disconnect(); |
| } |
| |
| TEST_CASE("disconnect", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| CommandServer server(commandQueue, nullContext.get()); |
| CHECK(!server.getWasDisconnected()); |
| |
| CHECK(server.processCommands()); |
| commandQueue->disconnect(); |
| CHECK(!server.processCommands()); |
| } |
| |
| TEST_CASE("global asset set / remove", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| |
| CommandServer server(commandQueue, nullContext.get()); |
| |
| std::ifstream imageStream("assets/batdude.png", std::ios::binary); |
| |
| auto imageHandle = commandQueue->decodeImage( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(imageStream), {})); |
| |
| commandQueue->addGlobalImageAsset("image", imageHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->testing_globalImageNamed("image") == imageHandle); |
| }); |
| |
| commandQueue->removeGlobalImageAsset("image"); |
| commandQueue->removeGlobalFontAsset("font"); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalImageContains("image")); |
| }); |
| |
| // should not add nullptr or invalid values to maps |
| commandQueue->addGlobalImageAsset("image", nullptr); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalImageContains("image")); |
| }); |
| |
| auto badImageHandle = |
| commandQueue->decodeImage(std::vector<uint8_t>(1024, 0)); |
| |
| commandQueue->addGlobalImageAsset("image", badImageHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalImageContains("image")); |
| }); |
| |
| commandQueue->removeGlobalImageAsset("blah"); |
| commandQueue->removeGlobalFontAsset("blah"); |
| commandQueue->removeGlobalAudioAsset("blah"); |
| |
| commandQueue->addGlobalImageAsset("image", imageHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->testing_globalImageNamed("image") == imageHandle); |
| }); |
| |
| commandQueue->deleteImage(imageHandle); |
| |
| // These should be removed automatically |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalImageContains("image")); |
| }); |
| |
| #ifdef WITH_RIVE_AUDIO |
| std::ifstream audioStream("assets/audio/what.wav", std::ios::binary); |
| |
| auto audioHandle = commandQueue->decodeAudio( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(audioStream), {})); |
| |
| commandQueue->addGlobalAudioAsset("audio", audioHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->testing_globalAudioNamed("audio") == audioHandle); |
| }); |
| |
| commandQueue->removeGlobalAudioAsset("audio"); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalAudioContains("audio")); |
| }); |
| |
| // should not add nullptr or invalid values to maps |
| commandQueue->addGlobalAudioAsset("audio", nullptr); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalAudioContains("audio")); |
| }); |
| |
| auto badAudioHandle = |
| commandQueue->decodeAudio(std::vector<uint8_t>(1024, 0)); |
| |
| commandQueue->addGlobalAudioAsset("audio", badAudioHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalAudioContains("audio")); |
| }); |
| |
| commandQueue->addGlobalAudioAsset("audio", audioHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->testing_globalAudioNamed("audio") == audioHandle); |
| }); |
| |
| commandQueue->deleteAudio(audioHandle); |
| |
| // These should be removed automatically |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalImageContains("image")); |
| CHECK(!server->testing_globalFontContains("font")); |
| CHECK(!server->testing_globalAudioContains("audio")); |
| }); |
| #endif |
| |
| #ifdef WITH_RIVE_TEXT |
| std::ifstream fontStream("assets/fonts/OpenSans-Italic.ttf", |
| std::ios::binary); |
| |
| auto fontHandle = commandQueue->decodeFont( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(fontStream), {})); |
| |
| commandQueue->addGlobalFontAsset("font", fontHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->testing_globalFontNamed("font") == fontHandle); |
| }); |
| |
| commandQueue->removeGlobalFontAsset("font"); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalFontContains("font")); |
| }); |
| |
| // should not add nullptr or invalid values to maps |
| commandQueue->addGlobalFontAsset("font", nullptr); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalFontContains("font")); |
| }); |
| |
| auto badFontHandle = |
| commandQueue->decodeFont(std::vector<uint8_t>(1024, 0)); |
| |
| commandQueue->addGlobalFontAsset("font", badFontHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalFontContains("font")); |
| }); |
| |
| commandQueue->addGlobalFontAsset("font", fontHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->testing_globalFontNamed("font") == fontHandle); |
| }); |
| |
| commandQueue->deleteFont(fontHandle); |
| |
| // These should be removed automatically |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(!server->testing_globalFontContains("font")); |
| }); |
| #endif |
| |
| server.processCommands(); |
| commandQueue->disconnect(); |
| } |
| |
| TEST_CASE("View Models", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| auto bviewModel = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, "Test All"); |
| auto dviewModel = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| "Test All"); |
| auto nviewModel = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Test All", |
| "Test Alternate"); |
| auto neviewModel = |
| commandQueue->referenceNestedViewModelInstance(bviewModel, |
| "Test Nested"); |
| commandQueue->insertViewModelInstanceListViewModel(bviewModel, |
| "Test List", |
| neviewModel, |
| 0); |
| auto listViewModel = |
| commandQueue->referenceListViewModelInstance(bviewModel, |
| "Test List", |
| 0); |
| commandQueue->runOnce([bviewModel, |
| dviewModel, |
| nviewModel, |
| neviewModel, |
| listViewModel](CommandServer* server) { |
| CHECK(server->getViewModelInstance(bviewModel) != nullptr); |
| CHECK(server->getViewModelInstance(dviewModel) != nullptr); |
| CHECK(server->getViewModelInstance(nviewModel) != nullptr); |
| CHECK(server->getViewModelInstance(neviewModel) != nullptr); |
| CHECK(server->getViewModelInstance(listViewModel) != nullptr); |
| CHECK(server->getViewModelInstance(listViewModel) == |
| server->getViewModelInstance(neviewModel)); |
| auto list = |
| server->getViewModelInstance(bviewModel)->propertyList("Test List"); |
| CHECK(list != nullptr); |
| CHECK(list->instanceAt(0).get() == |
| server->getViewModelInstance(listViewModel)); |
| }); |
| |
| commandQueue->removeViewModelInstanceListViewModel(bviewModel, |
| "Test List", |
| neviewModel); |
| |
| commandQueue->runOnce([bviewModel](CommandServer* server) { |
| auto list = |
| server->getViewModelInstance(bviewModel)->propertyList("Test List"); |
| CHECK(list != nullptr); |
| CHECK(list->size() == 0); |
| }); |
| |
| auto bbviewModel = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, "Blah"); |
| auto bdviewModel = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, "Blah"); |
| auto bnviewModel = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Blah", |
| "Blah"); |
| auto bnnviewModel = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Blah", |
| "Test Alternate"); |
| auto bnbviewModel = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Test All", |
| "Blah"); |
| |
| commandQueue->removeViewModelInstanceListViewModel(bviewModel, |
| "Blah", |
| neviewModel); |
| |
| commandQueue->removeViewModelInstanceListViewModel(bviewModel, |
| "Test List", |
| bnbviewModel); |
| auto badPathNeviewModel = |
| commandQueue->referenceNestedViewModelInstance(bviewModel, "Blah"); |
| |
| auto badNeviewModel = |
| commandQueue->referenceNestedViewModelInstance(bnnviewModel, |
| "Test Nested"); |
| |
| auto badListViewModel = |
| commandQueue->referenceListViewModelInstance(bviewModel, "Blah", 0); |
| |
| auto badListViewModel2 = |
| commandQueue->referenceListViewModelInstance(bviewModel, |
| "Test List", |
| 5); |
| |
| commandQueue->runOnce([bbviewModel, |
| bdviewModel, |
| bnviewModel, |
| bnnviewModel, |
| bnbviewModel, |
| badPathNeviewModel, |
| badNeviewModel, |
| badListViewModel, |
| badListViewModel2](CommandServer* server) { |
| CHECK(server->getViewModelInstance(bbviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(bdviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(bnviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(bnnviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(bnbviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(badPathNeviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(badNeviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(badListViewModel) == nullptr); |
| CHECK(server->getViewModelInstance(badListViewModel2) == nullptr); |
| }); |
| |
| auto artboard = |
| commandQueue->instantiateArtboardNamed(fileHandle, "Test Artboard"); |
| |
| auto abviewModel = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, artboard); |
| auto adviewModel = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, artboard); |
| auto anviewModel = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| artboard, |
| "Test Alternate"); |
| |
| commandQueue->runOnce( |
| [abviewModel, adviewModel, anviewModel](CommandServer* server) { |
| CHECK(server->getViewModelInstance(abviewModel) != nullptr); |
| CHECK(server->getViewModelInstance(adviewModel) != nullptr); |
| CHECK(server->getViewModelInstance(anviewModel) != nullptr); |
| }); |
| |
| auto badArtboard = |
| commandQueue->instantiateArtboardNamed(fileHandle, "Blah"); |
| |
| auto babviewModel = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, |
| badArtboard); |
| auto badviewModel = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| badArtboard); |
| auto banviewModel = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| badArtboard, |
| "Test Alternate"); |
| |
| commandQueue->runOnce( |
| [babviewModel, badviewModel, banviewModel](CommandServer* server) { |
| CHECK(server->getViewModelInstance(babviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(badviewModel) == nullptr); |
| CHECK(server->getViewModelInstance(banviewModel) == nullptr); |
| }); |
| |
| commandQueue->deleteViewModelInstance(badNeviewModel); |
| commandQueue->deleteViewModelInstance(bviewModel); |
| |
| commandQueue->runOnce([neviewModel](CommandServer* server) { |
| CHECK(server->getViewModelInstance(neviewModel) != nullptr); |
| }); |
| |
| commandQueue->deleteViewModelInstance(neviewModel); |
| commandQueue->runOnce([neviewModel](CommandServer* server) { |
| CHECK(server->getViewModelInstance(neviewModel) == nullptr); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class ViewModelListedListenerCallback : public CommandQueue::FileListener |
| { |
| public: |
| virtual void onViewModelsListed( |
| const FileHandle fileHandle, |
| uint64_t requestId, |
| std::vector<std::string> viewModelNames) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(m_fileHandle == fileHandle); |
| CHECK(viewModelNames.size() == std::size(m_expectedViewModelNames)); |
| |
| for (int i = 0; i < std::size(m_expectedViewModelNames); ++i) |
| { |
| CHECK(viewModelNames[i] == m_expectedViewModelNames[i]); |
| } |
| |
| m_hasCallback = true; |
| } |
| |
| std::array<std::string, 6> m_expectedViewModelNames = {"ListViewModel", |
| "Empty VM", |
| "Test All", |
| "Nested VM", |
| "State Transition", |
| "Alternate VM"}; |
| bool m_hasCallback = false; |
| FileHandle m_fileHandle = RIVE_NULL_HANDLE; |
| uint64_t m_requestId; |
| }; |
| |
| TEST_CASE("View Model Listed Listener", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| { |
| ViewModelListedListenerCallback listener; |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", |
| std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &listener); |
| |
| listener.m_requestId = 2; |
| listener.m_fileHandle = fileHandle; |
| |
| commandQueue->requestViewModelNames(fileHandle, listener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(listener.m_hasCallback); |
| } |
| |
| { |
| ViewModelListedListenerCallback listener; |
| |
| FileHandle fileHandle = |
| commandQueue->loadFile(std::vector<uint8_t>(1024 * 1024, {}), |
| &listener); |
| |
| listener.m_requestId = 2; |
| listener.m_fileHandle = fileHandle; |
| |
| commandQueue->requestViewModelNames(fileHandle, listener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(!listener.m_hasCallback); |
| } |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestViewModelFileListener : public CommandQueue::FileListener |
| { |
| public: |
| virtual void onViewModelInstanceNamesListed( |
| const FileHandle handle, |
| uint64_t requestId, |
| std::string viewModelName, |
| std::vector<std::string> instanceNames) override |
| { |
| CHECK(requestId == m_instanceRequestId); |
| CHECK(m_handle == handle); |
| CHECK(m_viewModelName == viewModelName); |
| CHECK(instanceNames.size() == std::size(m_expectedInstanceNames)); |
| |
| for (int i = 0; i < std::size(m_expectedInstanceNames); ++i) |
| { |
| CHECK(instanceNames[i] == m_expectedInstanceNames[i]); |
| } |
| |
| m_hasInstanceCallback = true; |
| } |
| |
| virtual void onViewModelPropertiesListed( |
| const FileHandle handle, |
| uint64_t requestId, |
| std::string viewModelName, |
| std::vector<CommandQueue::FileListener::ViewModelPropertyData> |
| properties) override |
| { |
| CHECK(requestId == m_propertyRequestId); |
| CHECK(m_handle == handle); |
| CHECK(m_viewModelName == viewModelName); |
| CHECK(properties.size() == std::size(m_expectedProperties)); |
| |
| for (int i = 0; i < std::size(m_expectedProperties); ++i) |
| { |
| CHECK(properties[i] == m_expectedProperties[i]); |
| } |
| |
| m_hasPropertyCallback = true; |
| } |
| |
| std::array<std::string, 2> m_expectedInstanceNames = {"Test Default", |
| "Test Alternate"}; |
| std::array<CommandQueue::FileListener::ViewModelPropertyData, 10> |
| m_expectedProperties = { |
| CommandQueue::FileListener::ViewModelPropertyData{ |
| DataType::artboard, |
| "Test Artboard"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::list, |
| "Test List"}, |
| CommandQueue::FileListener::ViewModelPropertyData{ |
| DataType::assetImage, |
| "Test Image"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::number, |
| "Test Num"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::string, |
| "Test String"}, |
| CommandQueue::FileListener::ViewModelPropertyData{ |
| DataType::enumType, |
| "Test Enum", |
| "Test Enum Values"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::boolean, |
| "Test Bool"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::color, |
| "Test Color"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::trigger, |
| "Test Trigger"}, |
| CommandQueue::FileListener::ViewModelPropertyData{ |
| DataType::viewModel, |
| "Test Nested", |
| "Nested VM"}}; |
| |
| bool m_hasInstanceCallback = false; |
| bool m_hasPropertyCallback = false; |
| FileHandle m_handle = RIVE_NULL_HANDLE; |
| std::string m_viewModelName = "Test All"; |
| uint64_t m_instanceRequestId = 2; |
| uint64_t m_propertyRequestId = 3; |
| }; |
| |
| TEST_CASE("View Model Listener", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| { |
| TestViewModelFileListener listener; |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", |
| std::ios::binary); |
| listener.m_handle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &listener); |
| |
| commandQueue->requestViewModelInstanceNames( |
| listener.m_handle, |
| listener.m_viewModelName, |
| listener.m_instanceRequestId); |
| commandQueue->requestViewModelPropertyDefinitions( |
| listener.m_handle, |
| listener.m_viewModelName, |
| listener.m_propertyRequestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(listener.m_hasInstanceCallback); |
| CHECK(listener.m_hasPropertyCallback); |
| } |
| |
| { |
| TestViewModelFileListener listener; |
| |
| listener.m_handle = |
| commandQueue->loadFile(std::vector<uint8_t>(1024 * 1024, {})); |
| |
| commandQueue->requestViewModelInstanceNames( |
| listener.m_handle, |
| listener.m_viewModelName, |
| listener.m_instanceRequestId); |
| commandQueue->requestViewModelPropertyDefinitions( |
| listener.m_handle, |
| listener.m_viewModelName, |
| listener.m_propertyRequestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(!listener.m_hasInstanceCallback); |
| CHECK(!listener.m_hasPropertyCallback); |
| } |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestViewModelInstanceListener |
| : public CommandQueue::ViewModelInstanceListener |
| { |
| public: |
| virtual void onViewModelDeleted(const ViewModelInstanceHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_deleteRequestId); |
| CHECK(m_handle == handle); |
| m_hasDeleteCallback = true; |
| } |
| |
| bool m_hasDeleteCallback = false; |
| |
| ViewModelInstanceHandle m_handle = RIVE_NULL_HANDLE; |
| uint64_t m_deleteRequestId = std::rand(); |
| }; |
| |
| TEST_CASE("View Model Instance Listener", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| TestViewModelInstanceListener bListener; |
| TestViewModelInstanceListener dListener; |
| TestViewModelInstanceListener nListener; |
| |
| TestViewModelInstanceListener badListener; |
| |
| TestViewModelInstanceListener aListener; |
| TestViewModelInstanceListener adListener; |
| TestViewModelInstanceListener anListener; |
| TestViewModelInstanceListener badAListener; |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| bListener.m_handle = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, |
| "Test All", |
| &bListener); |
| dListener.m_handle = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| "Test All", |
| &dListener); |
| nListener.m_handle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Test All", |
| "Test Alternate", |
| &nListener); |
| |
| badListener.m_handle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Blah", |
| "Blah", |
| &badListener); |
| |
| auto artboard = |
| commandQueue->instantiateArtboardNamed(fileHandle, "Test Artboard"); |
| |
| auto badArtboard = |
| commandQueue->instantiateArtboardNamed(fileHandle, "Blah"); |
| |
| aListener.m_handle = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, |
| artboard, |
| &aListener); |
| adListener.m_handle = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| artboard, |
| &adListener); |
| anListener.m_handle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| artboard, |
| "Test Alternate", |
| &anListener); |
| |
| badAListener.m_handle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| badArtboard, |
| "Test Alternate", |
| &badAListener); |
| |
| commandQueue->deleteViewModelInstance(bListener.m_handle, |
| bListener.m_deleteRequestId); |
| commandQueue->deleteViewModelInstance(dListener.m_handle, |
| dListener.m_deleteRequestId); |
| commandQueue->deleteViewModelInstance(nListener.m_handle, |
| nListener.m_deleteRequestId); |
| commandQueue->deleteViewModelInstance(badListener.m_handle, |
| badListener.m_deleteRequestId); |
| |
| commandQueue->deleteViewModelInstance(aListener.m_handle, |
| aListener.m_deleteRequestId); |
| commandQueue->deleteViewModelInstance(adListener.m_handle, |
| adListener.m_deleteRequestId); |
| commandQueue->deleteViewModelInstance(anListener.m_handle, |
| anListener.m_deleteRequestId); |
| commandQueue->deleteViewModelInstance(badAListener.m_handle, |
| badAListener.m_deleteRequestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(bListener.m_hasDeleteCallback); |
| CHECK(dListener.m_hasDeleteCallback); |
| CHECK(nListener.m_hasDeleteCallback); |
| CHECK(badListener.m_hasDeleteCallback); |
| |
| CHECK(aListener.m_hasDeleteCallback); |
| CHECK(adListener.m_hasDeleteCallback); |
| CHECK(anListener.m_hasDeleteCallback); |
| CHECK(badAListener.m_hasDeleteCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("External Resources", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| rcp<RenderImage> externalImage = nullptr; |
| rcp<AudioSource> externalAudio = nullptr; |
| rcp<Font> externalFont = nullptr; |
| |
| commandQueue->runOnce([&externalImage, &externalAudio, &externalFont]( |
| CommandServer* server) { |
| std::ifstream imageStream("assets/batdude.png", std::ios::binary); |
| std::vector<uint8_t> imageStreamData( |
| std::istreambuf_iterator<char>(imageStream), |
| {}); |
| |
| std::ifstream audioStream("assets/audio/what.wav", std::ios::binary); |
| std::vector<uint8_t> audioStreamData( |
| std::istreambuf_iterator<char>(audioStream), |
| {}); |
| |
| std::ifstream fontStream("assets/fonts/OpenSans-Italic.ttf", |
| std::ios::binary); |
| std::vector<uint8_t> fontStreamData( |
| std::istreambuf_iterator<char>(fontStream), |
| {}); |
| |
| auto factory = server->factory(); |
| |
| externalImage = factory->decodeImage(imageStreamData); |
| externalAudio = factory->decodeAudio(audioStreamData); |
| externalFont = factory->decodeFont(fontStreamData); |
| }); |
| |
| wait_for_server(commandQueue.get()); |
| |
| CHECK(externalImage); |
| CHECK(externalAudio); |
| CHECK(externalFont); |
| |
| RenderImageHandle externalImageHandle = |
| commandQueue->addExternalImage(externalImage); |
| AudioSourceHandle externalAudioHandle = |
| commandQueue->addExternalAudio(externalAudio); |
| FontHandle externalFontHandle = commandQueue->addExternalFont(externalFont); |
| |
| commandQueue->runOnce([externalImageHandle, |
| externalImage, |
| externalAudioHandle, |
| externalAudio, |
| externalFontHandle, |
| externalFont](CommandServer* server) { |
| auto image = server->getImage(externalImageHandle); |
| CHECK(image == externalImage.get()); |
| auto audio = server->getAudioSource(externalAudioHandle); |
| CHECK(audio == externalAudio.get()); |
| auto font = server->getFont(externalFontHandle); |
| CHECK(font == externalFont.get()); |
| }); |
| |
| commandQueue->deleteImage(externalImageHandle); |
| commandQueue->deleteAudio(externalAudioHandle); |
| commandQueue->deleteFont(externalFontHandle); |
| |
| commandQueue->runOnce([externalImageHandle, |
| externalAudioHandle, |
| externalFontHandle](CommandServer* server) { |
| auto image = server->getImage(externalImageHandle); |
| CHECK(image == nullptr); |
| auto audio = server->getAudioSource(externalAudioHandle); |
| CHECK(audio == nullptr); |
| auto font = server->getFont(externalFontHandle); |
| CHECK(font == nullptr); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("RenderImage", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/batdude.png", std::ios::binary); |
| RenderImageHandle imageHandle = commandQueue->decodeImage( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| RenderImageHandle badImageHandle = |
| commandQueue->decodeImage(std::vector<uint8_t>(1024, {})); |
| |
| commandQueue->runOnce([imageHandle, badImageHandle](CommandServer* server) { |
| auto image = server->getImage(imageHandle); |
| CHECK(image != nullptr); |
| auto badImage = server->getImage(badImageHandle); |
| CHECK(badImage == nullptr); |
| }); |
| |
| commandQueue->deleteImage(imageHandle); |
| commandQueue->deleteImage(badImageHandle); |
| |
| commandQueue->runOnce([imageHandle, badImageHandle](CommandServer* server) { |
| auto image = server->getImage(imageHandle); |
| CHECK(image == nullptr); |
| auto badImage = server->getImage(badImageHandle); |
| CHECK(badImage == nullptr); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| #ifdef WITH_RIVE_AUDIO |
| class AudioSourceDeletedListener : public CommandQueue::AudioSourceListener |
| { |
| public: |
| virtual void onAudioSourceDeleted(const AudioSourceHandle handle, |
| uint64_t requestId) |
| { |
| CHECK(m_handle == handle); |
| CHECK(m_requestId == requestId); |
| CHECK(!m_hasCallback); |
| m_hasCallback = true; |
| } |
| |
| AudioSourceHandle m_handle; |
| bool m_hasCallback = false; |
| uint64_t m_requestId = 0x10; |
| }; |
| |
| TEST_CASE("AudioSource", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/audio/what.wav", std::ios::binary); |
| |
| AudioSourceDeletedListener listener; |
| |
| AudioSourceHandle audioHandle = commandQueue->decodeAudio( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &listener, |
| 10); |
| listener.m_handle = audioHandle; |
| AudioSourceHandle badAudioHandle = |
| commandQueue->decodeAudio(std::vector<uint8_t>(1024, {})); |
| |
| commandQueue->runOnce([audioHandle, badAudioHandle](CommandServer* server) { |
| auto audio = server->getAudioSource(audioHandle); |
| CHECK(audio != nullptr); |
| auto badAudio = server->getAudioSource(badAudioHandle); |
| CHECK(badAudio == nullptr); |
| }); |
| |
| commandQueue->deleteAudio(audioHandle, listener.m_requestId); |
| commandQueue->deleteAudio(badAudioHandle); |
| |
| commandQueue->runOnce([audioHandle, badAudioHandle](CommandServer* server) { |
| auto audio = server->getAudioSource(audioHandle); |
| CHECK(audio == nullptr); |
| auto badAudio = server->getAudioSource(badAudioHandle); |
| CHECK(badAudio == nullptr); |
| }); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(listener.m_hasCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| #endif |
| |
| #if WITH_RIVE_TEXT |
| |
| class FontDeletedListener : public CommandQueue::FontListener |
| { |
| public: |
| virtual void onFontDeleted(const FontHandle handle, uint64_t requestId) |
| { |
| CHECK(m_handle == handle); |
| CHECK(m_requestId == requestId); |
| CHECK(!m_hasCallback); |
| m_hasCallback = true; |
| } |
| |
| FontHandle m_handle; |
| bool m_hasCallback = false; |
| uint64_t m_requestId = 0x10; |
| }; |
| |
| TEST_CASE("Font", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| FontDeletedListener listener; |
| |
| std::ifstream stream("assets/fonts/OpenSans-Italic.ttf", std::ios::binary); |
| FontHandle fontHandle = commandQueue->decodeFont( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &listener, |
| 10); |
| listener.m_handle = fontHandle; |
| FontHandle badFontHandle = |
| commandQueue->decodeFont(std::vector<uint8_t>(1024, {})); |
| |
| commandQueue->runOnce([fontHandle, badFontHandle](CommandServer* server) { |
| auto font = server->getFont(fontHandle); |
| CHECK(font != nullptr); |
| auto badFont = server->getFont(badFontHandle); |
| CHECK(badFont == nullptr); |
| }); |
| |
| commandQueue->deleteFont(fontHandle, listener.m_requestId); |
| commandQueue->deleteFont(badFontHandle); |
| |
| commandQueue->runOnce([fontHandle, badFontHandle](CommandServer* server) { |
| auto font = server->getFont(fontHandle); |
| CHECK(font == nullptr); |
| auto badFont = server->getFont(badFontHandle); |
| CHECK(badFont == nullptr); |
| }); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(listener.m_hasCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| #endif |
| |
| namespace rive |
| { |
| bool operator==(const CommandQueue::ViewModelInstanceData& l, |
| const CommandQueue::ViewModelInstanceData& r) |
| |
| { |
| bool ret = l.metaData == r.metaData; |
| switch (l.metaData.type) |
| { |
| case DataType::boolean: |
| ret &= l.boolValue == r.boolValue; |
| break; |
| case DataType::number: |
| ret &= l.numberValue == r.numberValue; |
| break; |
| case DataType::color: |
| ret &= l.colorValue == r.colorValue; |
| break; |
| case DataType::string: |
| case DataType::enumType: |
| ret &= l.stringValue == r.stringValue; |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| } // namespace rive |
| |
| class ViewModelPropertyListener : public CommandQueue::ViewModelInstanceListener |
| { |
| public: |
| virtual void onViewModelInstanceError(const ViewModelInstanceHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(error.size()); |
| ++m_receivedErrors; |
| } |
| virtual void onViewModelDeleted(const ViewModelInstanceHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(!n_wasDeleted); |
| n_wasDeleted = true; |
| } |
| |
| virtual void onViewModelDataReceived( |
| const ViewModelInstanceHandle handle, |
| uint64_t requestId, |
| CommandQueue::ViewModelInstanceData data) override |
| { |
| // the callback order should be garunteed. |
| // so getting these in the order they are requested should work |
| CHECK(m_expectedData.size()); |
| CHECK(m_expectedRequestIds.size()); |
| auto expectedData = m_expectedData.front(); |
| m_expectedData.pop_front(); |
| auto expectedRequestId = m_expectedRequestIds.front(); |
| m_expectedRequestIds.pop_front(); |
| |
| CHECK(handle == m_handle); |
| CHECK(data == expectedData); |
| CHECK(requestId == expectedRequestId); |
| } |
| |
| void pushExpectation(CommandQueue* queue, std::string name, float value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceNumber(m_handle, name, value, m_requestIdx); |
| queue->runOnce([handle = m_handle, name, value](CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| CHECK(instance != nullptr); |
| auto property = instance->propertyNumber(name); |
| CHECK(property != nullptr); |
| CHECK(property->value() == value); |
| }); |
| m_expectedData.push_back( |
| {.metaData = {DataType::number, name}, .numberValue = value}); |
| m_expectedRequestIds.push_back(m_requestIdx); |
| queue->requestViewModelInstanceNumber(m_handle, name, m_requestIdx); |
| } |
| |
| void pushExpectation(CommandQueue* queue, std::string name, ColorInt value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceColor(m_handle, name, value, m_requestIdx); |
| queue->runOnce([handle = m_handle, name, value](CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| CHECK(instance != nullptr); |
| auto property = instance->propertyColor(name); |
| CHECK(property != nullptr); |
| CHECK(property->value() == value); |
| }); |
| m_expectedData.push_back( |
| {.metaData = {DataType::color, name}, .colorValue = value}); |
| m_expectedRequestIds.push_back(m_requestIdx); |
| queue->requestViewModelInstanceColor(m_handle, name, m_requestIdx); |
| } |
| |
| void pushExpectation(CommandQueue* queue, std::string name, bool value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceBool(m_handle, name, value, m_requestIdx); |
| queue->runOnce([handle = m_handle, name, value](CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| CHECK(instance != nullptr); |
| auto property = instance->propertyBoolean(name); |
| CHECK(property != nullptr); |
| CHECK(property->value() == value); |
| }); |
| m_expectedData.push_back( |
| {.metaData = {DataType::boolean, name}, .boolValue = value}); |
| m_expectedRequestIds.push_back(m_requestIdx); |
| queue->requestViewModelInstanceBool(m_handle, name, m_requestIdx); |
| } |
| |
| void pushExpectation(CommandQueue* queue, |
| std::string name, |
| ViewModelInstanceHandle value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceNestedViewModel(m_handle, |
| name, |
| value, |
| m_requestIdx); |
| queue->runOnce([handle = m_handle, name, value](CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| auto nested = server->getViewModelInstance(value); |
| CHECK(instance != nullptr); |
| CHECK(nested != nullptr); |
| auto property = instance->propertyViewModel(name); |
| CHECK(property != nullptr); |
| CHECK(property.get() == nested); |
| }); |
| |
| // There is no requesting for nested view models |
| } |
| |
| void pushStringExpectation(CommandQueue* queue, |
| std::string name, |
| std::string value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceString(m_handle, name, value, m_requestIdx); |
| queue->runOnce([handle = m_handle, name, value](CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| CHECK(instance != nullptr); |
| auto property = instance->propertyString(name); |
| CHECK(property != nullptr); |
| CHECK(property->value() == value); |
| }); |
| m_expectedData.push_back( |
| {.metaData = {DataType::string, name}, .stringValue = value}); |
| m_expectedRequestIds.push_back(m_requestIdx); |
| queue->requestViewModelInstanceString(m_handle, name, m_requestIdx); |
| } |
| |
| void pushEnumExpectation(CommandQueue* queue, |
| std::string name, |
| std::string value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceEnum(m_handle, name, value, m_requestIdx); |
| queue->runOnce([handle = m_handle, name, value](CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| CHECK(instance != nullptr); |
| auto property = instance->propertyEnum(name); |
| CHECK(property != nullptr); |
| CHECK(property->value() == value); |
| }); |
| m_expectedData.push_back( |
| {.metaData = {DataType::enumType, name}, .stringValue = value}); |
| m_expectedRequestIds.push_back(m_requestIdx); |
| queue->requestViewModelInstanceEnum(m_handle, name, m_requestIdx); |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, std::string name, float value) |
| { |
| queue->setViewModelInstanceNumber(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, |
| std::string name, |
| ColorInt value) |
| { |
| queue->setViewModelInstanceColor(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, std::string name, bool value) |
| { |
| queue->setViewModelInstanceBool(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, |
| std::string name, |
| ViewModelInstanceHandle value) |
| { |
| queue->setViewModelInstanceNestedViewModel(m_handle, |
| name, |
| value, |
| m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadStringExpectation(CommandQueue* queue, |
| std::string name, |
| std::string value) |
| { |
| queue->setViewModelInstanceString(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadEnumExpectation(CommandQueue* queue, |
| std::string name, |
| std::string value) |
| { |
| queue->setViewModelInstanceEnum(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| // one data and id per callback |
| std::deque<CommandQueue::ViewModelInstanceData> m_expectedData; |
| std::deque<uint64_t> m_expectedRequestIds; |
| // all of the handles should be the same |
| ViewModelInstanceHandle m_handle; |
| bool n_wasDeleted = false; |
| |
| uint64_t m_requestIdx = 1; |
| |
| uint64_t m_expectedErrors = 0; |
| uint64_t m_receivedErrors = 0; |
| }; |
| |
| TEST_CASE("View Model Property Set/Get", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| ViewModelPropertyListener tester; |
| |
| auto artboardHandle = commandQueue->instantiateDefaultArtboard(fileHandle); |
| tester.m_handle = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| artboardHandle, |
| &tester); |
| auto blankHandle = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, |
| "Nested VM"); |
| auto alternateHandle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Nested VM", |
| "Alternate Nested"); |
| |
| tester.pushExpectation(commandQueue.get(), "Test Bool", true); |
| tester.pushExpectation(commandQueue.get(), "Test Num", 10.0f); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Nested/Nested Number", |
| 10.0f); |
| tester.pushExpectation(commandQueue.get(), "Test Nested", blankHandle); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Nested/Nested Number", |
| 10.0f); |
| commandQueue->runOnce([rootHandle = tester.m_handle, |
| blankHandle](CommandServer* server) { |
| auto root = server->getViewModelInstance(rootHandle); |
| auto nested = server->getViewModelInstance(blankHandle); |
| CHECK(root != nullptr); |
| CHECK(nested != nullptr); |
| auto rootProperty = root->propertyNumber("Test Nested/Nested Number"); |
| CHECK(rootProperty != nullptr); |
| CHECK(rootProperty->value() == 10.0f); |
| auto nestedProperty = nested->propertyNumber("Nested Number"); |
| CHECK(nestedProperty != nullptr); |
| CHECK(nestedProperty->value() == 10.0f); |
| }); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Color", |
| rive::colorARGB(255, 255, 0, 0)); |
| tester.pushEnumExpectation(commandQueue.get(), "Test Enum", "Value 2"); |
| tester.pushStringExpectation(commandQueue.get(), |
| "Test String", |
| "Some String"); |
| |
| // Images don't have a "get" equivalent so we test it with a run once |
| // directly. |
| std::ifstream imageStream("assets/batdude.png", std::ios::binary); |
| auto imageHandle = commandQueue->decodeImage( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(imageStream), {})); |
| commandQueue->setViewModelInstanceImage(tester.m_handle, |
| "Test Image", |
| imageHandle); |
| |
| commandQueue->runOnce( |
| [imageHandle, handle = tester.m_handle](CommandServer* server) { |
| auto image = server->getImage(imageHandle); |
| CHECK(image != nullptr); |
| auto viewModel = server->getViewModelInstance(handle); |
| CHECK(viewModel != nullptr); |
| auto imageProperty = viewModel->propertyImage("Test Image"); |
| CHECK(imageProperty != nullptr); |
| CHECK(imageProperty->testing_value() == image); |
| }); |
| |
| // Same for artboards. |
| auto bindableArtboardHandle = |
| commandQueue->instantiateDefaultArtboard(fileHandle); |
| commandQueue->setViewModelInstanceArtboard(tester.m_handle, |
| "Test Artboard", |
| bindableArtboardHandle); |
| |
| commandQueue->runOnce([bindableArtboardHandle, |
| handle = tester.m_handle](CommandServer* server) { |
| auto bindableArtboard = |
| server->getBindableArtboard(bindableArtboardHandle); |
| CHECK(bindableArtboard != nullptr); |
| auto viewModel = server->getViewModelInstance(handle); |
| CHECK(viewModel != nullptr); |
| auto artboardProperty = viewModel->propertyArtboard("Test Artboard"); |
| CHECK(artboardProperty != nullptr); |
| CHECK(artboardProperty->testing_value() == bindableArtboard); |
| }); |
| commandQueue->deleteArtboard(bindableArtboardHandle); |
| |
| auto badImageHandle = |
| commandQueue->decodeImage(std::vector<uint8_t>(1024 * 1024, {})); |
| commandQueue->setViewModelInstanceImage(tester.m_handle, |
| "Test Image", |
| badImageHandle); |
| ++tester.m_expectedErrors; |
| |
| auto badArtboardHandle = |
| commandQueue->instantiateArtboardNamed(fileHandle, "Blah"); |
| commandQueue->setViewModelInstanceArtboard(tester.m_handle, |
| "Test Artboard", |
| badArtboardHandle); |
| ++tester.m_expectedErrors; |
| |
| commandQueue->runOnce([imageHandle, |
| badImageHandle, |
| handle = tester.m_handle](CommandServer* server) { |
| auto image = server->getImage(imageHandle); |
| auto badImage = server->getImage(badImageHandle); |
| CHECK(image != nullptr); |
| CHECK(badImage == nullptr); |
| auto viewModel = server->getViewModelInstance(handle); |
| CHECK(viewModel != nullptr); |
| auto imageProperty = viewModel->propertyImage("Test Image"); |
| CHECK(imageProperty != nullptr); |
| CHECK(imageProperty->testing_value() == image); |
| }); |
| |
| commandQueue->setViewModelInstanceImage(tester.m_handle, |
| "Blah", |
| imageHandle); |
| // Account for bad image request. |
| ++tester.m_expectedErrors; |
| |
| commandQueue->setViewModelInstanceArtboard(tester.m_handle, |
| "Blah", |
| artboardHandle); |
| // Account for bad artboard request. |
| ++tester.m_expectedErrors; |
| |
| commandQueue->runOnce( |
| [imageHandle, handle = tester.m_handle](CommandServer* server) { |
| auto image = server->getImage(imageHandle); |
| CHECK(image != nullptr); |
| auto viewModel = server->getViewModelInstance(handle); |
| CHECK(viewModel != nullptr); |
| auto imageProperty = viewModel->propertyImage("Test Image"); |
| CHECK(imageProperty != nullptr); |
| CHECK(imageProperty->testing_value() == image); |
| }); |
| |
| // We should set / get in order as it goes through the list. |
| for (int i = 0; i < 10; ++i) |
| { |
| tester.pushExpectation(commandQueue.get(), |
| "Test Bool", |
| static_cast<bool>(i % 2)); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Num", |
| static_cast<float>(i)); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Nested", |
| i % 2 ? blankHandle : alternateHandle); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Color", |
| rive::colorARGB(i, i, i, i)); |
| tester.pushEnumExpectation(commandQueue.get(), |
| "Test Enum", |
| i % 2 ? "Value 2" : "Value 1"); |
| tester.pushStringExpectation(commandQueue.get(), |
| "Test String", |
| std::to_string(i)); |
| } |
| |
| // Bad values |
| |
| commandQueue->deleteViewModelInstance(blankHandle); |
| commandQueue->deleteViewModelInstance(alternateHandle); |
| |
| // Good property path bad value |
| tester.pushBadEnumExpectation(commandQueue.get(), "Test Enum", "Blah"); |
| tester.pushBadExpectation(commandQueue.get(), "Test Nested", blankHandle); |
| |
| // Bad everything |
| tester.pushBadExpectation(commandQueue.get(), "Blah", true); |
| tester.pushBadExpectation(commandQueue.get(), "Blah", 10.0f); |
| tester.pushBadExpectation(commandQueue.get(), "Blah", alternateHandle); |
| tester.pushBadExpectation(commandQueue.get(), |
| "Blah", |
| rive::colorARGB(255, 255, 0, 0)); |
| tester.pushBadEnumExpectation(commandQueue.get(), "Blah", "Value 2"); |
| tester.pushBadStringExpectation(commandQueue.get(), "Blah", "Some String"); |
| |
| // Delete the instance, the callback should continue fine and a delete |
| // should be received |
| commandQueue->deleteViewModelInstance(tester.m_handle); |
| |
| // Call sets on deleted handle |
| tester.pushBadExpectation(commandQueue.get(), "Test Bool", true); |
| tester.pushBadExpectation(commandQueue.get(), "Test Num", 10.0f); |
| tester.pushBadExpectation(commandQueue.get(), |
| "Test Color", |
| rive::colorARGB(255, 255, 0, 0)); |
| tester.pushBadEnumExpectation(commandQueue.get(), "Test Enum", "Value 2"); |
| tester.pushBadStringExpectation(commandQueue.get(), |
| "Test String", |
| "Some String"); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| // We expect there should be no expected values left |
| CHECK(tester.m_expectedData.size() == 0); |
| CHECK(tester.m_expectedRequestIds.size() == 0); |
| |
| CHECK(tester.m_expectedErrors == tester.m_receivedErrors); |
| |
| // We should have received the deleted event |
| CHECK(tester.n_wasDeleted); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class ViewModelPropertySubscriptionListener |
| : public CommandQueue::ViewModelInstanceListener |
| { |
| public: |
| virtual void onViewModelInstanceError(const ViewModelInstanceHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(error.size()); |
| ++m_receivedErrors; |
| } |
| |
| virtual void onViewModelDeleted(const ViewModelInstanceHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(!n_wasDeleted); |
| n_wasDeleted = true; |
| } |
| |
| virtual void onViewModelDataReceived( |
| const ViewModelInstanceHandle handle, |
| uint64_t requestId, |
| CommandQueue::ViewModelInstanceData data) override |
| { |
| // We only get one sub callback per value, so instead of a dequeue we |
| // use a map of names to values that we expect, it should always be the |
| // last value set. |
| CHECK(m_expectedData.size()); |
| auto itr = m_expectedData.find(data.metaData.name); |
| CHECK(itr != m_expectedData.end()); |
| auto& expectedData = itr->second; |
| |
| CHECK(handle == m_handle); |
| CHECK(data == expectedData); |
| |
| ++m_receivedCallbacks; |
| } |
| |
| void pushTriggerExpectation(CommandQueue* queue, std::string name) |
| { |
| ++m_requestIdx; |
| m_expectedData[name] = {.metaData = {DataType::trigger, name}}; |
| queue->fireViewModelTrigger(m_handle, name, m_requestIdx); |
| } |
| |
| void pushExpectation(CommandQueue* queue, std::string name, float value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceNumber(m_handle, name, value, m_requestIdx); |
| m_expectedData[name] = {.metaData = {DataType::number, name}, |
| .numberValue = value}; |
| } |
| |
| void pushExpectation(CommandQueue* queue, std::string name, ColorInt value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceColor(m_handle, name, value, m_requestIdx); |
| m_expectedData[name] = {.metaData = {DataType::color, name}, |
| .colorValue = value}; |
| } |
| |
| void pushExpectation(CommandQueue* queue, std::string name, bool value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceBool(m_handle, name, value, m_requestIdx); |
| m_expectedData[name] = {.metaData = {DataType::boolean, name}, |
| .boolValue = value}; |
| } |
| |
| void pushExpectation(CommandQueue* queue, |
| std::string name, |
| ViewModelInstanceHandle value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceNestedViewModel(m_handle, |
| name, |
| value, |
| m_requestIdx); |
| // there is no subscription for view models, you must subscribe to the |
| // nested property instead |
| } |
| |
| void pushListExpectation(CommandQueue* queue, |
| std::string name, |
| ViewModelInstanceHandle value) |
| { |
| ++m_requestIdx; |
| queue->appendViewModelInstanceListViewModel(m_handle, |
| name, |
| value, |
| m_requestIdx); |
| // there is no value for view models list subscription callbacks |
| m_expectedData[name] = {.metaData = {DataType::viewModel, name}}; |
| } |
| |
| void pushStringExpectation(CommandQueue* queue, |
| std::string name, |
| std::string value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceString(m_handle, name, value, m_requestIdx); |
| m_expectedData[name] = {.metaData = {DataType::string, name}, |
| .stringValue = value}; |
| } |
| |
| void pushEnumExpectation(CommandQueue* queue, |
| std::string name, |
| std::string value) |
| { |
| ++m_requestIdx; |
| queue->setViewModelInstanceEnum(m_handle, name, value, m_requestIdx); |
| m_expectedData[name] = {.metaData = {DataType::enumType, name}, |
| .stringValue = value}; |
| } |
| |
| void pushExpectation(CommandQueue* queue, |
| std::string name, |
| RenderImageHandle value) |
| { |
| queue->setViewModelInstanceImage(m_handle, "Test Image", value); |
| // no value for image subscriptions |
| m_expectedData[name] = {.metaData = {DataType::assetImage, name}}; |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, std::string name, float value) |
| { |
| queue->setViewModelInstanceNumber(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, |
| std::string name, |
| ColorInt value) |
| { |
| queue->setViewModelInstanceColor(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, std::string name, bool value) |
| { |
| queue->setViewModelInstanceBool(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, |
| std::string name, |
| RenderImageHandle value) |
| { |
| queue->setViewModelInstanceImage(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadExpectation(CommandQueue* queue, |
| std::string name, |
| ViewModelInstanceHandle value) |
| { |
| queue->setViewModelInstanceNestedViewModel(m_handle, |
| name, |
| value, |
| m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadStringExpectation(CommandQueue* queue, |
| std::string name, |
| std::string value) |
| { |
| queue->setViewModelInstanceString(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadEnumExpectation(CommandQueue* queue, |
| std::string name, |
| std::string value) |
| { |
| queue->setViewModelInstanceEnum(m_handle, name, value, m_requestIdx); |
| ++m_expectedErrors; |
| } |
| |
| void pushBadTriggerExpectation(CommandQueue* queue, std::string name) |
| { |
| queue->fireViewModelTrigger(m_handle, name); |
| ++m_expectedErrors; |
| } |
| |
| // One data and id per callback. |
| std::unordered_map<std::string, CommandQueue::ViewModelInstanceData> |
| m_expectedData; |
| // All of the handles should be the same. |
| ViewModelInstanceHandle m_handle; |
| bool n_wasDeleted = false; |
| |
| uint64_t m_requestIdx = 1; |
| int m_receivedCallbacks = 0; |
| |
| size_t m_expectedErrors = 0; |
| size_t m_receivedErrors = 0; |
| }; |
| |
| TEST_CASE("View Model Property Subscriptions", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| // The subscriptions happens once at the end of processCommands, so it's not |
| // order dependent, that makes it more difficult to test. To get around |
| // this, we just make the server on the same thread. Different tests will be |
| // used for testing async. |
| CommandServer server(commandQueue, nullContext.get()); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| ViewModelPropertySubscriptionListener tester; |
| |
| auto artboardHandle = commandQueue->instantiateDefaultArtboard(fileHandle); |
| tester.m_handle = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| artboardHandle, |
| &tester); |
| |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Nested/Nested Number", |
| DataType::number); |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Bool", |
| DataType::boolean); |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Num", |
| DataType::number); |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Color", |
| DataType::color); |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Enum", |
| DataType::enumType); |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test String", |
| DataType::string); |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Trigger", |
| DataType::trigger); |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test List", |
| DataType::list); |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Image", |
| DataType::assetImage); |
| |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Bad property", |
| DataType::assetImage); |
| ++tester.m_expectedErrors; |
| |
| // bad type |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Image", |
| DataType::integer); |
| ++tester.m_expectedErrors; |
| |
| commandQueue->runOnce([](CommandServer* server) { |
| auto subs = server->testing_getSubsciptions(); |
| CHECK(subs.size() == 9); |
| }); |
| |
| auto blankHandle = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, |
| "Nested VM"); |
| auto alternateHandle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Nested VM", |
| "Alternate Nested"); |
| |
| tester.pushExpectation(commandQueue.get(), "Test Bool", true); |
| tester.pushExpectation(commandQueue.get(), "Test Num", 10.0f); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Nested/Nested Number", |
| 10.0f); |
| tester.pushExpectation(commandQueue.get(), "Test Nested", blankHandle); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Nested/Nested Number", |
| 10.0f); |
| |
| tester.pushExpectation(commandQueue.get(), |
| "Test Color", |
| rive::colorARGB(255, 255, 0, 0)); |
| tester.pushEnumExpectation(commandQueue.get(), "Test Enum", "Value 2"); |
| tester.pushStringExpectation(commandQueue.get(), |
| "Test String", |
| "Some String"); |
| |
| tester.pushTriggerExpectation(commandQueue.get(), "Test Trigger"); |
| |
| std::ifstream imageStream("assets/batdude.png", std::ios::binary); |
| auto imageHandle = commandQueue->decodeImage( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(imageStream), {})); |
| tester.pushExpectation(commandQueue.get(), "Test Image", imageHandle); |
| |
| for (int i = 0; i < 10; ++i) |
| { |
| tester.pushExpectation(commandQueue.get(), |
| "Test Bool", |
| static_cast<bool>(i % 2)); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Num", |
| static_cast<float>(i)); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Nested", |
| i % 2 ? blankHandle : alternateHandle); |
| tester.pushExpectation(commandQueue.get(), |
| "Test Color", |
| rive::colorARGB(i, i, i, i)); |
| tester.pushEnumExpectation(commandQueue.get(), |
| "Test Enum", |
| i % 2 ? "Value 2" : "Value 1"); |
| tester.pushStringExpectation(commandQueue.get(), |
| "Test String", |
| std::to_string(i)); |
| } |
| |
| // Bad values |
| |
| auto badImageHandle = |
| commandQueue->decodeImage(std::vector<uint8_t>(1024 * 1024, {})); |
| |
| tester.pushBadExpectation(commandQueue.get(), "Test Image", badImageHandle); |
| |
| commandQueue->deleteViewModelInstance(blankHandle); |
| commandQueue->deleteViewModelInstance(alternateHandle); |
| |
| // Good property path bad value |
| tester.pushBadEnumExpectation(commandQueue.get(), "Test Enum", "Blah"); |
| tester.pushBadExpectation(commandQueue.get(), "Test Nested", blankHandle); |
| |
| // Bad everything |
| tester.pushBadExpectation(commandQueue.get(), "Blah", true); |
| tester.pushBadExpectation(commandQueue.get(), "Blah", 10.0f); |
| tester.pushBadExpectation(commandQueue.get(), "Blah", alternateHandle); |
| tester.pushBadExpectation(commandQueue.get(), |
| "Blah", |
| rive::colorARGB(255, 255, 0, 0)); |
| tester.pushBadEnumExpectation(commandQueue.get(), "Blah", "Value 2"); |
| tester.pushBadStringExpectation(commandQueue.get(), "Blah", "Some String"); |
| |
| tester.pushBadTriggerExpectation(commandQueue.get(), "Blah"); |
| |
| // Bad handle for trigger test. |
| commandQueue->fireViewModelTrigger(0, "Blah"); |
| |
| server.processCommands(); |
| commandQueue->processMessages(); |
| |
| // Once the handle is delted subscriptions will stop, so we do this after |
| // processMessages. |
| commandQueue->deleteViewModelInstance(tester.m_handle); |
| |
| // Call sets on deleted handle |
| tester.pushBadExpectation(commandQueue.get(), "Test Bool", true); |
| tester.pushBadExpectation(commandQueue.get(), "Test Num", 10.0f); |
| tester.pushBadExpectation(commandQueue.get(), |
| "Test Color", |
| rive::colorARGB(255, 255, 0, 0)); |
| tester.pushBadEnumExpectation(commandQueue.get(), "Test Enum", "Value 2"); |
| tester.pushBadStringExpectation(commandQueue.get(), |
| "Test String", |
| "Some String"); |
| server.processCommands(); |
| commandQueue->processMessages(); |
| |
| // We should have received the deleted event. |
| CHECK(tester.n_wasDeleted); |
| // We should have received the same number of callbacks as entries in the |
| // map. |
| CHECK(tester.m_receivedCallbacks == tester.m_expectedData.size()); |
| |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test Nested/Nested Number", |
| DataType::number); |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test Bool", |
| DataType::boolean); |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test Num", |
| DataType::number); |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test Color", |
| DataType::color); |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test Enum", |
| DataType::enumType); |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test String", |
| DataType::string); |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test Trigger", |
| DataType::trigger); |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test List", |
| DataType::list); |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test Image", |
| DataType::assetImage); |
| // Unsub something that doesn't exist. |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Blah", |
| DataType::boolean); |
| // Note we don't increment tester.m_expectedErrors because unsubing |
| // something invalid is ok to do, we just ignore it. |
| |
| commandQueue->runOnce([](CommandServer* server) { |
| auto subs = server->testing_getSubsciptions(); |
| CHECK(subs.empty()); |
| }); |
| |
| server.processCommands(); |
| commandQueue->processMessages(); |
| |
| CHECK(tester.m_receivedErrors == tester.m_expectedErrors); |
| |
| commandQueue->disconnect(); |
| } |
| |
| class AsyncSubListener : public CommandQueue::ViewModelInstanceListener |
| { |
| public: |
| virtual void onViewModelDataReceived( |
| const ViewModelInstanceHandle handle, |
| uint64_t requestId, |
| CommandQueue::ViewModelInstanceData data) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(!m_hasCallback); |
| m_hasCallback = true; |
| // we are just expecting the one sub so the value should be correct |
| CHECK(data.numberValue == 10); |
| } |
| |
| ViewModelInstanceHandle m_handle; |
| bool m_hasCallback = false; |
| }; |
| |
| // The above tests are to check that all subscription types work and that values |
| // come through correctly This is just checking to make sure subscriptions work |
| // while the server is in a seperate thread but we don't care about the exact |
| // callback happening since that is tested above |
| |
| TEST_CASE("View Model Property Async Subscriptions", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| AsyncSubListener tester; |
| |
| auto artboardHandle = commandQueue->instantiateDefaultArtboard(fileHandle); |
| tester.m_handle = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| artboardHandle, |
| &tester); |
| |
| commandQueue->subscribeToViewModelProperty(tester.m_handle, |
| "Test Num", |
| DataType::number); |
| |
| commandQueue->setViewModelInstanceNumber(tester.m_handle, "Test Num", 10); |
| |
| commandQueue->runOnce([](CommandServer* server) { |
| auto subs = server->testing_getSubsciptions(); |
| CHECK(subs.size() == 1); |
| }); |
| |
| commandQueue->testing_commandLoopBreak(); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(tester.m_hasCallback); |
| |
| commandQueue->unsubscribeToViewModelProperty(tester.m_handle, |
| "Test Num", |
| DataType::number); |
| |
| commandQueue->runOnce([](CommandServer* server) { |
| auto subs = server->testing_getSubsciptions(); |
| CHECK(subs.empty()); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class ListViewModelPropertyListener |
| : public CommandQueue::ViewModelInstanceListener |
| { |
| public: |
| ListViewModelPropertyListener(rcp<CommandQueue> queue) : |
| m_queue(std::move(queue)) |
| {} |
| |
| virtual void onViewModelListSizeReceived( |
| const ViewModelInstanceHandle handle, |
| uint64_t requestId, |
| std::string path, |
| size_t size) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(path == m_path); |
| CHECK(size == m_expectedSize); |
| CHECK(requestId == m_requestIdx); |
| m_receivedSize = true; |
| } |
| |
| virtual void onViewModelDeleted(const ViewModelInstanceHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(!n_wasDeleted); |
| n_wasDeleted = true; |
| } |
| |
| void pushRequestExpectation(std::string path, size_t expectedSize) |
| { |
| m_path = path; |
| m_expectedSize = expectedSize; |
| ++m_requestIdx; |
| m_receivedSize = false; |
| m_queue->requestViewModelInstanceListSize(m_handle, |
| m_path, |
| m_requestIdx); |
| |
| wait_for_server(m_queue.get()); |
| m_queue->processMessages(); |
| CHECK(m_receivedSize); |
| } |
| |
| void pushExpectation(std::string name, |
| ViewModelInstanceHandle expectedValueAtIndex1, |
| ViewModelInstanceHandle expectedValueAtIndex2, |
| int index, |
| int index2) |
| { |
| m_queue->swapViewModelInstanceListValues(m_handle, name, index, index2); |
| m_queue->runOnce([handle = m_handle, |
| name, |
| expectedValueAtIndex1, |
| expectedValueAtIndex2, |
| index, |
| index2](CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| CHECK(instance != nullptr); |
| auto value1Instance = |
| server->getViewModelInstance(expectedValueAtIndex1); |
| CHECK(value1Instance != nullptr); |
| auto value2Instance = |
| server->getViewModelInstance(expectedValueAtIndex2); |
| CHECK(value2Instance != nullptr); |
| auto property = instance->propertyList(name); |
| CHECK(property != nullptr); |
| CHECK(property->instanceAt(index).get() == value1Instance); |
| CHECK(property->instanceAt(index2).get() == value2Instance); |
| }); |
| } |
| |
| void pushExpectation(std::string name, |
| ViewModelInstanceHandle value, |
| int index) |
| { |
| m_queue->insertViewModelInstanceListViewModel(m_handle, |
| name, |
| value, |
| index); |
| m_queue->runOnce( |
| [handle = m_handle, name, value, index](CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| CHECK(instance != nullptr); |
| auto property = instance->propertyList(name); |
| CHECK(property != nullptr); |
| auto valueModel = server->getViewModelInstance(value); |
| CHECK(property->instanceAt(index).get() == valueModel); |
| }); |
| } |
| |
| void pushExpectation(std::string name, ViewModelInstanceHandle value) |
| { |
| m_queue->appendViewModelInstanceListViewModel(m_handle, name, value); |
| m_queue->runOnce([handle = m_handle, name, value]( |
| CommandServer* server) { |
| auto instance = server->getViewModelInstance(handle); |
| CHECK(instance != nullptr); |
| auto property = instance->propertyList(name); |
| CHECK(property != nullptr); |
| auto valueModel = server->getViewModelInstance(value); |
| CHECK(property->instanceAt(static_cast<int>(property->size()) - 1) |
| .get() == valueModel); |
| }); |
| } |
| |
| void pushBadExpectation(std::string name, |
| ViewModelInstanceHandle value, |
| int index) |
| { |
| m_queue->insertViewModelInstanceListViewModel(m_handle, |
| name, |
| value, |
| index); |
| } |
| |
| void pushBadExpectation(std::string name, ViewModelInstanceHandle value) |
| { |
| m_queue->appendViewModelInstanceListViewModel(m_handle, name, value); |
| } |
| |
| void pushBadExpectation(std::string name, int index, int index2) |
| { |
| m_queue->swapViewModelInstanceListValues(m_handle, name, index, index2); |
| } |
| |
| void pushBadRequestExpectation(std::string name) |
| { |
| m_receivedSize = false; |
| m_queue->requestViewModelInstanceListSize(m_handle, name); |
| |
| wait_for_server(m_queue.get()); |
| m_queue->processMessages(); |
| CHECK(!m_receivedSize); |
| } |
| |
| rcp<CommandQueue> m_queue; |
| ViewModelInstanceHandle m_handle; |
| bool n_wasDeleted = false; |
| // size callback test values |
| |
| std::string m_path; |
| size_t m_expectedSize; |
| uint64_t m_requestIdx = 1; |
| bool m_receivedSize = false; |
| }; |
| |
| TEST_CASE("List View Model Property Set/Get", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| ListViewModelPropertyListener tester(commandQueue); |
| auto artboardHandle = commandQueue->instantiateDefaultArtboard(fileHandle); |
| tester.m_handle = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| artboardHandle, |
| &tester); |
| auto blankHandle = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, |
| "Nested VM"); |
| auto alternateHandle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Nested VM", |
| "Alternate Nested"); |
| tester.pushExpectation("Test List", blankHandle); |
| tester.pushExpectation("Test List", alternateHandle); |
| tester.pushExpectation("Test List", alternateHandle, blankHandle, 2, 3); |
| tester.pushRequestExpectation("Test List", 4); |
| tester.pushExpectation("Test List", blankHandle, 0); |
| tester.pushExpectation("Test List", alternateHandle, 0); |
| tester.pushExpectation("Test List", blankHandle, alternateHandle, 0, 1); |
| tester.pushRequestExpectation("Test List", 6); |
| |
| auto badBlankHandle = |
| commandQueue->instantiateBlankViewModelInstance(fileHandle, "blah"); |
| auto badAlternateHandle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Nested VM", |
| "blah"); |
| tester.pushBadExpectation("Test List", badBlankHandle); |
| tester.pushBadExpectation("Test List", badAlternateHandle); |
| tester.pushBadExpectation("Test List", badBlankHandle, 0); |
| tester.pushBadExpectation("Test List", badAlternateHandle, 0); |
| |
| tester.pushBadExpectation("blah", blankHandle); |
| tester.pushBadExpectation("blah", alternateHandle); |
| tester.pushBadExpectation("blah", blankHandle, 0); |
| tester.pushBadExpectation("blah", alternateHandle, 0); |
| |
| tester.pushBadExpectation("Test List", 10, 1); |
| tester.pushBadExpectation("Test List", 0, 10); |
| tester.pushBadExpectation("Blah", 0, 1); |
| tester.pushBadExpectation("Blah", 10, 1); |
| tester.pushBadExpectation("Blah", 0, 10); |
| |
| tester.pushRequestExpectation("Test List", 6); |
| |
| tester.pushBadRequestExpectation("Blah"); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestFileErrorListener : public CommandQueue::FileListener |
| { |
| public: |
| virtual void onFileError(const FileHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(error.size()); |
| ++m_receivedErrors; |
| } |
| |
| FileHandle m_handle; |
| size_t m_receivedErrors = 0; |
| }; |
| |
| TEST_CASE("file Error Messages", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| TestFileErrorListener fileListener; |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| fileListener.m_handle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &fileListener); |
| |
| commandQueue->instantiateArtboardNamed(fileListener.m_handle, "Blah"); |
| commandQueue->instantiateViewModelInstanceNamed(fileListener.m_handle, |
| "Test All", |
| "blah"); |
| commandQueue->instantiateViewModelInstanceNamed(fileListener.m_handle, |
| "blah", |
| "blah"); |
| commandQueue->instantiateViewModelInstanceNamed(fileListener.m_handle, |
| nullptr, |
| "blah"); |
| |
| commandQueue->instantiateDefaultViewModelInstance(fileListener.m_handle, |
| "Blah"); |
| commandQueue->instantiateDefaultViewModelInstance(fileListener.m_handle, |
| nullptr); |
| |
| commandQueue->instantiateBlankViewModelInstance(fileListener.m_handle, |
| "Blah"); |
| commandQueue->instantiateBlankViewModelInstance(fileListener.m_handle, |
| nullptr); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(fileListener.m_receivedErrors == 8); |
| |
| TestFileErrorListener badFileListener; |
| badFileListener.m_handle = |
| commandQueue->loadFile(std::vector<uint8_t>(100 * 1024, 0), |
| &badFileListener); |
| |
| commandQueue->instantiateDefaultArtboard(badFileListener.m_handle); |
| commandQueue->instantiateDefaultViewModelInstance(badFileListener.m_handle, |
| nullptr); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| CHECK(badFileListener.m_receivedErrors == 3); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestFileListener : public CommandQueue::FileListener |
| { |
| public: |
| virtual void onArtboardsListed( |
| const FileHandle handle, |
| uint64_t requestId, |
| std::vector<std::string> artboardNames) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| CHECK(artboardNames.size() == m_artboardNames.size()); |
| for (auto i = 0; i < artboardNames.size(); ++i) |
| { |
| CHECK(artboardNames[i] == m_artboardNames[i]); |
| } |
| |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId; |
| FileHandle m_handle; |
| std::vector<std::string> m_artboardNames; |
| bool m_hasCallback = false; |
| }; |
| |
| TEST_CASE("listArtboard", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| TestFileListener fileListener; |
| |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| FileHandle goodFile = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &fileListener); |
| |
| fileListener.m_artboardNames = {"New Artboard", "New Artboard"}; |
| fileListener.m_handle = goodFile; |
| |
| fileListener.m_requestId = 0x40; |
| commandQueue->requestArtboardNames(goodFile, fileListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(fileListener.m_hasCallback); |
| |
| FileHandle badFile = |
| commandQueue->loadFile(std::vector<uint8_t>(100 * 1024, 0)); |
| fileListener.m_handle = goodFile; |
| fileListener.m_hasCallback = false; |
| |
| commandQueue->requestArtboardNames(badFile); |
| |
| wait_for_server(commandQueue.get()); |
| |
| CHECK(!fileListener.m_hasCallback); |
| |
| commandQueue->processMessages(); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestFileEnumListener : public CommandQueue::FileListener |
| { |
| public: |
| virtual void onViewModelEnumsListed(const FileHandle handle, |
| uint64_t requestId, |
| std::vector<ViewModelEnum> enums) |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| CHECK(enums.size() == m_enums.size()); |
| for (auto i = 0; i < enums.size(); ++i) |
| { |
| CHECK(enums[i].name == m_enums[i].name); |
| for (auto k = 0; k < enums[i].enumerants.size(); ++k) |
| { |
| CHECK(enums[i].enumerants[k] == m_enums[i].enumerants[k]); |
| } |
| } |
| |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId; |
| FileHandle m_handle; |
| std::array<ViewModelEnum, 1> m_enums = { |
| ViewModelEnum{"Test Enum Values", {"Value 1", "Value 2"}}}; |
| bool m_hasCallback = false; |
| }; |
| |
| TEST_CASE("listEnums", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| TestFileEnumListener fileListener; |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle goodFile = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &fileListener); |
| |
| fileListener.m_handle = goodFile; |
| |
| fileListener.m_requestId = 0x40; |
| commandQueue->requestViewModelEnums(goodFile, fileListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(fileListener.m_hasCallback); |
| |
| FileHandle badFile = |
| commandQueue->loadFile(std::vector<uint8_t>(100 * 1024, 0)); |
| fileListener.m_handle = goodFile; |
| fileListener.m_hasCallback = false; |
| |
| commandQueue->requestViewModelEnums(badFile); |
| |
| wait_for_server(commandQueue.get()); |
| |
| CHECK(!fileListener.m_hasCallback); |
| |
| commandQueue->processMessages(); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestRenderImageErrorListener : public CommandQueue::RenderImageListener |
| { |
| public: |
| virtual void onRenderImageError(const RenderImageHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(error.size()); |
| CHECK(!m_hasCallback); |
| m_hasCallback = true; |
| } |
| |
| RenderImageHandle m_handle; |
| bool m_hasCallback = false; |
| }; |
| |
| class TestAudioSourceErrorListener : public CommandQueue::AudioSourceListener |
| { |
| public: |
| virtual void onAudioSourceError(const AudioSourceHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(error.size()); |
| CHECK(!m_hasCallback); |
| m_hasCallback = true; |
| } |
| |
| AudioSourceHandle m_handle; |
| bool m_hasCallback = false; |
| }; |
| |
| class TestFontErrorListener : public CommandQueue::FontListener |
| { |
| public: |
| virtual void onFontError(const FontHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(error.size()); |
| CHECK(!m_hasCallback); |
| m_hasCallback = true; |
| } |
| |
| FontHandle m_handle; |
| bool m_hasCallback = false; |
| }; |
| |
| TEST_CASE("render image / audio source / font error", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| TestRenderImageErrorListener imageListener; |
| TestAudioSourceErrorListener audioListener; |
| TestFontErrorListener fontListener; |
| |
| imageListener.m_handle = |
| commandQueue->decodeImage(std::vector<uint8_t>(1024, 0), |
| &imageListener); |
| audioListener.m_handle = |
| commandQueue->decodeAudio(std::vector<uint8_t>(1024, 0), |
| &audioListener); |
| fontListener.m_handle = |
| commandQueue->decodeFont(std::vector<uint8_t>(1024, 0), &fontListener); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(imageListener.m_hasCallback); |
| CHECK(audioListener.m_hasCallback); |
| CHECK(fontListener.m_hasCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestStateMachineErrorListener : public CommandQueue::StateMachineListener |
| { |
| public: |
| virtual void onStateMachineError(const StateMachineHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(error.size()); |
| ++m_receivedErrors; |
| } |
| |
| StateMachineHandle m_handle; |
| size_t m_receivedErrors = 0; |
| }; |
| |
| TEST_CASE("state machine error", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| TestStateMachineErrorListener listener; |
| |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| auto artboardHandle = commandQueue->instantiateDefaultArtboard(fileHandle); |
| |
| listener.m_handle = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle, &listener); |
| commandQueue->bindViewModelInstance(listener.m_handle, nullptr); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(listener.m_receivedErrors == 1); |
| |
| TestStateMachineErrorListener badListener; |
| badListener.m_handle = |
| commandQueue->instantiateDefaultStateMachine(nullptr, &badListener); |
| |
| commandQueue->advanceStateMachine(badListener.m_handle, 0); |
| commandQueue->pointerDown(badListener.m_handle, {}); |
| commandQueue->pointerExit(badListener.m_handle, {}); |
| commandQueue->pointerUp(badListener.m_handle, {}); |
| commandQueue->pointerMove(badListener.m_handle, {}); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(badListener.m_receivedErrors == 5); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestArtboardErrorListener : public CommandQueue::ArtboardListener |
| { |
| public: |
| virtual void onArtboardError(const ArtboardHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(handle == m_handle); |
| CHECK(error.size()); |
| ++m_receivedErrors; |
| } |
| |
| size_t m_receivedErrors = 0; |
| ArtboardHandle m_handle; |
| }; |
| |
| TEST_CASE("artboard errors", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| TestArtboardErrorListener artboardListener; |
| |
| artboardListener.m_handle = |
| commandQueue->instantiateArtboardNamed(fileHandle, |
| "New Artboard", |
| &artboardListener); |
| |
| commandQueue->instantiateStateMachineNamed(artboardListener.m_handle, |
| "Blah"); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(artboardListener.m_receivedErrors == 1); |
| |
| TestArtboardErrorListener badArtboardListener; |
| badArtboardListener.m_handle = |
| commandQueue->instantiateArtboardNamed(fileHandle, |
| "Blah", |
| &badArtboardListener); |
| |
| commandQueue->requestStateMachineNames(badArtboardListener.m_handle); |
| commandQueue->instantiateDefaultStateMachine(badArtboardListener.m_handle); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(badArtboardListener.m_receivedErrors == 2); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestArtboardListener : public CommandQueue::ArtboardListener |
| { |
| public: |
| virtual void onStateMachinesListed( |
| const ArtboardHandle handle, |
| uint64_t requestId, |
| std::vector<std::string> stateMachineNames) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| CHECK(stateMachineNames.size() == m_stateMachineNames.size()); |
| for (auto i = 0; i < stateMachineNames.size(); ++i) |
| { |
| CHECK(stateMachineNames[i] == m_stateMachineNames[i]); |
| } |
| |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId; |
| ArtboardHandle m_handle; |
| std::vector<std::string> m_stateMachineNames; |
| bool m_hasCallback = false; |
| }; |
| |
| TEST_CASE("listStateMachine", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| FileHandle goodFile = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| TestArtboardListener artboardListener; |
| |
| auto artboardHandle = |
| commandQueue->instantiateArtboardNamed(goodFile, |
| "New Artboard", |
| &artboardListener); |
| artboardListener.m_stateMachineNames = {"State Machine 1"}; |
| artboardListener.m_handle = artboardHandle; |
| |
| artboardListener.m_requestId = 0x40; |
| commandQueue->requestStateMachineNames(artboardHandle, |
| artboardListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(artboardListener.m_hasCallback); |
| |
| FileHandle badFile = |
| commandQueue->loadFile(std::vector<uint8_t>(100 * 1024, 0)); |
| |
| auto badArtbaord = commandQueue->instantiateDefaultArtboard(badFile); |
| |
| artboardListener.m_handle = badArtbaord; |
| artboardListener.m_hasCallback = false; |
| |
| artboardListener.m_requestId = 0x40; |
| commandQueue->requestStateMachineNames(badArtbaord, |
| artboardListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(!artboardListener.m_hasCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestArtboardDefaultViewModelListener |
| : public CommandQueue::ArtboardListener |
| { |
| public: |
| virtual void onArtboardError(const ArtboardHandle handle, |
| uint64_t requestId, |
| std::string error) override |
| { |
| CHECK(m_hasErrorCallback == false); |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasErrorCallback = true; |
| } |
| |
| virtual void onDefaultViewModelInfoReceived( |
| const ArtboardHandle handle, |
| uint64_t requestId, |
| std::string viewModelName, |
| std::string viewModelInstanceName) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| CHECK(viewModelName == m_expectedViewModel); |
| CHECK(viewModelInstanceName == m_expectedViewModelInstance); |
| |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId; |
| ArtboardHandle m_handle; |
| std::string m_expectedViewModel = "Test All"; |
| std::string m_expectedViewModelInstance = "Test Default"; |
| bool m_hasCallback = false; |
| bool m_hasErrorCallback = false; |
| }; |
| |
| TEST_CASE("requestDefaultViewModel", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle goodFile = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| TestArtboardDefaultViewModelListener artboardListener; |
| |
| auto artboardHandle = |
| commandQueue->instantiateArtboardNamed(goodFile, |
| "Test Artboard", |
| &artboardListener); |
| artboardListener.m_handle = artboardHandle; |
| |
| artboardListener.m_requestId = 0x40; |
| commandQueue->requestDefaultViewModelInfo(artboardHandle, |
| goodFile, |
| artboardListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(artboardListener.m_hasCallback); |
| CHECK(!artboardListener.m_hasErrorCallback); |
| |
| FileHandle badFile = |
| commandQueue->loadFile(std::vector<uint8_t>(100 * 1024, 0)); |
| |
| auto badArtbaord = commandQueue->instantiateDefaultArtboard(badFile); |
| |
| artboardListener.m_hasCallback = false; |
| |
| commandQueue->requestDefaultViewModelInfo(badArtbaord, |
| goodFile, |
| artboardListener.m_requestId); |
| commandQueue->requestDefaultViewModelInfo(artboardHandle, |
| badFile, |
| artboardListener.m_requestId); |
| commandQueue->requestDefaultViewModelInfo(badArtbaord, |
| badFile, |
| artboardListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(!artboardListener.m_hasCallback); |
| CHECK(artboardListener.m_hasErrorCallback); |
| |
| std::ifstream tstream("assets/entry.riv", std::ios::binary); |
| FileHandle noViewModelFile = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(tstream), {})); |
| |
| TestArtboardDefaultViewModelListener noViewModelListener; |
| noViewModelListener.m_handle = |
| commandQueue->instantiateDefaultArtboard(noViewModelFile, |
| &noViewModelListener); |
| |
| commandQueue->requestDefaultViewModelInfo(noViewModelListener.m_handle, |
| noViewModelFile, |
| noViewModelListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CHECK(artboardListener.m_hasErrorCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class TestStateMachineListener : public CommandQueue::StateMachineListener |
| { |
| public: |
| virtual void onStateMachineDeleted(const StateMachineHandle handle, |
| uint64_t requestId) |
| { |
| CHECK(m_handle == handle); |
| } |
| |
| virtual void onStateMachineSettled(const StateMachineHandle handle, |
| uint64_t requestId) |
| { |
| CHECK(m_handle == handle); |
| CHECK(m_requestId == requestId); |
| m_hasCallbck = true; |
| } |
| |
| StateMachineHandle m_handle = RIVE_NULL_HANDLE; |
| uint64_t m_requestId = 0; |
| bool m_hasCallbck = false; |
| }; |
| |
| TEST_CASE("bindViewModelInstance", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| CommandServer server(commandQueue, nullContext.get()); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| auto viewModelHandle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "Test All", |
| "Test Alternate"); |
| |
| auto artboard = commandQueue->instantiateDefaultArtboard(fileHandle); |
| auto stateMachineHandle = |
| commandQueue->instantiateDefaultStateMachine(artboard); |
| |
| commandQueue->bindViewModelInstance(stateMachineHandle, viewModelHandle); |
| |
| commandQueue->runOnce([stateMachineHandle, |
| viewModelHandle](CommandServer* server) { |
| auto stateMachine = server->getStateMachineInstance(stateMachineHandle); |
| CHECK(stateMachine != nullptr); |
| auto viewModel = server->getViewModelInstance(viewModelHandle); |
| CHECK(viewModel != nullptr); |
| |
| CHECK(stateMachine->artboard()->dataContext()->viewModelInstance() == |
| viewModel->instance()); |
| }); |
| |
| auto badInstanceHandle = |
| commandQueue->instantiateViewModelInstanceNamed(fileHandle, |
| "blah", |
| "Test Alternate"); |
| |
| auto badStateMachineHandle = |
| commandQueue->instantiateStateMachineNamed(artboard, "blah"); |
| |
| // every combo of good / bad handles |
| |
| commandQueue->bindViewModelInstance(stateMachineHandle, badInstanceHandle); |
| commandQueue->bindViewModelInstance(badStateMachineHandle, viewModelHandle); |
| commandQueue->bindViewModelInstance(badStateMachineHandle, |
| badInstanceHandle); |
| |
| server.processCommands(); |
| commandQueue->processMessages(); |
| |
| commandQueue->disconnect(); |
| } |
| |
| TEST_CASE("advanceStateMachine", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| CommandServer server(commandQueue, nullContext.get()); |
| |
| std::ifstream stream("assets/settler.riv", std::ios::binary); |
| FileHandle goodFile = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| |
| auto artboardHandle = commandQueue->instantiateDefaultArtboard(goodFile); |
| |
| TestStateMachineListener stateMachineListener; |
| stateMachineListener.m_handle = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle, |
| &stateMachineListener); |
| |
| commandQueue->advanceStateMachine(stateMachineListener.m_handle, 10); |
| commandQueue->advanceStateMachine(stateMachineListener.m_handle, |
| 10.0, |
| stateMachineListener.m_requestId); |
| // the last advance is what actually settles the statemachine, so we store |
| // that id. |
| |
| stateMachineListener.m_requestId = 0x50; |
| commandQueue->advanceStateMachine(stateMachineListener.m_handle, |
| 10.0, |
| stateMachineListener.m_requestId); |
| |
| server.processCommands(); |
| commandQueue->processMessages(); |
| |
| CHECK(stateMachineListener.m_hasCallbck); |
| |
| TestStateMachineListener badStateMachineListener; |
| |
| badStateMachineListener.m_handle = |
| commandQueue->instantiateStateMachineNamed(artboardHandle, |
| "blah blah", |
| &badStateMachineListener); |
| badStateMachineListener.m_requestId = 0x51; |
| commandQueue->advanceStateMachine(badStateMachineListener.m_handle, |
| 10, |
| badStateMachineListener.m_requestId); |
| |
| server.processCommands(); |
| commandQueue->processMessages(); |
| |
| CHECK(!badStateMachineListener.m_hasCallbck); |
| } |
| |
| class DeleteFileListener : public CommandQueue::FileListener |
| { |
| public: |
| virtual void onFileDeleted(const FileHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId; |
| FileHandle m_handle; |
| bool m_hasCallback = false; |
| }; |
| |
| class DeleteArtboardListener : public CommandQueue::ArtboardListener |
| { |
| public: |
| virtual void onArtboardDeleted(const ArtboardHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId; |
| ArtboardHandle m_handle; |
| bool m_hasCallback = false; |
| }; |
| |
| class DeleteStateMachineListener : public CommandQueue::StateMachineListener |
| { |
| public: |
| virtual void onStateMachineDeleted(const StateMachineHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId; |
| StateMachineHandle m_handle; |
| bool m_hasCallback = false; |
| }; |
| |
| class DeleteRenderImageListener : public CommandQueue::RenderImageListener |
| { |
| public: |
| virtual void onRenderImageDeleted(const RenderImageHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId; |
| RenderImageHandle m_handle; |
| bool m_hasCallback = false; |
| }; |
| |
| TEST_CASE("listenerDeleteCallbacks", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| DeleteFileListener fileListener; |
| DeleteArtboardListener artboardListener; |
| DeleteStateMachineListener stateMachineListener; |
| DeleteRenderImageListener renderImageListener; |
| |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| FileHandle goodFile = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &fileListener); |
| |
| auto artboardHandle = |
| commandQueue->instantiateArtboardNamed(goodFile, |
| "New Artboard", |
| &artboardListener); |
| |
| auto stateMachineHandle = |
| commandQueue->instantiateStateMachineNamed(artboardHandle, |
| "", |
| &stateMachineListener); |
| |
| std::ifstream imageStream("assets/batdude.png", std::ios::binary); |
| auto renderImage = commandQueue->decodeImage( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &renderImageListener); |
| |
| fileListener.m_handle = goodFile; |
| artboardListener.m_handle = artboardHandle; |
| stateMachineListener.m_handle = stateMachineHandle; |
| renderImageListener.m_handle = renderImage; |
| |
| CHECK(!fileListener.m_hasCallback); |
| CHECK(!artboardListener.m_hasCallback); |
| CHECK(!stateMachineListener.m_hasCallback); |
| CHECK(!renderImageListener.m_hasCallback); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(!fileListener.m_hasCallback); |
| CHECK(!artboardListener.m_hasCallback); |
| CHECK(!stateMachineListener.m_hasCallback); |
| CHECK(!renderImageListener.m_hasCallback); |
| |
| stateMachineListener.m_requestId = 0x50; |
| commandQueue->deleteStateMachine(stateMachineHandle, |
| stateMachineListener.m_requestId); |
| artboardListener.m_requestId = 0x51, |
| commandQueue->deleteArtboard(artboardHandle, artboardListener.m_requestId); |
| fileListener.m_requestId = 0x52; |
| commandQueue->deleteFile(goodFile, fileListener.m_requestId); |
| renderImageListener.m_requestId = 0x53; |
| commandQueue->deleteImage(renderImage, renderImageListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(fileListener.m_hasCallback); |
| CHECK(artboardListener.m_hasCallback); |
| CHECK(stateMachineListener.m_hasCallback); |
| CHECK(renderImageListener.m_hasCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class LoadedFileListener : public CommandQueue::FileListener |
| { |
| public: |
| virtual void onFileLoaded(const FileHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId = 0x10; |
| FileHandle m_handle = RIVE_NULL_HANDLE; |
| bool m_hasCallback = false; |
| }; |
| |
| TEST_CASE("fileLoadedCallback", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| |
| LoadedFileListener fileListener; |
| |
| fileListener.m_handle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &fileListener, |
| fileListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(fileListener.m_hasCallback); |
| |
| LoadedFileListener badFileListener; |
| badFileListener.m_handle = |
| commandQueue->loadFile(std::vector<uint8_t>(1024, {}), |
| &badFileListener, |
| badFileListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(!badFileListener.m_hasCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| class DecodedImageListener : public CommandQueue::RenderImageListener |
| { |
| public: |
| virtual void onRenderImageDecoded(const RenderImageHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId = 0x10; |
| RenderImageHandle m_handle = RIVE_NULL_HANDLE; |
| bool m_hasCallback = false; |
| }; |
| |
| class DecodedAudioListener : public CommandQueue::AudioSourceListener |
| { |
| public: |
| virtual void onAudioSourceDecoded(const AudioSourceHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId = 0x10; |
| AudioSourceHandle m_handle = RIVE_NULL_HANDLE; |
| bool m_hasCallback = false; |
| }; |
| |
| class DecodedFontListener : public CommandQueue::FontListener |
| { |
| public: |
| virtual void onFontDecoded(const FontHandle handle, |
| uint64_t requestId) override |
| { |
| CHECK(requestId == m_requestId); |
| CHECK(handle == m_handle); |
| m_hasCallback = true; |
| } |
| |
| uint64_t m_requestId = 0x10; |
| FontHandle m_handle = RIVE_NULL_HANDLE; |
| bool m_hasCallback = false; |
| }; |
| |
| TEST_CASE("decodedCallbacks", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| |
| DecodedImageListener imageListener; |
| DecodedAudioListener audioListener; |
| DecodedFontListener fontListener; |
| |
| std::ifstream imageStream("assets/batdude.png", std::ios::binary); |
| imageListener.m_handle = commandQueue->decodeImage( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(imageStream), {}), |
| &imageListener, |
| imageListener.m_requestId); |
| |
| std::ifstream audioStream("assets/audio/what.wav", std::ios::binary); |
| audioListener.m_handle = commandQueue->decodeAudio( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(audioStream), {}), |
| &audioListener, |
| audioListener.m_requestId); |
| |
| std::ifstream fontStream("assets/fonts/OpenSans-Italic.ttf", |
| std::ios::binary); |
| fontListener.m_handle = commandQueue->decodeFont( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(fontStream), {}), |
| &fontListener, |
| fontListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(imageListener.m_hasCallback); |
| CHECK(audioListener.m_hasCallback); |
| CHECK(fontListener.m_hasCallback); |
| |
| DecodedImageListener badimageListener; |
| DecodedAudioListener badaudioListener; |
| DecodedFontListener badfontListener; |
| |
| badimageListener.m_handle = |
| commandQueue->decodeImage(std::vector<uint8_t>(1024, {}), |
| &badimageListener, |
| badimageListener.m_requestId); |
| |
| badaudioListener.m_handle = |
| commandQueue->decodeAudio(std::vector<uint8_t>(1024, {}), |
| &badaudioListener, |
| badaudioListener.m_requestId); |
| |
| badfontListener.m_handle = |
| commandQueue->decodeFont(std::vector<uint8_t>(1024, {}), |
| &badfontListener, |
| badfontListener.m_requestId); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(!badimageListener.m_hasCallback); |
| CHECK(!badaudioListener.m_hasCallback); |
| CHECK(!badfontListener.m_hasCallback); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| TEST_CASE("listenerLifeTimes", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/entry.riv", std::ios::binary); |
| |
| CommandQueue::FileListener fileListener; |
| CommandQueue::ArtboardListener artboardListener; |
| CommandQueue::StateMachineListener stateMachineListener; |
| |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &fileListener); |
| |
| auto artboardHandle = |
| commandQueue->instantiateArtboardNamed(fileHandle, |
| "New Artboard", |
| &artboardListener); |
| |
| auto stateMachineHandle = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle, |
| &stateMachineListener); |
| |
| commandQueue->requestArtboardNames(fileHandle); |
| commandQueue->requestStateMachineNames(artboardHandle); |
| |
| CHECK(commandQueue->testing_getFileListener(fileHandle)); |
| CHECK(commandQueue->testing_getArtboardListener(artboardHandle)); |
| CHECK(commandQueue->testing_getStateMachineListener(stateMachineHandle)); |
| |
| wait_for_server(commandQueue.get()); |
| |
| commandQueue->processMessages(); |
| |
| CommandQueue::FileListener deleteFileListener; |
| CommandQueue::ArtboardListener deleteArtboardListener; |
| CommandQueue::StateMachineListener deleteStateMachineListener; |
| |
| FileHandle dFileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| &deleteFileListener); |
| |
| auto dArtboardHandle = |
| commandQueue->instantiateArtboardNamed(fileHandle, |
| "New Artboard", |
| &deleteArtboardListener); |
| |
| auto dStateMachineHandle = commandQueue->instantiateDefaultStateMachine( |
| artboardHandle, |
| &deleteStateMachineListener); |
| |
| CHECK(commandQueue->testing_getFileListener(dFileHandle)); |
| CHECK(commandQueue->testing_getArtboardListener(dArtboardHandle)); |
| CHECK(commandQueue->testing_getStateMachineListener(dStateMachineHandle)); |
| |
| commandQueue->deleteFile(dFileHandle); |
| commandQueue->deleteArtboard(dArtboardHandle); |
| commandQueue->deleteStateMachine(dStateMachineHandle); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK(commandQueue->testing_getFileListener(dFileHandle)); |
| CHECK(commandQueue->testing_getArtboardListener(dArtboardHandle)); |
| CHECK(commandQueue->testing_getStateMachineListener(dStateMachineHandle)); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| |
| FileHandle fh; |
| |
| { |
| CommandQueue::FileListener listener; |
| fh = commandQueue->loadFile(std::vector<uint8_t>(100 * 1024, 0), |
| &listener); |
| CHECK(commandQueue->testing_getFileListener(fh)); |
| } |
| |
| CHECK(!commandQueue->testing_getFileListener(fh)); |
| |
| ArtboardHandle ah; |
| { |
| CommandQueue::ArtboardListener listener; |
| ah = commandQueue->instantiateDefaultArtboard(fh, &listener); |
| CHECK(commandQueue->testing_getArtboardListener(ah)); |
| } |
| CHECK(!commandQueue->testing_getArtboardListener(ah)); |
| |
| StateMachineHandle sh; |
| { |
| CommandQueue::StateMachineListener listener; |
| sh = commandQueue->instantiateDefaultStateMachine(ah, &listener); |
| CHECK(commandQueue->testing_getStateMachineListener(sh)); |
| } |
| CHECK(!commandQueue->testing_getStateMachineListener(sh)); |
| |
| CHECK(commandQueue->testing_getFileListener(fileHandle)); |
| CHECK(commandQueue->testing_getArtboardListener(artboardHandle)); |
| CHECK(commandQueue->testing_getStateMachineListener(stateMachineHandle)); |
| |
| // If we move we should now point to the new listener |
| CommandQueue::FileListener newFileListener = std::move(fileListener); |
| auto listenerPtr = commandQueue->testing_getFileListener(fileHandle); |
| CHECK(&newFileListener == listenerPtr); |
| |
| CommandQueue::ArtboardListener newArtboardListener = |
| std::move(artboardListener); |
| auto listenerPtr1 = |
| commandQueue->testing_getArtboardListener(artboardHandle); |
| CHECK(&newArtboardListener == listenerPtr1); |
| |
| CommandQueue::StateMachineListener newStateMachineListener = |
| std::move(stateMachineListener); |
| auto listenerPtr2 = |
| commandQueue->testing_getStateMachineListener(stateMachineHandle); |
| CHECK(&newStateMachineListener == listenerPtr2); |
| |
| // force unref the commandQueue to ensure it stays alive for listeners |
| commandQueue.reset(); |
| |
| // after this we are checking the destructors for fileListener, |
| // artboardListener and stateMachineListener as they should gracefully |
| // remove themselves from the commandQeueue even though the ref here is gone |
| } |
| |
| TEST_CASE("empty test for code cove", "[CommandQueue]") |
| { |
| CommandQueue::FileListener fileL; |
| CommandQueue::ArtboardListener artboardL; |
| CommandQueue::StateMachineListener statemachineL; |
| |
| std::vector<std::string> emptyVector; |
| |
| fileL.onFileDeleted(0, 0); |
| fileL.onArtboardsListed(0, 0, emptyVector); |
| |
| artboardL.onArtboardDeleted(0, 0); |
| artboardL.onStateMachinesListed(0, 0, emptyVector); |
| |
| statemachineL.onStateMachineDeleted(0, 0); |
| statemachineL.onStateMachineSettled(0, 0); |
| |
| CHECK(true); |
| } |
| |
| // Helps with repetition of checks in the following test |
| void checkStateMachineBool(rcp<CommandQueue>& commandQueue, |
| StateMachineHandle handle, |
| const char* boolName, |
| bool expectedValue) |
| { |
| commandQueue->runOnce( |
| [handle, boolName, expectedValue](CommandServer* server) { |
| rive::StateMachineInstance* sm = |
| server->getStateMachineInstance(handle); |
| CHECK(sm->getBool(boolName)->value() == expectedValue); |
| }); |
| } |
| |
| TEST_CASE("pointer input", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/pointer_events.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| ArtboardHandle artboardHandle = |
| commandQueue->instantiateDefaultArtboard(fileHandle); |
| StateMachineHandle smHandle = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle); |
| |
| commandQueue->runOnce( |
| [fileHandle, artboardHandle, smHandle](CommandServer* server) { |
| CHECK(fileHandle != RIVE_NULL_HANDLE); |
| CHECK(server->getFile(fileHandle) != nullptr); |
| CHECK(artboardHandle != RIVE_NULL_HANDLE); |
| CHECK(server->getArtboardInstance(artboardHandle) != nullptr); |
| CHECK(smHandle != RIVE_NULL_HANDLE); |
| CHECK(server->getStateMachineInstance(smHandle) != nullptr); |
| }); |
| |
| // Prime for events by advancing once |
| commandQueue->advanceStateMachine(smHandle, 0.0f); |
| |
| // The listener object in the bottom-left corner, |
| // which toggles `isDown` when receiving down events. |
| Vec2D toggleOnDownCorner(425.0f, 425.0f); |
| commandQueue->pointerDown(smHandle, {.position = toggleOnDownCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| commandQueue->pointerUp(smHandle, {.position = toggleOnDownCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| commandQueue->pointerDown(smHandle, {.position = toggleOnDownCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", false); |
| |
| // The listener object in the top-left corner, |
| // which toggles `isDown` when receiving down or up events. |
| Vec2D toggleOnDownOrUpCorner(75.0f, 75.0f); |
| commandQueue->pointerDown(smHandle, {.position = toggleOnDownOrUpCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| commandQueue->pointerUp(smHandle, {.position = toggleOnDownOrUpCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", false); |
| |
| // The listener object in the top-right corner, |
| // which toggles `isDown` when hovered. |
| Vec2D toggleOnHoverCorner(425.0f, 75.0f); |
| // Center, which has no pointer listener. |
| Vec2D center(250.0f, 250.0f); |
| commandQueue->pointerMove(smHandle, {.position = center}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", false); |
| commandQueue->pointerMove(smHandle, {.position = toggleOnHoverCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| commandQueue->pointerMove(smHandle, {.position = center}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| commandQueue->pointerMove(smHandle, {.position = toggleOnHoverCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", false); |
| |
| // A position off of the artboard, which can be used for pointer exits. |
| Vec2D offArtboard(-25.0f, -25.0f); |
| commandQueue->pointerDown(smHandle, {.position = toggleOnDownOrUpCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| // Slide off while "holding down" the pointer. |
| commandQueue->pointerExit(smHandle, {.position = offArtboard}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| // Release the pointer while off the artboard - should not toggle |
| commandQueue->pointerUp(smHandle, {.position = offArtboard}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| // Reset |
| commandQueue->pointerUp(smHandle, {.position = toggleOnDownOrUpCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", false); |
| |
| // New test, sliding off and back, but this time releasing back on the |
| // artboard. |
| commandQueue->pointerDown(smHandle, {.position = toggleOnDownOrUpCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| commandQueue->pointerExit(smHandle, {.position = offArtboard}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| commandQueue->pointerMove(smHandle, {.position = toggleOnDownOrUpCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", true); |
| commandQueue->pointerUp(smHandle, {.position = toggleOnDownOrUpCorner}); |
| checkStateMachineBool(commandQueue, smHandle, "isDown", false); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| static bool aboutEquals(const Vec2D& l, const Vec2D& r, float theta = 0.0001) |
| { |
| auto b = l - r; |
| return abs(b.x) < theta && abs(b.y) < theta; |
| } |
| |
| TEST_CASE("pointer input translation", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::unique_ptr<gpu::RenderContext> nullContext = |
| RenderContextNULL::MakeContext(); |
| CommandServer server(commandQueue, nullContext.get()); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {})); |
| ArtboardHandle artboardHandle = |
| commandQueue->instantiateDefaultArtboard(fileHandle); |
| StateMachineHandle smHandle = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle); |
| // artboard is 500x500 so 50x50 should translate to 250,250 |
| |
| // expected result |
| Vec2D expected(250, 250); |
| // Inputs |
| Vec2D center(50, 50); |
| Vec2D size(100, 100); |
| |
| commandQueue->runOnce([expected, center, size, smHandle]( |
| CommandServer* server) { |
| auto instance = server->getStateMachineInstance(smHandle); |
| CHECK(instance != nullptr); |
| auto translated = server->testing_cursorPosForPointerEvent( |
| instance, |
| {.fit = Fit::contain, .screenBounds = size, .position = center}); |
| CHECK(aboutEquals(translated, expected)); |
| }); |
| |
| // expected result |
| Vec2D expectedTL(125, 125); |
| // Inputs |
| Vec2D topLeft(25, 25); |
| |
| commandQueue->runOnce([expectedTL, topLeft, size, smHandle]( |
| CommandServer* server) { |
| auto instance = server->getStateMachineInstance(smHandle); |
| CHECK(instance != nullptr); |
| auto translated = server->testing_cursorPosForPointerEvent( |
| instance, |
| {.fit = Fit::contain, .screenBounds = size, .position = topLeft}); |
| CHECK(aboutEquals(translated, expectedTL)); |
| }); |
| |
| // expected result |
| Vec2D expectedTR(375, 375); |
| // Inputs |
| Vec2D topRight(75, 75); |
| |
| commandQueue->runOnce([expectedTR, topRight, size, smHandle]( |
| CommandServer* server) { |
| auto instance = server->getStateMachineInstance(smHandle); |
| CHECK(instance != nullptr); |
| auto translated = server->testing_cursorPosForPointerEvent( |
| instance, |
| {.fit = Fit::contain, .screenBounds = size, .position = topRight}); |
| CHECK(aboutEquals(translated, expectedTR)); |
| }); |
| |
| // expected result |
| Vec2D expectedBR(375, 125); |
| // Inputs |
| Vec2D bottomRight(75, 25); |
| |
| commandQueue->runOnce([bottomRight, expectedBR, size, smHandle]( |
| CommandServer* server) { |
| auto instance = server->getStateMachineInstance(smHandle); |
| CHECK(instance != nullptr); |
| auto translated = |
| server->testing_cursorPosForPointerEvent(instance, |
| {.fit = Fit::contain, |
| .screenBounds = size, |
| .position = bottomRight}); |
| CHECK(aboutEquals(translated, expectedBR)); |
| }); |
| |
| // expected result |
| Vec2D expectedBL(125, 375); |
| // Inputs |
| Vec2D bottomLeft(25, 75); |
| |
| commandQueue->runOnce([bottomLeft, expectedBL, size, smHandle]( |
| CommandServer* server) { |
| auto instance = server->getStateMachineInstance(smHandle); |
| CHECK(instance != nullptr); |
| auto translated = |
| server->testing_cursorPosForPointerEvent(instance, |
| {.fit = Fit::contain, |
| .screenBounds = size, |
| .position = bottomLeft}); |
| CHECK(aboutEquals(translated, expectedBL)); |
| }); |
| |
| server.processCommands(); |
| commandQueue->processMessages(); |
| |
| commandQueue->disconnect(); |
| } |
| |
| // clang format is removing the needed space between the func names and the ( |
| // clang-format off |
| #define DEFINE_TEST_CALLBACK(fun, handleType, expectedRequestId) \ |
| bool m_##fun##WasCalled = false; \ |
| virtual void fun (const handleType handle, uint64_t requestId) override \ |
| { \ |
| CHECK(handle == m_handle); \ |
| CHECK(requestId == expectedRequestId); \ |
| m_##fun##WasCalled = true; \ |
| } |
| |
| #define DEFINE_TEST_CALLBACK_ONE_PARAM(fun, \ |
| handleType, \ |
| expectedRequestId, \ |
| paramType, \ |
| param) \ |
| bool m_##fun##WasCalled = false; \ |
| virtual void fun (const handleType handle, \ |
| uint64_t requestId, \ |
| paramType param) override \ |
| { \ |
| CHECK(handle == m_handle); \ |
| CHECK(requestId == expectedRequestId); \ |
| CHECK(param == m_##param); \ |
| m_##fun##WasCalled = true; \ |
| } |
| |
| #define DEFINE_TEST_CALLBACK_TWO_PARAM(fun, \ |
| handleType, \ |
| expectedRequestId, \ |
| paramType, \ |
| param, \ |
| param2Type, \ |
| param2) \ |
| bool m_##fun##WasCalled = false; \ |
| virtual void fun (const handleType handle, \ |
| uint64_t requestId, \ |
| paramType param, \ |
| param2Type param2) override \ |
| { \ |
| CHECK(handle == m_handle); \ |
| CHECK(requestId == expectedRequestId); \ |
| CHECK(param == m_##param); \ |
| CHECK(param2 == m_##param2); \ |
| m_##fun##WasCalled = true; \ |
| } |
| // clang-format on |
| #define CHECK_CALLBACK(obj, func) CHECK(obj.m_##func##WasCalled) |
| |
| class GlobalFileListener : public CommandQueue::FileListener |
| { |
| public: |
| virtual void onFileError(const FileHandle, |
| uint64_t requestId, |
| std::string error) override |
| {} |
| |
| DEFINE_TEST_CALLBACK(onFileDeleted, FileHandle, 7); |
| DEFINE_TEST_CALLBACK(onFileLoaded, FileHandle, 1); |
| DEFINE_TEST_CALLBACK_ONE_PARAM(onArtboardsListed, |
| FileHandle, |
| 2, |
| std::vector<std::string>, |
| artboardNames); |
| DEFINE_TEST_CALLBACK_ONE_PARAM(onViewModelsListed, |
| FileHandle, |
| 3, |
| std::vector<std::string>, |
| viewModelNames); |
| |
| DEFINE_TEST_CALLBACK_TWO_PARAM(onViewModelInstanceNamesListed, |
| FileHandle, |
| 4, |
| std::string, |
| viewModelNameI, |
| std::vector<std::string>, |
| instanceNames); |
| DEFINE_TEST_CALLBACK_TWO_PARAM( |
| onViewModelPropertiesListed, |
| FileHandle, |
| 5, |
| std::string, |
| viewModelNameP, |
| std::vector<CommandQueue::FileListener::ViewModelPropertyData>, |
| properties); |
| DEFINE_TEST_CALLBACK_ONE_PARAM(onViewModelEnumsListed, |
| FileHandle, |
| 6, |
| std::vector<ViewModelEnum>, |
| enums); |
| |
| FileHandle m_handle; |
| std::array<std::string, 4> m_artboardNames = {"Test Artboard", |
| "Test Transitions", |
| "Test Observation", |
| "Artboard"}; |
| std::array<std::string, 6> m_viewModelNames = {"ListViewModel", |
| "Empty VM", |
| "Test All", |
| "Nested VM", |
| "State Transition", |
| "Alternate VM"}; |
| std::array<std::string, 2> m_instanceNames = {"Test Default", |
| "Test Alternate"}; |
| std::array<CommandQueue::FileListener::ViewModelPropertyData, 10> |
| m_properties = { |
| CommandQueue::FileListener::ViewModelPropertyData{ |
| DataType::artboard, |
| "Test Artboard"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::list, |
| "Test List"}, |
| CommandQueue::FileListener::ViewModelPropertyData{ |
| DataType::assetImage, |
| "Test Image"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::number, |
| "Test Num"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::string, |
| "Test String"}, |
| CommandQueue::FileListener::ViewModelPropertyData{ |
| DataType::enumType, |
| "Test Enum", |
| "Test Enum Values"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::boolean, |
| "Test Bool"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::color, |
| "Test Color"}, |
| CommandQueue::FileListener::ViewModelPropertyData{DataType::trigger, |
| "Test Trigger"}, |
| CommandQueue::FileListener::ViewModelPropertyData{ |
| DataType::viewModel, |
| "Test Nested", |
| "Nested VM"}}; |
| std::array<ViewModelEnum, 1> m_enums = { |
| ViewModelEnum{"Test Enum Values", {"Value 1", "Value 2"}}}; |
| std::string m_viewModelNameI = "Test All"; |
| std::string m_viewModelNameP = "Test All"; |
| }; |
| |
| class GlobalRenderImageListener : public CommandQueue::RenderImageListener |
| { |
| public: |
| virtual void onRenderImageError(const RenderImageHandle, |
| uint64_t requestId, |
| std::string error) override |
| {} |
| |
| DEFINE_TEST_CALLBACK(onRenderImageDeleted, RenderImageHandle, 8); |
| |
| RenderImageHandle m_handle; |
| }; |
| |
| class GlobalAudioSourceListener : public CommandQueue::AudioSourceListener |
| { |
| public: |
| virtual void onAudioSourceError(const AudioSourceHandle, |
| uint64_t requestId, |
| std::string error) override |
| {} |
| |
| DEFINE_TEST_CALLBACK(onAudioSourceDeleted, AudioSourceHandle, 9); |
| |
| AudioSourceHandle m_handle; |
| }; |
| |
| class GlobalFontListener : public CommandQueue::FontListener |
| { |
| public: |
| virtual void onFontError(const FontHandle, |
| uint64_t requestId, |
| std::string error) override |
| {} |
| |
| DEFINE_TEST_CALLBACK(onFontDeleted, FontHandle, 10); |
| |
| FontHandle m_handle; |
| }; |
| |
| class GlobalArtboardListener : public CommandQueue::ArtboardListener |
| { |
| public: |
| virtual void onArtboardError(const ArtboardHandle, |
| uint64_t requestId, |
| std::string error) override |
| {} |
| |
| DEFINE_TEST_CALLBACK(onArtboardDeleted, ArtboardHandle, 12); |
| DEFINE_TEST_CALLBACK_ONE_PARAM(onStateMachinesListed, |
| ArtboardHandle, |
| 11, |
| std::vector<std::string>, |
| stateMachineNames); |
| DEFINE_TEST_CALLBACK_TWO_PARAM(onDefaultViewModelInfoReceived, |
| ArtboardHandle, |
| 20, |
| std::string, |
| viewModelName, |
| std::string, |
| instanceName); |
| |
| ArtboardHandle m_handle; |
| std::array<std::string, 1> m_stateMachineNames = {"SM"}; |
| std::string m_viewModelName = "Test All"; |
| std::string m_instanceName = "Test Default"; |
| }; |
| |
| class GlobalViewModelInstanceListener |
| : public CommandQueue::ViewModelInstanceListener |
| { |
| public: |
| virtual void onViewModelInstanceError(const ViewModelInstanceHandle, |
| uint64_t requestId, |
| std::string error) override |
| {} |
| |
| DEFINE_TEST_CALLBACK(onViewModelDeleted, ViewModelInstanceHandle, 15); |
| |
| DEFINE_TEST_CALLBACK_ONE_PARAM(onViewModelDataReceived, |
| ViewModelInstanceHandle, |
| 13, |
| CommandQueue::ViewModelInstanceData, |
| instanceData); |
| |
| DEFINE_TEST_CALLBACK_TWO_PARAM(onViewModelListSizeReceived, |
| ViewModelInstanceHandle, |
| 14, |
| std::string, |
| path, |
| size_t, |
| size); |
| |
| size_t m_size = 2; |
| std::string m_path = "Test List"; |
| ViewModelInstanceHandle m_handle; |
| CommandQueue::ViewModelInstanceData m_instanceData = { |
| .metaData = PropertyData{DataType::boolean, "Test Bool"}, |
| .boolValue = true}; |
| }; |
| |
| class GlobalStateMachineListener : public CommandQueue::StateMachineListener |
| { |
| public: |
| virtual void onStateMachineError(const StateMachineHandle, |
| uint64_t requestId, |
| std::string error) override |
| {} |
| |
| DEFINE_TEST_CALLBACK(onStateMachineDeleted, StateMachineHandle, 17); |
| DEFINE_TEST_CALLBACK(onStateMachineSettled, StateMachineHandle, 16); |
| |
| StateMachineHandle m_handle; |
| }; |
| |
| TEST_CASE("global Listener", "[CommandQueue]") |
| { |
| GlobalStateMachineListener globalStateMachineListener; |
| GlobalViewModelInstanceListener globalViewModelInstanceListener; |
| GlobalArtboardListener globalArtboardListener; |
| GlobalFontListener globalFontListener; |
| GlobalAudioSourceListener globalAudioSourceListener; |
| GlobalRenderImageListener globalRenderImageListener; |
| GlobalFileListener globalFileListener; |
| |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| commandQueue->setGlobalFileListener(&globalFileListener); |
| commandQueue->setGlobalArtboardListener(&globalArtboardListener); |
| commandQueue->setGlobalStateMachineListener(&globalStateMachineListener); |
| commandQueue->setGlobalRenderImageListener(&globalRenderImageListener); |
| commandQueue->setGlobalAudioSourceListener(&globalAudioSourceListener); |
| commandQueue->setGlobalViewModelInstanceListener( |
| &globalViewModelInstanceListener); |
| commandQueue->setGlobalFontListener(&globalFontListener); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| nullptr, |
| 1); |
| auto artboardHandle = commandQueue->instantiateDefaultArtboard(fileHandle); |
| auto stateMachineHandle = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle); |
| auto viewModel = |
| commandQueue->instantiateDefaultViewModelInstance(fileHandle, |
| artboardHandle); |
| |
| std::ifstream imageStream("assets/batdude.png", std::ios::binary); |
| auto renderImage = commandQueue->decodeImage( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(imageStream), {})); |
| |
| std::ifstream audioStream("assets/audio/what.wav", std::ios::binary); |
| auto audioSource = commandQueue->decodeAudio( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(audioStream), {})); |
| |
| std::ifstream fontStream("assets/fonts/OpenSans-Italic.ttf", |
| std::ios::binary); |
| auto font = commandQueue->decodeFont( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(fontStream), {})); |
| |
| globalFileListener.m_handle = fileHandle; |
| globalArtboardListener.m_handle = artboardHandle; |
| globalStateMachineListener.m_handle = stateMachineHandle; |
| globalViewModelInstanceListener.m_handle = viewModel; |
| globalRenderImageListener.m_handle = renderImage; |
| globalAudioSourceListener.m_handle = audioSource; |
| globalFontListener.m_handle = font; |
| |
| // 1 is create file or fileLoaded callback |
| commandQueue->requestArtboardNames(fileHandle, 2); |
| commandQueue->requestViewModelNames(fileHandle, 3); |
| commandQueue->requestViewModelInstanceNames(fileHandle, "Test All", 4); |
| commandQueue->requestViewModelPropertyDefinitions(fileHandle, |
| "Test All", |
| 5); |
| commandQueue->requestViewModelEnums(fileHandle, 6); |
| commandQueue->requestStateMachineNames(artboardHandle, 11); |
| commandQueue->requestDefaultViewModelInfo(artboardHandle, fileHandle, 20); |
| commandQueue->requestViewModelInstanceBool(viewModel, "Test Bool", 13); |
| commandQueue->requestViewModelInstanceListSize(viewModel, "Test List", 14); |
| commandQueue->advanceStateMachine(stateMachineHandle, 1, 16); |
| commandQueue->advanceStateMachine(stateMachineHandle, 1, 16); |
| commandQueue->advanceStateMachine(stateMachineHandle, 1, 16); |
| |
| commandQueue->deleteFont(font, 10); |
| commandQueue->deleteStateMachine(stateMachineHandle, 17); |
| commandQueue->deleteArtboard(artboardHandle, 12); |
| commandQueue->deleteViewModelInstance(viewModel, 15); |
| commandQueue->deleteImage(renderImage, 8); |
| commandQueue->deleteFile(fileHandle, 7); |
| commandQueue->deleteAudio(audioSource, 9); |
| |
| wait_for_server(commandQueue.get()); |
| commandQueue->processMessages(); |
| |
| CHECK_CALLBACK(globalStateMachineListener, onStateMachineDeleted); |
| CHECK_CALLBACK(globalStateMachineListener, onStateMachineSettled); |
| |
| CHECK_CALLBACK(globalViewModelInstanceListener, onViewModelDeleted); |
| CHECK_CALLBACK(globalViewModelInstanceListener, onViewModelDataReceived); |
| CHECK_CALLBACK(globalViewModelInstanceListener, |
| onViewModelListSizeReceived); |
| |
| CHECK_CALLBACK(globalArtboardListener, onArtboardDeleted); |
| CHECK_CALLBACK(globalArtboardListener, onStateMachinesListed); |
| |
| CHECK_CALLBACK(globalFileListener, onFileDeleted); |
| CHECK_CALLBACK(globalFileListener, onFileLoaded); |
| CHECK_CALLBACK(globalFileListener, onArtboardsListed); |
| CHECK_CALLBACK(globalFileListener, onViewModelsListed); |
| CHECK_CALLBACK(globalFileListener, onViewModelEnumsListed); |
| CHECK_CALLBACK(globalFileListener, onViewModelPropertiesListed); |
| CHECK_CALLBACK(globalFileListener, onViewModelInstanceNamesListed); |
| |
| CHECK_CALLBACK(globalFontListener, onFontDeleted); |
| CHECK_CALLBACK(globalAudioSourceListener, onAudioSourceDeleted); |
| CHECK_CALLBACK(globalRenderImageListener, onRenderImageDeleted); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |
| |
| // StateMachines depend on Artboards and Artboards depend on Files. |
| // So if an artboard gets deleted so should the statemachines assosiated with |
| // it. If a file is deleted so should artboards and therefore statemachines. |
| |
| TEST_CASE("dependency lifetime management", "[CommandQueue]") |
| { |
| auto commandQueue = make_rcp<CommandQueue>(); |
| std::thread serverThread(server_thread, commandQueue); |
| |
| std::ifstream stream("assets/data_bind_test_cmdq.riv", std::ios::binary); |
| FileHandle fileHandle = commandQueue->loadFile( |
| std::vector<uint8_t>(std::istreambuf_iterator<char>(stream), {}), |
| nullptr, |
| 1); |
| |
| auto artboardHandle = commandQueue->instantiateDefaultArtboard(fileHandle); |
| auto artboardHandle2 = commandQueue->instantiateDefaultArtboard(fileHandle); |
| auto artboardHandle3 = commandQueue->instantiateDefaultArtboard(fileHandle); |
| |
| auto stateMachine = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle); |
| auto stateMachine_2 = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle); |
| |
| auto stateMachine2 = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle2); |
| auto stateMachine2_2 = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle2); |
| |
| auto stateMachine3 = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle2); |
| auto stateMachine3_2 = |
| commandQueue->instantiateDefaultStateMachine(artboardHandle2); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->getFile(fileHandle) != nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle) != nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle2) != nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle3) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine_2) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine2) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine2_2) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine3) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine3_2) != nullptr); |
| }); |
| |
| commandQueue->deleteArtboard(artboardHandle); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->getFile(fileHandle) != nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle) == nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle2) != nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle3) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine) == nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine_2) == nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine2) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine2_2) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine3) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine3_2) != nullptr); |
| }); |
| |
| commandQueue->deleteStateMachine(stateMachine2); |
| |
| commandQueue->runOnce([&](CommandServer* server) { |
| CHECK(server->getFile(fileHandle) != nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle) == nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle2) != nullptr); |
| CHECK(server->getArtboardInstance(artboardHandle3) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine) == nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine_2) == nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine2) == nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine2_2) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine3) != nullptr); |
| CHECK(server->getStateMachineInstance(stateMachine3_2) != nullptr); |
| }); |
| |
| commandQueue->disconnect(); |
| serverThread.join(); |
| } |