blob: 91424e435e1b4f4d880b6533d27e3edb17a1bee5 [file] [log] [blame]
<!DOCTYPE html>
<title>Test that stalled, timeupdate and waiting events are sent when media load stalls in the middle.</title>
<video id="test_video"></video>
<div id="log_console"></div>
<script src=../../media-resources/media-file.js></script>
<script src="/w3c/resources/testharness.js"></script>
<script src="/w3c/resources/testharnessreport.js"></script>
<script>
async_test(t => {
let video = document.getElementById('test_video');
let log_console = document.getElementById('log_console');
// For debugging.
function logMessage(msg) {
let span = document.createElement("span");
span.innerHTML = msg + '<br>';
log_console.appendChild(span);
};
function logEvent(e) {
logMessage('event: ' + e.type);
};
playback_ew = new EventWatcher(t, video, [
'canplay',
'canplaythrough',
'durationchange',
'loadedmetadata',
'loadeddata',
'play',
'playing',
'waiting']);
// The stalled event needs a separate watcher as it can be fired at any
// point during the sequence of other playback events. Stalled is triggered
// by prolonged network inactivity.
stalled_ew = new EventWatcher(t, video, [
'stalled']);
// This helper is an alternative to EventWatcher for events that fire on a
// recurring basis. EventWatcher is not suitable because you must always be
// "waiting" for the event to fire every time, whereas this method allows
// you to just verify that it fired once and move on.
function waitForRecurringEvent(name) {
let resolve_cb;
let promise = new Promise(function(resolve, reject) {
resolve_cb = resolve;
});
video.addEventListener(name, t.step_func((evt) => resolve_cb(evt)));
return promise;
}
// Monitor timeupdate w.r.t. readyState throughout test. Time should not
// advance while readyState <= HAVE_CURRENT_DATA.
let timeAtStartOfWaiting = null;
video.addEventListener('timeupdate', t.step_func(function(e) {
logMessage('event: timeupdate at time: ' + e.target.currentTime +' readystate:' + e.target.readyState);
if (e.target.readyState <= HTMLMediaElement.HAVE_CURRENT_DATA) {
if (timeAtStartOfWaiting == null) {
// Note the time when waiting begins.
timeAtStartOfWaiting = e.target.currentTime;
} else {
// Verify that current time is not advancing while waiting.
assert_equals(e.target.currentTime, timeAtStartOfWaiting);
}
} else if (timeAtStartOfWaiting != null) {
// Waiting has ended, so clear timeAtStartOfWaiting.
timeAtStartOfWaiting = null;
}
}));
// NOTE: Event sequence verification is achieved by chaining together
// promises via then(). To verify separate parallel event sequences (e.g.
// playback vs network), we setup separate chains of promises. Promise.all
// ensures that all separate sequences complete.
// Verify playback progresses then runs out of data.
Promise.all([
// First wait for the resource to load.
playback_ew.wait_for('durationchange').then(logEvent)
.then(() => playback_ew.wait_for('loadedmetadata')).then(logEvent)
.then(() => playback_ew.wait_for('loadeddata')).then(logEvent)
.then(() => playback_ew.wait_for('canplay')).then(logEvent)
.then(() => playback_ew.wait_for('canplaythrough')).then(logEvent)
.then(t.step_func(function() {
assert_true(video.readyState > HTMLMediaElement.HAVE_CURRENT_DATA);
}))
// Now play the file and wait for playback to stall (fire waiting).
.then(t.step_func(function() {
video.play();
// NOTE: setting the wait_for here because we will miss it if we do
// it after the play call resolves its promise.
return playback_ew.wait_for('play').then(logEvent);
}))
.then(() => playback_ew.wait_for('playing')).then(logEvent)
// Now observe waiting event and verify readyState
.then(() => playback_ew.wait_for('waiting')).then(logEvent)
.then(t.step_func(function(){
assert_equals(video.readyState, HTMLMediaElement.HAVE_CURRENT_DATA);
})),
// timeupdate should fire throughout playback. Make sure we see one.
waitForRecurringEvent('timeupdate').then(logEvent),
// progress should fire throughout download. Make sure we see one.
// Later the download should stall.
waitForRecurringEvent('progress').then(logEvent)
.then(() => stalled_ew.wait_for('stalled')).then(logEvent)
// Verify download and playback resume.
]).then(() => Promise.all([
// Playback should resume when download again makes progress.
waitForRecurringEvent('progress').then(logEvent),
// timeupdate should fire throughout playback. Make sure we see one.
waitForRecurringEvent('timeupdate').then(logEvent),
// Verify correct sequence of playback events.
playback_ew.wait_for('canplay').then(logEvent)
.then(t.step_func(function() {
assert_true(video.readyState > HTMLMediaElement.HAVE_CURRENT_DATA);
}))
.then(() => playback_ew.wait_for('playing')).then(logEvent)
.then(() => playback_ew.wait_for('canplaythrough')).then(logEvent)
])).then(t.step_func_done());
// Find a supported media file.
var mediaFile = "content/test.ogv";
var mimeType = mimeTypeForFile(mediaFile);
// URL will load part of the file, pause for 8 seconds, then load the rest.
// The delay of 8 seconds is chosen to reduce flakiness in waiting for the
// stalled event, which should arrive after roughly 3 seconds of inactivity.
video.src = "http://127.0.0.1:8000/resources/load-and-stall.php?name=../../../media/" + mediaFile + "&mimeType=" + mimeType + "&stallAt=100000&stallFor=8";
}, "Stalled download pauses playback. When download resumes playback continues. Verify events and readyStates.");
</script>