53 lines
2.4 KiB
TypeScript
53 lines
2.4 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|||
|
|
import { AdaptiveDesktopQuality, automaticBitrateMbps, normalizeAdaptiveProfiles } from "./rd-adaptive-quality.js";
|
||
|
|
|
||
|
|
const profiles = [
|
||
|
|
{ maxHeight: 720, width: 1280, height: 720, fps: 60 },
|
||
|
|
{ maxHeight: 1080, width: 1920, height: 1080, fps: 60 },
|
||
|
|
{ maxHeight: -1, width: 2560, height: 1440, fps: 144 },
|
||
|
|
];
|
||
|
|
|
||
|
|
describe("adaptive remote desktop quality", () => {
|
||
|
|
test("ranks native resolution first and starts at the best profile", () => {
|
||
|
|
const ranked = normalizeAdaptiveProfiles(profiles);
|
||
|
|
expect(ranked.map((profile) => profile.height)).toEqual([1440, 1080, 720]);
|
||
|
|
const targets: any[] = [];
|
||
|
|
const controller = new AdaptiveDesktopQuality((target) => targets.push(target));
|
||
|
|
controller.setProfiles(profiles);
|
||
|
|
controller.start(0);
|
||
|
|
expect(targets[0].height).toBe(1440);
|
||
|
|
expect(targets[0].fps).toBe(144);
|
||
|
|
expect(targets[0].bitrateMbps).toBe(automaticBitrateMbps(ranked[0]));
|
||
|
|
});
|
||
|
|
|
||
|
|
test("requires sustained congestion before reducing bitrate and then profile", () => {
|
||
|
|
const targets: any[] = [];
|
||
|
|
const controller = new AdaptiveDesktopQuality((target) => targets.push(target), { cooldownMs: 1 });
|
||
|
|
controller.setProfiles(profiles);
|
||
|
|
controller.start(0);
|
||
|
|
const bad = { rttMs: 300, video: { lossPercent: 7, framesDroppedDelta: 4, framesPerSecond: 40 } };
|
||
|
|
expect(controller.sample(bad, { fps: 144 }, 10)).toBeNull();
|
||
|
|
expect(controller.sample(bad, { fps: 144 }, 11)?.height).toBe(1440);
|
||
|
|
expect(controller.sample(bad, { fps: 144 }, 20)).toBeNull();
|
||
|
|
expect(controller.sample(bad, { fps: 144 }, 21)?.height).toBe(1080);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("slowly recovers after stable feedback", () => {
|
||
|
|
const controller = new AdaptiveDesktopQuality(() => {}, {
|
||
|
|
cooldownMs: 1,
|
||
|
|
badSamplesRequired: 1,
|
||
|
|
stableSamplesRequired: 2,
|
||
|
|
});
|
||
|
|
controller.setProfiles(profiles);
|
||
|
|
controller.start(0);
|
||
|
|
const bad = { rttMs: 300, video: { lossPercent: 8, framesDroppedDelta: 5, framesPerSecond: 20 } };
|
||
|
|
controller.sample(bad, { fps: 144 }, 2);
|
||
|
|
controller.sample(bad, { fps: 144 }, 4);
|
||
|
|
expect(controller.current()?.profile.height).toBe(1080);
|
||
|
|
const stable = { rttMs: 30, video: { lossPercent: 0, framesDroppedDelta: 0, framesPerSecond: 60, jitterBufferMs: 5 } };
|
||
|
|
expect(controller.sample(stable, { fps: 60 }, 6)).toBeNull();
|
||
|
|
const recovered = controller.sample(stable, { fps: 60 }, 7);
|
||
|
|
expect(recovered?.height).toBe(1440);
|
||
|
|
});
|
||
|
|
});
|