123 lines
4.5 KiB
C++
123 lines
4.5 KiB
C++
#include <gtest/gtest.h>
|
|
#include <mcp/core/runtime/Gallery_Plots.hpp>
|
|
#include <render_common.hpp>
|
|
#include <web_server/src/media/Gallery_Video_Stream.hpp>
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <ranges>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
namespace aethera::web::media {
|
|
namespace {
|
|
|
|
using Clock = std::chrono::steady_clock;
|
|
|
|
void corun_until_or_throw(std::string_view operation,
|
|
const std::function<bool()>& completed) {
|
|
std::string failure;
|
|
Task_Graph coordinator{"gallery.video.test.await"};
|
|
coordinator.add("await", [&] {
|
|
const auto deadline = Clock::now() + std::chrono::seconds{10};
|
|
Task_Graph::corun_until([&] {
|
|
if (completed()) return true;
|
|
if (Clock::now() < deadline) return false;
|
|
failure = std::string{operation} + " timed out";
|
|
return true;
|
|
});
|
|
});
|
|
aethera::detail::run_taskflow(coordinator);
|
|
if (!failure.empty()) throw std::runtime_error(failure);
|
|
}
|
|
|
|
void configure_manual_pixel_delivery(const std::shared_ptr<Plot>& plot) {
|
|
ASSERT_TRUE(plot);
|
|
ASSERT_TRUE(plot->write_prop(
|
|
"frame-analysis", "pacing_mode", "manual").value(
|
|
"success", false));
|
|
ASSERT_TRUE(plot->write_prop(
|
|
"frame-analysis", "pixel_delivery_enabled", true).value(
|
|
"success", false));
|
|
ASSERT_NO_THROW(corun_until_or_throw("manual Plot policy", [&] {
|
|
const auto diagnostics = plot->diagnostics();
|
|
const auto& configuration = diagnostics.at("frame_policy").at(
|
|
"configuration");
|
|
return configuration.at("mode") == "manual" &&
|
|
configuration.at("pixel_delivery_enabled").get<bool>();
|
|
}));
|
|
}
|
|
|
|
void schedule_manual_frame(const std::shared_ptr<Plot>& plot,
|
|
std::uint64_t correlation_sequence) {
|
|
const auto now = Clock::now();
|
|
plot->schedule_render(Plot_Render_Tick{
|
|
.issued_at = now,
|
|
.sequence = correlation_sequence,
|
|
.time_milliseconds =
|
|
std::chrono::duration<double, std::milli>(
|
|
now.time_since_epoch()).count(),
|
|
.width = 720,
|
|
.height = 420,
|
|
.source = Frame_Request_Source::immediate
|
|
});
|
|
}
|
|
|
|
TEST(Gallery_Video_Stream,
|
|
Encodes_Each_Plots_Independent_Policy_Frames_Without_Grouping) {
|
|
const auto definitions = gallery_plot_definitions();
|
|
const auto found = std::ranges::find_if(
|
|
definitions, [](const Plot_Definition& definition) {
|
|
return definition.dimension == Plot_Dimension::two_d;
|
|
});
|
|
ASSERT_NE(found, definitions.end());
|
|
auto plot_a = found->create();
|
|
auto plot_b = found->create();
|
|
configure_manual_pixel_delivery(plot_a);
|
|
configure_manual_pixel_delivery(plot_b);
|
|
|
|
auto stream = Gallery_Video_Stream::create({
|
|
{"plot-a", plot_a}, {"plot-b", plot_b}});
|
|
ASSERT_TRUE(stream);
|
|
constexpr std::size_t frame_count{5};
|
|
std::array<std::atomic_uint64_t, frame_count> sequences{};
|
|
std::atomic_size_t received{};
|
|
const auto subscription = stream->subscribe(
|
|
"gallery-video-test",
|
|
[&](Gallery_Stream_Frame frame) {
|
|
if (!frame.video) return;
|
|
const auto index = received.fetch_add(
|
|
1, std::memory_order_acq_rel);
|
|
if (index < sequences.size())
|
|
sequences[index].store(
|
|
frame.video->sequence, std::memory_order_release);
|
|
},
|
|
[] { return true; },
|
|
[] { return nlohmann::json::object(); });
|
|
|
|
const std::array<std::shared_ptr<Plot>, frame_count> schedule{
|
|
plot_a, plot_a, plot_b, plot_a, plot_b};
|
|
for (std::size_t index = 0; index < schedule.size(); ++index) {
|
|
schedule_manual_frame(schedule[index], index + 1U);
|
|
ASSERT_NO_THROW(corun_until_or_throw("encoded Plot frame", [&] {
|
|
return received.load(std::memory_order_acquire) > index;
|
|
}));
|
|
}
|
|
|
|
EXPECT_EQ(received.load(std::memory_order_acquire), frame_count);
|
|
for (std::size_t index = 0; index < frame_count; ++index)
|
|
EXPECT_EQ(sequences[index].load(std::memory_order_acquire),
|
|
index + 1U);
|
|
const auto diagnostics = stream->diagnostics();
|
|
EXPECT_EQ(diagnostics.at("received_frame_count"), frame_count);
|
|
EXPECT_EQ(diagnostics.at("processed_frame_count"), frame_count);
|
|
EXPECT_EQ(diagnostics.at("encoded_frame_count"), frame_count);
|
|
EXPECT_EQ(diagnostics.at("pending_frame_count"), 0U);
|
|
|
|
stream->unsubscribe(subscription);
|
|
stream->shutdown();
|
|
}
|
|
|
|
}
|
|
}
|