Init: Migrate SimpleRemoter (Since v1.3.1) to Gitea
This commit is contained in:
691
test/unit/screen/QualityAdaptiveTest.cpp
Normal file
691
test/unit/screen/QualityAdaptiveTest.cpp
Normal file
@@ -0,0 +1,691 @@
|
||||
// QualityAdaptiveTest.cpp - Phase 4: 质量自适应单元测试
|
||||
// 测试 RTT 到质量等级的映射和防抖策略
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
|
||||
// ============================================
|
||||
// 质量等级枚举 (来自 commands.h)
|
||||
// ============================================
|
||||
enum QualityLevel {
|
||||
QUALITY_DISABLED = -2, // 关闭质量控制
|
||||
QUALITY_ADAPTIVE = -1, // 自适应模式
|
||||
QUALITY_ULTRA = 0, // 极佳(局域网)
|
||||
QUALITY_HIGH = 1, // 优秀
|
||||
QUALITY_GOOD = 2, // 良好
|
||||
QUALITY_MEDIUM = 3, // 一般
|
||||
QUALITY_LOW = 4, // 较差
|
||||
QUALITY_MINIMAL = 5, // 最低
|
||||
QUALITY_COUNT = 6,
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// 算法枚举
|
||||
// ============================================
|
||||
#define ALGORITHM_GRAY 0
|
||||
#define ALGORITHM_DIFF 1
|
||||
#define ALGORITHM_H264 2
|
||||
#define ALGORITHM_RGB565 3
|
||||
|
||||
// ============================================
|
||||
// 质量配置结构体 (来自 commands.h)
|
||||
// ============================================
|
||||
struct QualityProfile {
|
||||
int maxFPS; // 最大帧率
|
||||
int maxWidth; // 最大宽度 (0=不限)
|
||||
int algorithm; // 压缩算法
|
||||
int bitRate; // kbps (仅H264使用)
|
||||
};
|
||||
|
||||
// 默认质量配置表
|
||||
static const QualityProfile g_QualityProfiles[QUALITY_COUNT] = {
|
||||
{25, 0, ALGORITHM_DIFF, 0 }, // Ultra: 25FPS, 原始, DIFF
|
||||
{20, 0, ALGORITHM_RGB565, 0 }, // High: 20FPS, 原始, RGB565
|
||||
{20, 1920, ALGORITHM_H264, 3000}, // Good: 20FPS, 1080P, H264
|
||||
{15, 1600, ALGORITHM_H264, 2000}, // Medium: 15FPS, 900P, H264
|
||||
{12, 1280, ALGORITHM_H264, 1200}, // Low: 12FPS, 720P, H264
|
||||
{8, 1024, ALGORITHM_H264, 800 }, // Minimal: 8FPS, 540P, H264
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// RTT 阈值表 (来自 commands.h)
|
||||
// ============================================
|
||||
// 行0: 直连模式, 行1: FRP代理模式
|
||||
static const int g_RttThresholds[2][QUALITY_COUNT] = {
|
||||
/* DIRECT */ { 30, 80, 150, 250, 400, INT_MAX },
|
||||
/* PROXY */ { 60, 160, 300, 500, 800, INT_MAX },
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// RTT到质量等级映射函数 (来自 commands.h)
|
||||
// ============================================
|
||||
inline int GetTargetQualityLevel(int rtt, int usingFRP)
|
||||
{
|
||||
int row = usingFRP ? 1 : 0;
|
||||
for (int level = 0; level < QUALITY_COUNT; level++) {
|
||||
if (rtt < g_RttThresholds[row][level]) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
return QUALITY_MINIMAL;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 防抖策略模拟器
|
||||
// ============================================
|
||||
class QualityDebouncer {
|
||||
public:
|
||||
// 防抖参数
|
||||
static const int DOWNGRADE_STABLE_COUNT = 2; // 降级所需稳定次数
|
||||
static const int UPGRADE_STABLE_COUNT = 5; // 升级所需稳定次数
|
||||
static const int DEFAULT_COOLDOWN_MS = 3000; // 默认冷却时间
|
||||
static const int RES_CHANGE_DOWNGRADE_COOLDOWN_MS = 15000; // 分辨率降级冷却
|
||||
static const int RES_CHANGE_UPGRADE_COOLDOWN_MS = 30000; // 分辨率升级冷却
|
||||
static const int STARTUP_DELAY_MS = 60000; // 启动延迟
|
||||
|
||||
QualityDebouncer()
|
||||
: m_currentLevel(QUALITY_HIGH)
|
||||
, m_stableCount(0)
|
||||
, m_lastChangeTime(0)
|
||||
, m_startTime(0)
|
||||
, m_enabled(true)
|
||||
{}
|
||||
|
||||
void Reset() {
|
||||
m_currentLevel = QUALITY_HIGH;
|
||||
m_stableCount = 0;
|
||||
m_lastChangeTime = 0;
|
||||
m_startTime = 0;
|
||||
}
|
||||
|
||||
void SetStartTime(uint64_t time) {
|
||||
m_startTime = time;
|
||||
}
|
||||
|
||||
void SetEnabled(bool enabled) {
|
||||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
// 评估并返回新的质量等级
|
||||
// 返回 -1 表示不改变
|
||||
int Evaluate(int targetLevel, uint64_t currentTime, bool resolutionChange = false) {
|
||||
if (!m_enabled) return -1;
|
||||
|
||||
// 启动延迟
|
||||
if (currentTime - m_startTime < STARTUP_DELAY_MS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 冷却时间检查
|
||||
uint64_t cooldown = DEFAULT_COOLDOWN_MS;
|
||||
if (resolutionChange) {
|
||||
cooldown = (targetLevel > m_currentLevel)
|
||||
? RES_CHANGE_DOWNGRADE_COOLDOWN_MS
|
||||
: RES_CHANGE_UPGRADE_COOLDOWN_MS;
|
||||
}
|
||||
|
||||
if (currentTime - m_lastChangeTime < cooldown) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 降级: 快速响应
|
||||
if (targetLevel > m_currentLevel) {
|
||||
m_stableCount++;
|
||||
if (m_stableCount >= DOWNGRADE_STABLE_COUNT) {
|
||||
int newLevel = targetLevel;
|
||||
m_currentLevel = newLevel;
|
||||
m_stableCount = 0;
|
||||
m_lastChangeTime = currentTime;
|
||||
return newLevel;
|
||||
}
|
||||
}
|
||||
// 升级: 谨慎处理,每次只升一级
|
||||
else if (targetLevel < m_currentLevel) {
|
||||
m_stableCount++;
|
||||
if (m_stableCount >= UPGRADE_STABLE_COUNT) {
|
||||
int newLevel = m_currentLevel - 1; // 只升一级
|
||||
m_currentLevel = newLevel;
|
||||
m_stableCount = 0;
|
||||
m_lastChangeTime = currentTime;
|
||||
return newLevel;
|
||||
}
|
||||
}
|
||||
// 目标等级等于当前等级
|
||||
else {
|
||||
m_stableCount = 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int GetCurrentLevel() const { return m_currentLevel; }
|
||||
int GetStableCount() const { return m_stableCount; }
|
||||
|
||||
private:
|
||||
int m_currentLevel;
|
||||
int m_stableCount;
|
||||
uint64_t m_lastChangeTime;
|
||||
uint64_t m_startTime;
|
||||
bool m_enabled;
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// 测试夹具
|
||||
// ============================================
|
||||
class QualityAdaptiveTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
debouncer.Reset();
|
||||
}
|
||||
|
||||
QualityDebouncer debouncer;
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// RTT 映射测试 - 直连模式
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Direct_Ultra) {
|
||||
// RTT < 30ms -> Ultra
|
||||
EXPECT_EQ(GetTargetQualityLevel(0, 0), QUALITY_ULTRA);
|
||||
EXPECT_EQ(GetTargetQualityLevel(10, 0), QUALITY_ULTRA);
|
||||
EXPECT_EQ(GetTargetQualityLevel(29, 0), QUALITY_ULTRA);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Direct_High) {
|
||||
// 30ms <= RTT < 80ms -> High
|
||||
EXPECT_EQ(GetTargetQualityLevel(30, 0), QUALITY_HIGH);
|
||||
EXPECT_EQ(GetTargetQualityLevel(50, 0), QUALITY_HIGH);
|
||||
EXPECT_EQ(GetTargetQualityLevel(79, 0), QUALITY_HIGH);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Direct_Good) {
|
||||
// 80ms <= RTT < 150ms -> Good
|
||||
EXPECT_EQ(GetTargetQualityLevel(80, 0), QUALITY_GOOD);
|
||||
EXPECT_EQ(GetTargetQualityLevel(100, 0), QUALITY_GOOD);
|
||||
EXPECT_EQ(GetTargetQualityLevel(149, 0), QUALITY_GOOD);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Direct_Medium) {
|
||||
// 150ms <= RTT < 250ms -> Medium
|
||||
EXPECT_EQ(GetTargetQualityLevel(150, 0), QUALITY_MEDIUM);
|
||||
EXPECT_EQ(GetTargetQualityLevel(200, 0), QUALITY_MEDIUM);
|
||||
EXPECT_EQ(GetTargetQualityLevel(249, 0), QUALITY_MEDIUM);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Direct_Low) {
|
||||
// 250ms <= RTT < 400ms -> Low
|
||||
EXPECT_EQ(GetTargetQualityLevel(250, 0), QUALITY_LOW);
|
||||
EXPECT_EQ(GetTargetQualityLevel(300, 0), QUALITY_LOW);
|
||||
EXPECT_EQ(GetTargetQualityLevel(399, 0), QUALITY_LOW);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Direct_Minimal) {
|
||||
// RTT >= 400ms -> Minimal
|
||||
EXPECT_EQ(GetTargetQualityLevel(400, 0), QUALITY_MINIMAL);
|
||||
EXPECT_EQ(GetTargetQualityLevel(500, 0), QUALITY_MINIMAL);
|
||||
EXPECT_EQ(GetTargetQualityLevel(1000, 0), QUALITY_MINIMAL);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// RTT 映射测试 - FRP代理模式
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_FRP_Ultra) {
|
||||
// RTT < 60ms -> Ultra (FRP模式阈值更宽松)
|
||||
EXPECT_EQ(GetTargetQualityLevel(0, 1), QUALITY_ULTRA);
|
||||
EXPECT_EQ(GetTargetQualityLevel(30, 1), QUALITY_ULTRA);
|
||||
EXPECT_EQ(GetTargetQualityLevel(59, 1), QUALITY_ULTRA);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_FRP_High) {
|
||||
// 60ms <= RTT < 160ms -> High
|
||||
EXPECT_EQ(GetTargetQualityLevel(60, 1), QUALITY_HIGH);
|
||||
EXPECT_EQ(GetTargetQualityLevel(100, 1), QUALITY_HIGH);
|
||||
EXPECT_EQ(GetTargetQualityLevel(159, 1), QUALITY_HIGH);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_FRP_Good) {
|
||||
// 160ms <= RTT < 300ms -> Good
|
||||
EXPECT_EQ(GetTargetQualityLevel(160, 1), QUALITY_GOOD);
|
||||
EXPECT_EQ(GetTargetQualityLevel(200, 1), QUALITY_GOOD);
|
||||
EXPECT_EQ(GetTargetQualityLevel(299, 1), QUALITY_GOOD);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_FRP_Medium) {
|
||||
// 300ms <= RTT < 500ms -> Medium
|
||||
EXPECT_EQ(GetTargetQualityLevel(300, 1), QUALITY_MEDIUM);
|
||||
EXPECT_EQ(GetTargetQualityLevel(400, 1), QUALITY_MEDIUM);
|
||||
EXPECT_EQ(GetTargetQualityLevel(499, 1), QUALITY_MEDIUM);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_FRP_Low) {
|
||||
// 500ms <= RTT < 800ms -> Low
|
||||
EXPECT_EQ(GetTargetQualityLevel(500, 1), QUALITY_LOW);
|
||||
EXPECT_EQ(GetTargetQualityLevel(600, 1), QUALITY_LOW);
|
||||
EXPECT_EQ(GetTargetQualityLevel(799, 1), QUALITY_LOW);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_FRP_Minimal) {
|
||||
// RTT >= 800ms -> Minimal
|
||||
EXPECT_EQ(GetTargetQualityLevel(800, 1), QUALITY_MINIMAL);
|
||||
EXPECT_EQ(GetTargetQualityLevel(1000, 1), QUALITY_MINIMAL);
|
||||
EXPECT_EQ(GetTargetQualityLevel(2000, 1), QUALITY_MINIMAL);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 质量配置表测试
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Profile_Ultra) {
|
||||
const auto& p = g_QualityProfiles[QUALITY_ULTRA];
|
||||
EXPECT_EQ(p.maxFPS, 25);
|
||||
EXPECT_EQ(p.maxWidth, 0); // 无限制
|
||||
EXPECT_EQ(p.algorithm, ALGORITHM_DIFF);
|
||||
EXPECT_EQ(p.bitRate, 0);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Profile_High) {
|
||||
const auto& p = g_QualityProfiles[QUALITY_HIGH];
|
||||
EXPECT_EQ(p.maxFPS, 20);
|
||||
EXPECT_EQ(p.maxWidth, 0); // 无限制
|
||||
EXPECT_EQ(p.algorithm, ALGORITHM_RGB565);
|
||||
EXPECT_EQ(p.bitRate, 0);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Profile_Good) {
|
||||
const auto& p = g_QualityProfiles[QUALITY_GOOD];
|
||||
EXPECT_EQ(p.maxFPS, 20);
|
||||
EXPECT_EQ(p.maxWidth, 1920); // 1080p
|
||||
EXPECT_EQ(p.algorithm, ALGORITHM_H264);
|
||||
EXPECT_EQ(p.bitRate, 3000);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Profile_Medium) {
|
||||
const auto& p = g_QualityProfiles[QUALITY_MEDIUM];
|
||||
EXPECT_EQ(p.maxFPS, 15);
|
||||
EXPECT_EQ(p.maxWidth, 1600); // 900p
|
||||
EXPECT_EQ(p.algorithm, ALGORITHM_H264);
|
||||
EXPECT_EQ(p.bitRate, 2000);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Profile_Low) {
|
||||
const auto& p = g_QualityProfiles[QUALITY_LOW];
|
||||
EXPECT_EQ(p.maxFPS, 12);
|
||||
EXPECT_EQ(p.maxWidth, 1280); // 720p
|
||||
EXPECT_EQ(p.algorithm, ALGORITHM_H264);
|
||||
EXPECT_EQ(p.bitRate, 1200);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Profile_Minimal) {
|
||||
const auto& p = g_QualityProfiles[QUALITY_MINIMAL];
|
||||
EXPECT_EQ(p.maxFPS, 8);
|
||||
EXPECT_EQ(p.maxWidth, 1024); // 540p
|
||||
EXPECT_EQ(p.algorithm, ALGORITHM_H264);
|
||||
EXPECT_EQ(p.bitRate, 800);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 防抖策略测试 - 启动延迟
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_StartupDelay) {
|
||||
debouncer.SetStartTime(0);
|
||||
|
||||
// 启动后60秒内不应改变
|
||||
EXPECT_EQ(debouncer.Evaluate(QUALITY_MINIMAL, 1000), -1);
|
||||
EXPECT_EQ(debouncer.Evaluate(QUALITY_MINIMAL, 30000), -1);
|
||||
EXPECT_EQ(debouncer.Evaluate(QUALITY_MINIMAL, 59999), -1);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_AfterStartupDelay) {
|
||||
debouncer.SetStartTime(0);
|
||||
|
||||
// 启动60秒后应该可以改变
|
||||
// 需要连续2次降级请求
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, 60000); // 第1次
|
||||
int result = debouncer.Evaluate(QUALITY_MINIMAL, 60001); // 第2次
|
||||
EXPECT_EQ(result, QUALITY_MINIMAL);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 防抖策略测试 - 降级
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_Downgrade_RequiresTwice) {
|
||||
debouncer.SetStartTime(0);
|
||||
uint64_t time = 60000;
|
||||
|
||||
// 第1次降级请求 - 不应立即执行
|
||||
int result = debouncer.Evaluate(QUALITY_MINIMAL, time);
|
||||
EXPECT_EQ(result, -1);
|
||||
EXPECT_EQ(debouncer.GetStableCount(), 1);
|
||||
|
||||
// 第2次降级请求 - 应该执行
|
||||
result = debouncer.Evaluate(QUALITY_MINIMAL, time + 100);
|
||||
EXPECT_EQ(result, QUALITY_MINIMAL);
|
||||
EXPECT_EQ(debouncer.GetCurrentLevel(), QUALITY_MINIMAL);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_Downgrade_ResetOnStable) {
|
||||
debouncer.SetStartTime(0);
|
||||
uint64_t time = 60000;
|
||||
|
||||
// 第1次降级请求
|
||||
debouncer.Evaluate(QUALITY_LOW, time);
|
||||
EXPECT_EQ(debouncer.GetStableCount(), 1);
|
||||
|
||||
// 目标等级恢复 - 计数应重置
|
||||
debouncer.Evaluate(QUALITY_HIGH, time + 100); // 当前等级
|
||||
EXPECT_EQ(debouncer.GetStableCount(), 0);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 防抖策略测试 - 升级
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_Upgrade_RequiresFiveTimes) {
|
||||
debouncer.SetStartTime(0);
|
||||
uint64_t time = 60000;
|
||||
|
||||
// 先降级到 MINIMAL
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, time);
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, time + 100);
|
||||
EXPECT_EQ(debouncer.GetCurrentLevel(), QUALITY_MINIMAL);
|
||||
|
||||
// 尝试升级到 ULTRA (需要5次)
|
||||
time += 5000; // 冷却后
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int result = debouncer.Evaluate(QUALITY_ULTRA, time + i * 100);
|
||||
EXPECT_EQ(result, -1); // 前4次不应执行
|
||||
EXPECT_EQ(debouncer.GetStableCount(), i + 1);
|
||||
}
|
||||
|
||||
// 第5次应该执行,但只升一级
|
||||
int result = debouncer.Evaluate(QUALITY_ULTRA, time + 500);
|
||||
EXPECT_EQ(result, QUALITY_LOW); // MINIMAL -> LOW (只升一级)
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_Upgrade_OneStepAtATime) {
|
||||
debouncer.SetStartTime(0);
|
||||
uint64_t time = 60000;
|
||||
|
||||
// 降级到 MINIMAL
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, time);
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, time + 100);
|
||||
EXPECT_EQ(debouncer.GetCurrentLevel(), QUALITY_MINIMAL);
|
||||
|
||||
// 多次升级请求,验证每次只升一级
|
||||
// 从 MINIMAL(5) 升到 HIGH(1) 需要4次升级,每次需要5个稳定请求
|
||||
time += 5000;
|
||||
int upgradeCount = 0;
|
||||
while (debouncer.GetCurrentLevel() > QUALITY_HIGH && upgradeCount < 10) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
debouncer.Evaluate(QUALITY_ULTRA, time); // 请求最高等级
|
||||
time += 100;
|
||||
}
|
||||
time += 5000; // 冷却
|
||||
upgradeCount++;
|
||||
}
|
||||
|
||||
// 最终应该回到 HIGH (或更高)
|
||||
EXPECT_LE(debouncer.GetCurrentLevel(), QUALITY_HIGH);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 防抖策略测试 - 冷却时间
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_DefaultCooldown) {
|
||||
debouncer.SetStartTime(0);
|
||||
uint64_t time = 60000;
|
||||
|
||||
// 执行一次降级
|
||||
debouncer.Evaluate(QUALITY_LOW, time);
|
||||
debouncer.Evaluate(QUALITY_LOW, time + 100);
|
||||
EXPECT_EQ(debouncer.GetCurrentLevel(), QUALITY_LOW);
|
||||
|
||||
// 冷却期内不应再次改变
|
||||
int result = debouncer.Evaluate(QUALITY_MINIMAL, time + 200);
|
||||
EXPECT_EQ(result, -1);
|
||||
|
||||
result = debouncer.Evaluate(QUALITY_MINIMAL, time + 2999);
|
||||
EXPECT_EQ(result, -1);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_AfterCooldown) {
|
||||
debouncer.SetStartTime(0);
|
||||
uint64_t time = 60000;
|
||||
|
||||
// 执行一次降级
|
||||
debouncer.Evaluate(QUALITY_LOW, time);
|
||||
debouncer.Evaluate(QUALITY_LOW, time + 100);
|
||||
|
||||
// 冷却后应该可以再次改变
|
||||
time += 3100;
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, time);
|
||||
int result = debouncer.Evaluate(QUALITY_MINIMAL, time + 100);
|
||||
EXPECT_EQ(result, QUALITY_MINIMAL);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 防抖策略测试 - 分辨率变化冷却
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_ResolutionChange_DowngradeCooldown) {
|
||||
debouncer.SetStartTime(0);
|
||||
uint64_t time = 60000;
|
||||
|
||||
// 执行一次带分辨率变化的降级
|
||||
debouncer.Evaluate(QUALITY_LOW, time, true); // resolutionChange=true
|
||||
debouncer.Evaluate(QUALITY_LOW, time + 100, true);
|
||||
|
||||
// 分辨率降级冷却15秒
|
||||
int result = debouncer.Evaluate(QUALITY_MINIMAL, time + 14000, true);
|
||||
EXPECT_EQ(result, -1);
|
||||
|
||||
// 15秒后可以
|
||||
time += 16000;
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, time, true);
|
||||
result = debouncer.Evaluate(QUALITY_MINIMAL, time + 100, true);
|
||||
EXPECT_EQ(result, QUALITY_MINIMAL);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_ResolutionChange_UpgradeCooldown) {
|
||||
debouncer.SetStartTime(0);
|
||||
uint64_t time = 60000;
|
||||
|
||||
// 先降级到 MINIMAL (不带分辨率变化,使用默认冷却)
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, time);
|
||||
debouncer.Evaluate(QUALITY_MINIMAL, time + 100);
|
||||
EXPECT_EQ(debouncer.GetCurrentLevel(), QUALITY_MINIMAL);
|
||||
|
||||
// 等待足够长时间(>30秒)后,进行带分辨率变化的升级
|
||||
time += 35000; // 超过30秒冷却
|
||||
for (int i = 0; i < 5; i++) {
|
||||
debouncer.Evaluate(QUALITY_ULTRA, time + i * 100, true); // resolutionChange=true
|
||||
}
|
||||
// 应该成功升级一级 (MINIMAL -> LOW)
|
||||
EXPECT_EQ(debouncer.GetCurrentLevel(), QUALITY_LOW);
|
||||
|
||||
// 30秒内不应再次升级(带分辨率变化的升级需要30秒冷却)
|
||||
time += 1000;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
debouncer.Evaluate(QUALITY_ULTRA, time + i * 100, true);
|
||||
}
|
||||
EXPECT_EQ(debouncer.GetCurrentLevel(), QUALITY_LOW); // 未改变(还在30秒冷却期内)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 禁用自适应测试
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, Debounce_Disabled) {
|
||||
debouncer.SetStartTime(0);
|
||||
debouncer.SetEnabled(false);
|
||||
|
||||
uint64_t time = 60000;
|
||||
int result = debouncer.Evaluate(QUALITY_MINIMAL, time);
|
||||
EXPECT_EQ(result, -1);
|
||||
|
||||
result = debouncer.Evaluate(QUALITY_MINIMAL, time + 100);
|
||||
EXPECT_EQ(result, -1);
|
||||
|
||||
// 质量等级应保持不变
|
||||
EXPECT_EQ(debouncer.GetCurrentLevel(), QUALITY_HIGH);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 边界值测试
|
||||
// ============================================
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Boundary_Zero) {
|
||||
EXPECT_EQ(GetTargetQualityLevel(0, 0), QUALITY_ULTRA);
|
||||
EXPECT_EQ(GetTargetQualityLevel(0, 1), QUALITY_ULTRA);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Boundary_Negative) {
|
||||
// 负值RTT应该仍返回最高质量
|
||||
EXPECT_EQ(GetTargetQualityLevel(-1, 0), QUALITY_ULTRA);
|
||||
EXPECT_EQ(GetTargetQualityLevel(-100, 0), QUALITY_ULTRA);
|
||||
}
|
||||
|
||||
TEST_F(QualityAdaptiveTest, RTT_Boundary_VeryHigh) {
|
||||
// 非常高的RTT
|
||||
EXPECT_EQ(GetTargetQualityLevel(10000, 0), QUALITY_MINIMAL);
|
||||
EXPECT_EQ(GetTargetQualityLevel(100000, 0), QUALITY_MINIMAL);
|
||||
EXPECT_EQ(GetTargetQualityLevel(INT_MAX - 1, 0), QUALITY_MINIMAL);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 常量验证测试
|
||||
// ============================================
|
||||
|
||||
TEST(QualityConstantsTest, QualityLevelEnum) {
|
||||
EXPECT_EQ(QUALITY_DISABLED, -2);
|
||||
EXPECT_EQ(QUALITY_ADAPTIVE, -1);
|
||||
EXPECT_EQ(QUALITY_ULTRA, 0);
|
||||
EXPECT_EQ(QUALITY_HIGH, 1);
|
||||
EXPECT_EQ(QUALITY_GOOD, 2);
|
||||
EXPECT_EQ(QUALITY_MEDIUM, 3);
|
||||
EXPECT_EQ(QUALITY_LOW, 4);
|
||||
EXPECT_EQ(QUALITY_MINIMAL, 5);
|
||||
EXPECT_EQ(QUALITY_COUNT, 6);
|
||||
}
|
||||
|
||||
TEST(QualityConstantsTest, ProfileCount) {
|
||||
// 应该有 QUALITY_COUNT 个配置
|
||||
EXPECT_EQ(sizeof(g_QualityProfiles) / sizeof(g_QualityProfiles[0]),
|
||||
static_cast<size_t>(QUALITY_COUNT));
|
||||
}
|
||||
|
||||
TEST(QualityConstantsTest, ThresholdCount) {
|
||||
// 每行应该有 QUALITY_COUNT 个阈值
|
||||
EXPECT_EQ(sizeof(g_RttThresholds[0]) / sizeof(g_RttThresholds[0][0]),
|
||||
static_cast<size_t>(QUALITY_COUNT));
|
||||
}
|
||||
|
||||
TEST(QualityConstantsTest, ThresholdIncreasing) {
|
||||
// 阈值应该递增
|
||||
for (int row = 0; row < 2; row++) {
|
||||
for (int i = 0; i < QUALITY_COUNT - 1; i++) {
|
||||
EXPECT_LT(g_RttThresholds[row][i], g_RttThresholds[row][i + 1])
|
||||
<< "Row " << row << ", index " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(QualityConstantsTest, FRPThresholdsHigher) {
|
||||
// FRP模式阈值应该比直连模式高
|
||||
for (int i = 0; i < QUALITY_COUNT - 1; i++) {
|
||||
EXPECT_GT(g_RttThresholds[1][i], g_RttThresholds[0][i])
|
||||
<< "Index " << i;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 参数化测试:RTT值遍历
|
||||
// ============================================
|
||||
|
||||
class RTTMappingTest : public ::testing::TestWithParam<std::tuple<int, int, int>> {};
|
||||
|
||||
TEST_P(RTTMappingTest, RTT_Mapping) {
|
||||
auto [rtt, usingFRP, expectedLevel] = GetParam();
|
||||
EXPECT_EQ(GetTargetQualityLevel(rtt, usingFRP), expectedLevel);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
DirectMode,
|
||||
RTTMappingTest,
|
||||
::testing::Values(
|
||||
std::make_tuple(0, 0, QUALITY_ULTRA),
|
||||
std::make_tuple(29, 0, QUALITY_ULTRA),
|
||||
std::make_tuple(30, 0, QUALITY_HIGH),
|
||||
std::make_tuple(79, 0, QUALITY_HIGH),
|
||||
std::make_tuple(80, 0, QUALITY_GOOD),
|
||||
std::make_tuple(149, 0, QUALITY_GOOD),
|
||||
std::make_tuple(150, 0, QUALITY_MEDIUM),
|
||||
std::make_tuple(249, 0, QUALITY_MEDIUM),
|
||||
std::make_tuple(250, 0, QUALITY_LOW),
|
||||
std::make_tuple(399, 0, QUALITY_LOW),
|
||||
std::make_tuple(400, 0, QUALITY_MINIMAL),
|
||||
std::make_tuple(1000, 0, QUALITY_MINIMAL)
|
||||
)
|
||||
);
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
FRPMode,
|
||||
RTTMappingTest,
|
||||
::testing::Values(
|
||||
std::make_tuple(0, 1, QUALITY_ULTRA),
|
||||
std::make_tuple(59, 1, QUALITY_ULTRA),
|
||||
std::make_tuple(60, 1, QUALITY_HIGH),
|
||||
std::make_tuple(159, 1, QUALITY_HIGH),
|
||||
std::make_tuple(160, 1, QUALITY_GOOD),
|
||||
std::make_tuple(299, 1, QUALITY_GOOD),
|
||||
std::make_tuple(300, 1, QUALITY_MEDIUM),
|
||||
std::make_tuple(499, 1, QUALITY_MEDIUM),
|
||||
std::make_tuple(500, 1, QUALITY_LOW),
|
||||
std::make_tuple(799, 1, QUALITY_LOW),
|
||||
std::make_tuple(800, 1, QUALITY_MINIMAL),
|
||||
std::make_tuple(2000, 1, QUALITY_MINIMAL)
|
||||
)
|
||||
);
|
||||
|
||||
// ============================================
|
||||
// 质量配置合理性测试
|
||||
// ============================================
|
||||
|
||||
TEST(QualityProfileTest, FPSDecreasing) {
|
||||
// FPS 应该随质量降低而减少
|
||||
for (int i = 0; i < QUALITY_COUNT - 1; i++) {
|
||||
EXPECT_GE(g_QualityProfiles[i].maxFPS, g_QualityProfiles[i + 1].maxFPS)
|
||||
<< "Level " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(QualityProfileTest, MaxWidthDecreasing) {
|
||||
// maxWidth 应该随质量降低而减少(除了0表示不限)
|
||||
int prevWidth = INT_MAX;
|
||||
for (int i = 0; i < QUALITY_COUNT; i++) {
|
||||
int width = g_QualityProfiles[i].maxWidth;
|
||||
if (width > 0) {
|
||||
EXPECT_LE(width, prevWidth) << "Level " << i;
|
||||
prevWidth = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(QualityProfileTest, BitRateDecreasing) {
|
||||
// H264 bitRate 应该随质量降低而减少
|
||||
int prevBitRate = INT_MAX;
|
||||
for (int i = 0; i < QUALITY_COUNT; i++) {
|
||||
if (g_QualityProfiles[i].algorithm == ALGORITHM_H264) {
|
||||
int bitRate = g_QualityProfiles[i].bitRate;
|
||||
EXPECT_LE(bitRate, prevBitRate) << "Level " << i;
|
||||
prevBitRate = bitRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user