| /* | |
| * Copyright 2026 Rive | |
| */ | |
| // Shared helpers for the C++ semantic test suite. Groups the fixture-load + | |
| // bind + settle boilerplate, the tiny diff-query utilities, and the | |
| // per-test constants that were previously duplicated across the five | |
| // semantic_*_test.cpp files. | |
| #ifndef _RIVE_SEMANTIC_TEST_HELPERS_HPP_ | |
| #define _RIVE_SEMANTIC_TEST_HELPERS_HPP_ | |
| #include <rive/animation/state_machine_instance.hpp> | |
| #include <rive/artboard.hpp> | |
| #include <rive/file.hpp> | |
| #include <rive/math/vec2d.hpp> | |
| #include <rive/semantic/semantic_manager.hpp> | |
| #include <rive/semantic/semantic_node.hpp> | |
| #include <rive/semantic/semantic_role.hpp> | |
| #include <rive/semantic/semantic_snapshot.hpp> | |
| #include <rive/semantic/semantic_state.hpp> | |
| #include <rive/viewmodel/runtime/viewmodel_instance_runtime.hpp> | |
| #include "rive_file_reader.hpp" | |
| #include <cstdint> | |
| #include <memory> | |
| #include <string> | |
| namespace rive | |
| { | |
| namespace semantic_test | |
| { | |
| // Default settle duration: 10 frames at 0.1s each = 1s of simulated time. | |
| // Long enough for layout, transitions, and data-binding-driven list-item | |
| // spawns to reach steady state in the current fixtures. | |
| constexpr int kSettleFrames = 10; | |
| constexpr float kSettleDt = 0.1f; | |
| // Shared fixture labels (authored into .riv; drift here fails tests in | |
| // multiple files at once). | |
| constexpr const char* kDropdownLabel = "Select a fandom"; | |
| inline void advance(StateMachineInstance* sm, | |
| int frames = kSettleFrames, | |
| float dt = kSettleDt) | |
| { | |
| for (int i = 0; i < frames; ++i) | |
| { | |
| sm->advanceAndApply(dt); | |
| } | |
| } | |
| inline SemanticsDiff settleAndDrainDiff(StateMachineInstance* sm, | |
| int frames = kSettleFrames, | |
| float dt = kSettleDt) | |
| { | |
| advance(sm, frames, dt); | |
| auto* mgr = sm->semanticManager(); | |
| return mgr != nullptr ? mgr->drainDiff() : SemanticsDiff{}; | |
| } | |
| inline const SemanticsDiffNode* findAddedByLabel(const SemanticsDiff& diff, | |
| const std::string& label) | |
| { | |
| for (const auto& n : diff.added) | |
| { | |
| if (n.label == label) | |
| { | |
| return &n; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| inline const SemanticsDiffNode* findAddedByRole(const SemanticsDiff& diff, | |
| SemanticRole role) | |
| { | |
| const uint32_t asU = static_cast<uint32_t>(role); | |
| for (const auto& n : diff.added) | |
| { | |
| if (n.role == asU) | |
| { | |
| return &n; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| inline Vec2D centerOf(const SemanticsDiffNode& node) | |
| { | |
| return Vec2D((node.minX + node.maxX) * 0.5f, | |
| (node.minY + node.maxY) * 0.5f); | |
| } | |
| inline bool isSelected(const SemanticNode* node) | |
| { | |
| return node != nullptr && | |
| hasSemanticState(node->stateFlags(), SemanticState::Selected); | |
| } | |
| // Holds a loaded .riv + its default artboard, state machine, and bound view | |
| // model instance, with semantics enabled and the tree already settled. | |
| struct Fixture | |
| { | |
| rcp<File> file; | |
| std::unique_ptr<ArtboardInstance> artboard; | |
| std::unique_ptr<StateMachineInstance> sm; | |
| rcp<ViewModelInstance> vmi; | |
| }; | |
| // Load a fixture, enable semantics, bind the default VMI (if any), advance | |
| // to settle. Every fixture-driven test in the suite uses this. | |
| inline Fixture loadFixture(const char* path) | |
| { | |
| Fixture f; | |
| f.file = ReadRiveFile(path); | |
| f.artboard = f.file->artboardDefault(); | |
| f.sm = f.artboard->stateMachineAt(0); | |
| if (f.sm != nullptr) | |
| { | |
| f.sm->enableSemantics(); | |
| } | |
| f.vmi = f.file->createDefaultViewModelInstance(f.artboard.get()); | |
| if (f.vmi != nullptr) | |
| { | |
| f.artboard->bindViewModelInstance(f.vmi); | |
| if (f.sm != nullptr) | |
| { | |
| f.sm->bindViewModelInstance(f.vmi); | |
| } | |
| } | |
| if (f.sm != nullptr) | |
| { | |
| advance(f.sm.get()); | |
| } | |
| return f; | |
| } | |
| } // namespace semantic_test | |
| } // namespace rive | |
| #endif |