Files
libopenshot/tests/AdaptivePreroll.cpp

88 lines
3.3 KiB
C++

/**
* @file
* @brief Unit tests for adaptive preview pre-roll
* @author OpenShot Studios, LLC
*
* @ref License
*/
// Copyright (c) 2026 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "openshot_catch.h"
#include "Qt/AdaptivePreroll.h"
using openshot::AdaptivePreroll;
TEST_CASE("adaptive startup uses first cheap samples", "[preroll]") {
AdaptivePreroll policy;
policy.Reset(1000);
CHECK(policy.Target(30, 1, 100, 30, 60) == 30);
CHECK_FALSE(policy.Ready(2000, 2, true, 30));
for (int i = 0; i < 3; ++i) policy.Observe(1000);
CHECK(policy.Target(30, 1, 100, 30, 60) == 2);
CHECK(policy.Ready(4000, 2, true, 2));
CHECK_FALSE(policy.Ready(5000, 0, false, 2));
CHECK(policy.Ready(6000, 1, true, 2));
}
TEST_CASE("adaptive variability increases headroom with hysteresis", "[preroll]") {
AdaptivePreroll policy;
policy.Reset(0);
for (int i = 0; i < 3; ++i) policy.Observe(1000);
CHECK(policy.Target(30, 1, 100, 30, 60) == 2);
policy.Observe(60000);
const auto peak = policy.Target(30, 1, 100, 30, 60);
CHECK(peak > 2);
policy.Observe(1000);
const auto next = policy.Target(30, 1, 100, 30, 60);
CHECK(next <= peak);
CHECK(next >= 2);
for (int i = 0; i < 30; ++i) policy.Observe(1000);
CHECK(policy.Target(30, 1, 100, 30, 60) == 2);
}
TEST_CASE("slow producer cannot wait indefinitely for optional headroom", "[preroll]") {
AdaptivePreroll policy;
policy.Reset(100);
for (int i = 0; i < 3; ++i) policy.Observe(90000);
const auto required = policy.Target(30, 1, 100, 30, 60);
CHECK(required > 2);
CHECK_FALSE(policy.Ready(499999, 1, true, required));
CHECK(policy.Ready(500100, 1, true, required));
CHECK(policy.TimedOut());
CHECK_FALSE(policy.Ready(600100, 0, true, required));
CHECK_FALSE(policy.Ready(600100, 1, false, required));
}
TEST_CASE("capacity speed boundary and discontinuity clamp adaptive target", "[preroll]") {
AdaptivePreroll policy;
policy.Reset(0);
for (int i = 0; i < 3; ++i) policy.Observe(1000);
CHECK(policy.Target(30, -3, 100, 30, 60) == 6);
CHECK(policy.Target(30, 3, 1, 30, 60) == 1);
CHECK(policy.Target(30, 3, 0, 30, 60) == 0);
CHECK(policy.Ready(1, 0, true, 0, 0));
policy.Reset(1000000);
CHECK_FALSE(policy.Ready(1000001, 2, true, 30));
CHECK(policy.Target(30, 1, 100, 30, 60) == 30);
CHECK(policy.Target(30, 1, 10000, 10000, 10000) == AdaptivePreroll::MaxProbeFrames);
CHECK(policy.Target(30, 1, 100, -1, 60) == 0);
}
TEST_CASE("fake-clock startup comparison separates latency from sustained throughput", "[preroll]") {
auto startup = [](int64_t decode_us, bool adaptive) {
AdaptivePreroll policy;
policy.Reset(0);
int64_t now = 0;
for (int64_t frames = 1; frames < 100; ++frames) {
now += decode_us;
policy.Observe(decode_us);
const auto target = policy.Target(30,1,100,30,60);
if (adaptive ? policy.Ready(now,frames-1,true,target) : frames >= 31) return now;
}
return int64_t(-1);
};
CHECK(startup(1000,false) == 31000);
CHECK(startup(1000,true) == 3000);
CHECK(startup(55000,false) == 1705000);
CHECK(startup(55000,true) <= AdaptivePreroll::StartupLimitUs + 55000);
// 55ms production still exceeds 33ms consumption after either start.
}