Files
Renderive/render_3D/tests/Point_State_Tests.cpp
T
2026-08-14 11:07:38 +08:00

129 lines
4.6 KiB
C++

#include "render_3D/Point_Visual.h"
#include "render_3D/detail/Point_Core.h"
#include <gtest/gtest.h>
#include <atomic>
#include <cmath>
#include <memory>
#include <thread>
#include <vector>
namespace renderive::render_3d {
namespace {
TEST(PointState, UsesKernelDoubleStateAtFrameBoundary) {
auto data = std::make_shared<Point_Data>();
data->update(std::vector<Point>{{{1.0F, 2.0F, 3.0F}, {1, 2, 3, 255}, 7.0F}});
Point_Visual visual(data);
Point_State configured;
configured.visible = false;
configured.style.stroke_width_px = 3.0F;
visual.configure(configured);
const auto published = detail::Point_State_Access::publish(visual);
EXPECT_EQ(published.state, configured);
ASSERT_EQ(published.data.size(), 1U);
EXPECT_EQ(published.data.front().position, (Vec3{1.0F, 2.0F, 3.0F}));
EXPECT_EQ(published.state_revision, 1U);
EXPECT_EQ(published.data_revision, 1U);
}
TEST(PointState, UsesKernelLatestRealTimeDataForBulkPayloads) {
auto data = std::make_shared<Point_Data>();
Point_Visual visual(data);
constexpr int update_count = 500;
std::atomic<bool> done{};
std::thread writer([&] {
for (int value = 1; value <= update_count; ++value) {
std::vector<Point> points(static_cast<std::size_t>(value % 31 + 1));
for (auto& point : points) {
point.position.x = static_cast<float>(value);
point.diameter_px = static_cast<float>(value % 12 + 1);
}
data->update(std::move(points));
}
done.store(true, std::memory_order_release);
});
do {
const auto published = detail::Point_State_Access::publish(visual);
if (!published.data.empty()) {
const float expected = published.data.front().position.x;
for (const auto& point : published.data)
EXPECT_EQ(point.position.x, expected);
}
} while (!done.load(std::memory_order_acquire));
writer.join();
const auto published = detail::Point_State_Access::publish(visual);
EXPECT_EQ(published.data_revision, update_count);
ASSERT_FALSE(published.data.empty());
EXPECT_EQ(published.data.front().position.x, static_cast<float>(update_count));
}
TEST(PointState, RejectsInvalidStateAndPayloadAtTheOwningBoundary) {
auto data = std::make_shared<Point_Data>();
Point_Visual visual(data);
Point_State invalid;
invalid.style.stroke_width_px = -1.0F;
EXPECT_THROW(visual.configure(invalid), std::invalid_argument);
data->update(std::vector<Point>{{{}, {}, 0.0F}});
EXPECT_THROW((void)detail::Point_State_Access::publish(visual),
std::invalid_argument);
}
TEST(PointFrameControl, ReusesKernelManualAndPlaybackStrategies) {
auto data = std::make_shared<Point_Data>();
data->update(std::vector<Point>{{{}, {}, 8.0F}});
Point_Visual visual(data);
detail::Scene_State_Buffer scene({{320, 180}, {}});
detail::Input_Collector input;
detail::Frame_Scheduler manual(Frame_Mode::Manual, 60.0);
EXPECT_TRUE(manual.prepare(scene, visual, input));
auto manual_status = manual.status();
EXPECT_EQ(manual_status.mode, Frame_Mode::Manual);
EXPECT_EQ(manual_status.produced_frame_count, 1U);
EXPECT_EQ(manual_status.pending_frame_count, 1U);
bool rendered{};
EXPECT_FALSE(manual.render([&](const detail::Point_Frame_Data&) {
rendered = true;
return true;
}));
EXPECT_FALSE(rendered);
EXPECT_TRUE(manual.refresh());
EXPECT_TRUE(manual.render([&](const detail::Point_Frame_Data& frame) {
rendered = true;
EXPECT_EQ(frame.point.data.size(), 1U);
return true;
}));
EXPECT_TRUE(rendered);
manual_status = manual.status();
EXPECT_EQ(manual_status.consumed_frame_count, 1U);
EXPECT_EQ(manual_status.pending_frame_count, 0U);
detail::Frame_Scheduler playback(Frame_Mode::Playback, 60.0);
EXPECT_TRUE(playback.prepare(scene, visual, input));
data->update(std::vector<Point>(2, Point{{}, {}, 8.0F}));
EXPECT_TRUE(playback.prepare(scene, visual, input));
auto playback_status = playback.status();
EXPECT_EQ(playback_status.produced_frame_count, 2U);
EXPECT_EQ(playback_status.pending_frame_count, 2U);
EXPECT_TRUE(playback.render([](const detail::Point_Frame_Data& frame) {
return frame.point.data.size() == 1U;
}));
EXPECT_TRUE(playback.render([](const detail::Point_Frame_Data& frame) {
return frame.point.data.size() == 2U;
}));
playback_status = playback.status();
EXPECT_EQ(playback_status.consumed_frame_count, 2U);
EXPECT_EQ(playback_status.pending_frame_count, 0U);
}
} // namespace
} // namespace renderive::render_3d