Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 54 additions & 18 deletions tests/e2e/specs/progress-loop.e2e.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
// the screen's every frame, up to 144 times a second for a video of 24 to 60. It waits for the
// video's next frame now (requestVideoFrameCallback), and goes on after a pause, which such a
// wait never sees end: play() asks for it again.
//
// Counted against the frames the video really presented, not against a second of the clock:
// some Linux CI runners present only 1 to 5 frames in the first second, where others show 23
// to 28 and Windows 28, and a count per second failed there twice in a row (#641, #644).

import {browser, expect} from '@wdio/globals';

const fixtureUrl = () => `${globalThis.__E2E_FIXTURES_ORIGIN__}/fixtures/frames-24fps.mp4`;

/** How many frames each measurement waits for. At 24 fps, half a second of playback. */
const FRAMES = 12;

/**
* Opens the player with no source, autoplay and remembered positions off.
* @return {Promise<void>}
Expand All @@ -22,24 +29,52 @@ async function openEmptyPlayer() {
}

/**
* How often the client was told the time during a second of playback.
* @return {Promise<number>}
* The progress loop's rounds, and how often the client was told the time, while the video
* presents the next FRAMES frames.
* @return {Promise<{rounds: number, times: number, frames: number, ms: number}>}
*/
async function updatesInASecond() {
async function roundsOverFrames() {
await browser.execute(() => {
const client = window.fastStream;
if (!client.__zzCount) {
if (!window.__zzCount) {
window.__zzCount = {rounds: 0, times: 0, frames: 0, start: 0};
const update = client.updateTime.bind(client);
client.__zzCount = {n: 0};
client.updateTime = (time) => {
client.__zzCount.n++;
window.__zzCount.times++;
return update(time);
};
const controller = client.interfaceController;
const schedule = controller.scheduleProgressFrame.bind(controller);
controller.scheduleProgressFrame = () => {
window.__zzCount.rounds++;
schedule();
};
const video = client.currentVideo;
const onFrame = () => {
window.__zzCount.frames++;
video.requestVideoFrameCallback(onFrame);
};
video.requestVideoFrameCallback(onFrame);
}
client.__zzCount.n = 0;
Object.assign(window.__zzCount, {rounds: 0, times: 0, frames: 0, start: performance.now()});
});
await browser.waitUntil(async () => (await browser.execute(() => window.__zzCount.frames)) >= FRAMES,
{timeout: 30000, interval: 50, timeoutMsg: `the video did not present ${FRAMES} frames in 30 s`});
return browser.execute(() => {
const {rounds, times, frames, start} = window.__zzCount;
return {rounds, times, frames, ms: Math.round(performance.now() - start)};
});
await browser.pause(1000);
return browser.execute(() => window.fastStream.__zzCount.n);
}

/**
* One round a frame, each telling the client the time (timeupdate tells it too, so the times can
* be more). At the screen's 60 a second it would be 2.5 rounds for this 24 fps video.
* @param {{rounds: number, times: number, frames: number}} seen
*/
function expectOneRoundAFrame(seen) {
expect(seen.rounds).toBeGreaterThanOrEqual(seen.frames - 2);
expect(seen.rounds).toBeLessThanOrEqual(seen.frames * 1.5);
expect(seen.times).toBeGreaterThanOrEqual(seen.rounds);
}

describe('The progress loop of a short video', function() {
Expand All @@ -59,21 +94,22 @@ describe('The progress loop of a short video', function() {
}), {timeout: 30000, timeoutMsg: 'the video never loaded'});

const play = () => browser.execute(() => {
window.fastStream.currentVideo.muted = true;
const video = window.fastStream.currentVideo;
video.muted = true;
// The fixture lasts 4 s: a runner that presents a frame a second must not reach its end.
video.loop = true;
return window.fastStream.play();
});
await play();
const playing = await updatesInASecond();
console.log(` updates in a second of playback: ${playing}`);
// A 24 fps video: about 24 rounds, not the screen's 60 (or more).
expect(playing).toBeGreaterThan(10);
expect(playing).toBeLessThan(40);
const playing = await roundsOverFrames();
console.log(` playing: ${JSON.stringify(playing)}`);
expectOneRoundAFrame(playing);

await browser.execute(() => window.fastStream.pause());
await browser.pause(300);
await play();
const again = await updatesInASecond();
console.log(` updates in a second after a pause: ${again}`);
expect(again).toBeGreaterThan(10);
const again = await roundsOverFrames();
console.log(` after a pause: ${JSON.stringify(again)}`);
expectOneRoundAFrame(again);
});
});
3 changes: 3 additions & 0 deletions tests/workflows/toolchain-updates.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ setup() {
# The recipes' own scripts, as main has them.
mkdir -p tools tests/e2e/live-specs
cp "$REPO/tools/check-toolchain.mjs" "$REPO/tools/check-patched-updates.mjs" "$REPO/tools/setup-dev-profile.mjs" tools/
# uBlock Origin at the version these scenarios start from, whatever main pins: the
# toolchain's own pull request moves that pin, and went red here for it (#646).
sed -i "s/^ version: '[^']*',\$/ version: '1.75.0',/" tools/setup-dev-profile.mjs
# The player libraries, as main has them: two, at old releases.
printf '{\n "plyr": "3.8.5",\n "dplayer": "1.27.1"\n}\n' > tests/e2e/live-specs/player-libraries.json
git add -A && git commit -q -m init && git remote add origin "$T/origin.git" && git push -q origin main
Expand Down
Loading