Commit 71d6ab0d authored by Thomas Guilbert's avatar Thomas Guilbert Committed by Commit Bot

Cleanup WebCodecs WPT

A lot of WebCodecs tests are using async_test, which lead to time outs
when there is an error, and potentially missed test failures.

Additionally, a lot of code verifying configs, and which calls are
allowed in which codec state are common to many test files. This CL
extracts some of common elements into its own utils file

Bug: 1094096, 1094182
Change-Id: I7dd6c9ab76959ed6adb86fd2e6bf94d21610838a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2389008
Commit-Queue: Thomas Guilbert <tguilbert@chromium.org>
Reviewed-by: default avatarPhilip Jägenstedt <foolip@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804479}
parent 4df2ec50
...@@ -3,70 +3,14 @@ ...@@ -3,70 +3,14 @@
<title>Test the AudioDecoder API.</title> <title>Test the AudioDecoder API.</title>
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="/webcodecs/utils.js"></script>
<script> <script>
// Calls done after giving async output/error callbacks a final chance to run. const defaultConfig = {
async function asyncDone(test) { codec: "opus",
test.step_timeout(test.step_func_done(), 0); sampleRate: 48000,
} numberOfChannels: 2
};
async_test(async (t) => {
// AudioDecoderInit lacks required fields.
assert_throws_js(TypeError, () => { new AudioDecoder({}); });
// AudioDecoderInit has required fields.
let decoder = new AudioDecoder({
output(chunk) { t.unreached_func("Unexpected output").call(); },
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
});
asyncDone(t);
}, 'Test AudioDecoder construction');
async_test(async (t) => {
let decoder = new AudioDecoder({
output(chunk) { t.unreached_func("Unexpected output").call(); },
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
});
let config = {
sampleRate: 48000,
numberOfChannels: 2
}
assert_equals(decoder.state, "unconfigured");
// Empty codec rejected.
config.codec = '';
assert_throws_js(TypeError, () => { decoder.configure(config); });
// Invalid codec rejected.
config.codec = 'bogus';
assert_throws_js(TypeError, () => { decoder.configure(config); });
// Video codec rejected.
config.codec = 'vp8';
assert_throws_js(TypeError, () => { decoder.configure(config); });
// Codec with mime type rejected.
config.codec = 'audio/webm; codecs="opus"';
assert_throws_js(TypeError, () => { decoder.configure(config); });
// Valid audio codec accepted.
config.codec = 'opus';
decoder.configure(config);
assert_equals(decoder.state, "configured");
// Test we can configure after a reset.
decoder.reset()
assert_equals(decoder.state, "unconfigured");
decoder.configure(config);
assert_equals(decoder.state, "configured");
asyncDone(t);
}, 'Test AudioDecoder.configure() codec validity');
function getFakeChunk() { function getFakeChunk() {
return new EncodedAudioChunk({ return new EncodedAudioChunk({
...@@ -77,79 +21,44 @@ function getFakeChunk() { ...@@ -77,79 +21,44 @@ function getFakeChunk() {
} }
promise_test(t => { promise_test(t => {
let decoder = new AudioDecoder({ // AudioDecoderInit lacks required fields.
output(frame) { assert_throws_js(TypeError, () => { new AudioDecoder({}); });
t.step(() => {
throw "unexpected output"; // AudioDecoderInit has required fields.
}); let decoder = new AudioDecoder(getDefaultCodecInit(t));
},
error(e) {
t.step(() => {
throw "unexpected error";
});
}
});
assert_equals(decoder.state, "unconfigured");
decoder.close(); decoder.close();
assert_equals(decoder.state, "closed"); return endAfterEventLoopTurn();
}, 'Test AudioDecoder construction');
let config = {
sampleRate: 48000,
numberOfChannels: 2,
codec: 'opus'
}
let fakeChunk = getFakeChunk();
assert_throws_dom("InvalidStateError",
() => decoder.configure(config),
"configure");
assert_throws_dom("InvalidStateError",
() => decoder.decode(fakeChunk),
"decode");
assert_throws_dom("InvalidStateError",
() => decoder.reset(),
"reset");
assert_throws_dom("InvalidStateError",
() => decoder.close(),
"close");
return promise_rejects_dom(t, 'InvalidStateError', decoder.flush(), 'flush');
}, 'Closed decoder');
promise_test(t => { promise_test(t => {
let decoder = new AudioDecoder({ let decoder = new AudioDecoder(getDefaultCodecInit(t));
output(frame) {
t.step(() => {
throw "unexpected output";
});
},
error(e) {
t.step(() => {
throw "unexpected error";
});
}
});
assert_equals(decoder.state, "unconfigured"); let badCodecsList = [
'', // Empty codec
'bogus', // Non exsitent codec
'vp8', // Video codec
'audio/webm; codecs="opus"' // Codec with mime type
]
// Reseting an unconfigured encoder is a no-op. testConfigurations(decoder, defaultConfig, badCodecsList);
decoder.reset();
assert_equals(decoder.state, "unconfigured");
let config = { return endAfterEventLoopTurn();
sampleRate: 48000, }, 'Test AudioDecoder.configure()');
numberOfChannels: 2,
codec: 'opus'
}
let fakeChunk = getFakeChunk(); promise_test(t => {
let decoder = new AudioDecoder(getDefaultCodecInit(t));
return testClosedCodec(t, decoder, defaultConfig, getFakeChunk());
}, 'Verify closed AudioDecoder operations');
promise_test(t => {
let decoder = new AudioDecoder(getDefaultCodecInit(t));
assert_throws_dom("InvalidStateError", return testUnconfiguredCodec(t, decoder, getFakeChunk());
() => decoder.decode(fakeChunk), }, 'Verify unconfigured AudioDecoder operations');
"decode");
return promise_rejects_dom(t, 'InvalidStateError', decoder.flush(), 'flush');
}, 'Unconfigured decoder');
</script> </script>
......
function makeImageBitmap(width, height) {
let canvas = new OffscreenCanvas(width, height);
let ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgba(50, 100, 150, 255)';
ctx.fillRect(0, 0, width, height);
return canvas.transferToImageBitmap();
}
// Gives a chance to pending output and error callbacks to complete before
// resolving.
function endAfterEventLoopTurn() {
return new Promise(resolve => step_timeout(resolve, 0));
}
// Returns a codec initialization with callbacks that expected to not be called.
function getDefaultCodecInit(test) {
return {
output: test.unreached_func("unexpected output"),
error: test.unreached_func("unexpected error"),
}
}
// Checks that codec can be configured, reset, reconfigured, and that incomplete
// or invalid configs throw errors immediately.
function testConfigurations(codec, validCondig, invalidCodecs) {
assert_equals(codec.state, "unconfigured");
const requiredConfigPairs = validCondig;
let incrementalConfig = {};
for (let key in requiredConfigPairs) {
// Configure should fail while required keys are missing.
assert_throws_js(TypeError, () => { codec.configure(incrementalConfig); });
incrementalConfig[key] = requiredConfigPairs[key];
assert_equals(codec.state, "unconfigured");
}
// Configure should pass once incrementalConfig meets all requirements.
codec.configure(incrementalConfig);
assert_equals(codec.state, "configured");
// We should be able to reconfigure the codec.
codec.configure(incrementalConfig);
assert_equals(codec.state, "configured");
let config = incrementalConfig;
invalidCodecs.forEach(badCodec => {
// Invalid codecs should fail.
config.codec = badCodec;
assert_throws_js(TypeError, () => { codec.configure(config); }, badCodec);
})
// The failed configures should not affect the current config.
assert_equals(codec.state, "configured");
// Test we can configure after a reset.
codec.reset()
assert_equals(codec.state, "unconfigured");
codec.configure(validCondig);
assert_equals(codec.state, "configured");
}
// Performs an encode or decode with the provided input, depending on whether
// the passed codec is an encoder or a decoder.
function encodeOrDecodeShouldThrow(codec, input) {
// We are testing encode/decode on codecs in invalid states.
assert_not_equals(codec.state, "configured");
if (codec.decode) {
assert_throws_dom("InvalidStateError",
() => codec.decode(input),
"decode");
} else if (codec.encode) {
// Encoders consume frames, so clone it to be safe.
assert_throws_dom("InvalidStateError",
() => codec.encode(input.clone()),
"encode");
} else {
assert_unreached("Codec should have encode or decode function");
}
}
// Makes sure that we cannot close, configure, reset, flush, decode or encode a
// closed codec.
function testClosedCodec(test, codec, validconfig, codecInput) {
assert_equals(codec.state, "unconfigured");
codec.close();
assert_equals(codec.state, "closed");
assert_throws_dom("InvalidStateError",
() => codec.configure(validconfig),
"configure");
assert_throws_dom("InvalidStateError",
() => codec.reset(),
"reset");
assert_throws_dom("InvalidStateError",
() => codec.close(),
"close");
encodeOrDecodeShouldThrow(codec, codecInput);
return promise_rejects_dom(test, 'InvalidStateError', codec.flush(), 'flush');
}
// Makes sure we cannot flush, encode or decode with an unconfigured coded, and
// that reset is a valid no-op.
function testUnconfiguredCodec(test, codec, codecInput) {
assert_equals(codec.state, "unconfigured");
// Configure() and Close() are valid operations that would transition us into
// a different state.
// Resetting an unconfigured encoder is a no-op.
codec.reset();
assert_equals(codec.state, "unconfigured");
encodeOrDecodeShouldThrow(codec, codecInput);
return promise_rejects_dom(test, 'InvalidStateError', codec.flush(), 'flush');
}
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
<title>Test the VideoDecoder API.</title> <title>Test the VideoDecoder API.</title>
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="/webcodecs/utils.js"></script>
<script> <script>
'use strict'; 'use strict';
...@@ -10,7 +11,7 @@ ...@@ -10,7 +11,7 @@
// TODO(sandersd): Add H.264 idecode test once there is an API to query for // TODO(sandersd): Add H.264 idecode test once there is an API to query for
// supported codecs. // supported codecs.
let h264 = { let h264 = {
buffer: fetch('h264.mp4').then(r => r.arrayBuffer()), async buffer() { return (await fetch('h264.mp4')).arrayBuffer(); },
codec: "avc1.64000c", codec: "avc1.64000c",
description: {offset: 7229, size: 46}, description: {offset: 7229, size: 46},
frames: [{offset: 48, size: 4007}, frames: [{offset: 48, size: 4007},
...@@ -26,7 +27,7 @@ let h264 = { ...@@ -26,7 +27,7 @@ let h264 = {
}; };
let vp9 = { let vp9 = {
buffer: fetch('vp9.mp4').then(r => r.arrayBuffer()), async buffer() { return (await fetch('vp9.mp4')).arrayBuffer(); },
// TODO(sandersd): Verify that the file is actually level 1. // TODO(sandersd): Verify that the file is actually level 1.
codec: "vp09.00.10.08", codec: "vp09.00.10.08",
frames: [{offset: 44, size: 3315}, frames: [{offset: 44, size: 3315},
...@@ -53,79 +54,39 @@ function getFakeChunk() { ...@@ -53,79 +54,39 @@ function getFakeChunk() {
}); });
} }
// Calls done after giving async output/error callbacks a final chance to run. promise_test(t => {
async function asyncDone(test) {
test.step_timeout(test.step_func_done(), 0);
}
async_test(async (t) => {
// VideoDecoderInit lacks required fields. // VideoDecoderInit lacks required fields.
assert_throws_js(TypeError, () => { new VideoDecoder({}); }); assert_throws_js(TypeError, () => { new VideoDecoder({}); });
// VideoDecoderInit has required fields. // VideoDecoderInit has required fields.
let decoder = new VideoDecoder({ let decoder = new VideoDecoder(getDefaultCodecInit(t));
output(chunk) { t.unreached_func("Unexpected output").call(); },
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
});
assert_equals(decoder.state, "unconfigured"); assert_equals(decoder.state, "unconfigured");
asyncDone(t); decoder.close();
}, 'Test VideoDecoder construction');
async_test(async (t) => {
let decoder = new VideoDecoder({
output(chunk) { t.unreached_func("Unexpected output").call(); },
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
});
let config = {};
// Bogus codec rejected.
config.codec = 'bogus';
assert_throws_js(TypeError, () => { decoder.configure(config); });
// Audio codec rejected.
config.codec = 'vorbis';
assert_throws_js(TypeError, () => { decoder.configure(config); });
// Ambiguous codec rejected.
config.codec = 'vp9';
assert_throws_js(TypeError, () => { decoder.configure(config); });
// Codec with mime type rejected.
config.codec = 'video/webm; codecs="vp9"';
assert_throws_js(TypeError, () => { decoder.configure(config); });
// Valid unambiguous codec accepted.
config.codec = 'vp09.00.10.08';
decoder.configure(config);
assert_equals(decoder.state, "configured");
// Verify a failed reconfigure does not affect the current configuration.
config.codec = 'bogus';
assert_throws_js(TypeError, () => { decoder.configure(config); });
assert_equals(decoder.state, "configured"); return endAfterEventLoopTurn();
}, 'Test VideoDecoder construction');
// Verify we can reconfigure. promise_test(t => {
config.codec = 'vp09.00.10.08'; let decoder = new VideoDecoder(getDefaultCodecInit(t));
decoder.configure(config);
assert_equals(decoder.state, "configured"); let badCodecsList = [
'', // Empty codec
'bogus', // Non exsitent codec
'vorbis', // Audio codec
'vp9', // Ambiguous codec
'video/webm; codecs="vp9"' // Codec with mime type
]
// Test we can configure after a reset. testConfigurations(decoder, { codec: vp9.codec }, badCodecsList);
decoder.reset()
assert_equals(decoder.state, "unconfigured");
decoder.configure(config); return endAfterEventLoopTurn();
assert_equals(decoder.state, "configured"); }, 'Test VideoDecoder.configure()');
asyncDone(t); promise_test(async t => {
}, 'Test VideoDecoder.configure() codec validity'); let buffer = await vp9.buffer();
promise_test(t => vp9.buffer.then(buffer => {
let numOutputs = 0; let numOutputs = 0;
let decoder = new VideoDecoder({ let decoder = new VideoDecoder({
output(frame) { output(frame) {
...@@ -153,92 +114,36 @@ promise_test(t => vp9.buffer.then(buffer => { ...@@ -153,92 +114,36 @@ promise_test(t => vp9.buffer.then(buffer => {
data: view(buffer, vp9.frames[0]) data: view(buffer, vp9.frames[0])
})); }));
return decoder.flush().then(() => { await decoder.flush();
assert_equals(numOutputs, 1, "outputs");
});
}), 'Decode VP9');
promise_test(t => { assert_equals(numOutputs, 1, "outputs");
let decoder = new VideoDecoder({ }, 'Decode VP9');
output(frame) {
t.step(() => {
throw "unexpected output";
});
},
error(e) {
t.step(() => {
throw "unexpected error";
});
}
});
decoder.close();
assert_equals(decoder.state, "closed");
let fakeChunk = getFakeChunk();
assert_throws_dom("InvalidStateError",
() => decoder.configure({codec: vp9.codec}),
"configure");
assert_throws_dom("InvalidStateError",
() => decoder.decode(fakeChunk),
"decode");
assert_throws_dom("InvalidStateError",
() => decoder.reset(),
"reset");
assert_throws_dom("InvalidStateError",
() => decoder.close(),
"close");
return promise_rejects_dom(t, 'InvalidStateError', decoder.flush(), 'flush');
}, 'Closed decoder');
promise_test(t => { promise_test(t => {
let decoder = new VideoDecoder({ let decoder = new VideoDecoder(getDefaultCodecInit(t));
output(frame) {
t.step(() => {
throw "unexpected output";
});
},
error(e) {
t.step(() => {
throw "unexpected error";
});
}
});
assert_equals(decoder.state, "unconfigured"); return testClosedCodec(t, decoder, { codec: vp9.codec }, getFakeChunk());
}, 'Verify closed VideoDecoder operations');
// Reseting an unconfigured encoder is a no-op. promise_test(t => {
decoder.reset(); let decoder = new VideoDecoder(getDefaultCodecInit(t));
assert_equals(decoder.state, "unconfigured");
let fakeChunk = getFakeChunk(); return testUnconfiguredCodec(t, decoder, getFakeChunk());
assert_throws_dom("InvalidStateError", }, 'Verify unconfigured VideoDecoder operations');
() => decoder.decode(fakeChunk),
"decode");
return promise_rejects_dom(t, 'InvalidStateError', decoder.flush(), 'flush');
}, 'Unconfigured decoder');
promise_test(t => { promise_test(t => {
let numErrors = 0; let numErrors = 0;
let decoder = new VideoDecoder({ let codecInit = getDefaultCodecInit(t);
output(frame) { codecInit.error = _ => numErrors++;
t.step(() => {
throw "unexpected output"; let decoder = new VideoDecoder(codecInit);
});
},
error(e) {
numErrors++;
}
});
decoder.configure({codec: vp9.codec}); decoder.configure({codec: vp9.codec});
let fakeChunk = getFakeChunk(); let fakeChunk = getFakeChunk();
decoder.decode(fakeChunk); decoder.decode(fakeChunk);
return decoder.flush().then( return promise_rejects_exactly(t, undefined, decoder.flush()).then(
() => { throw "flush succeeded unexpectedly"; },
() => { () => {
assert_equals(numErrors, 1, "errors"); assert_equals(numErrors, 1, "errors");
assert_equals(decoder.state, "closed"); assert_equals(decoder.state, "closed");
...@@ -247,41 +152,25 @@ promise_test(t => { ...@@ -247,41 +152,25 @@ promise_test(t => {
promise_test(t => { promise_test(t => {
let numErrors = 0; let numErrors = 0;
let decoder = new VideoDecoder({ let codecInit = getDefaultCodecInit(t);
output(frame) { codecInit.error = _ => numErrors++;
t.step(() => {
throw "unexpected output"; let decoder = new VideoDecoder(codecInit);
});
},
error(e) {
numErrors++;
}
});
decoder.configure({codec: vp9.codec}); decoder.configure({codec: vp9.codec});
let fakeChunk = getFakeChunk(); let fakeChunk = getFakeChunk();
decoder.decode(fakeChunk); decoder.decode(fakeChunk);
return decoder.flush().then( return promise_rejects_exactly(t, undefined, decoder.flush()).then(
() => { throw "flush succeeded unexpectedly"; }, () => {
() => { assert_equals(numErrors, 1, "errors"); }); assert_equals(numErrors, 1, "errors");
assert_equals(decoder.state, "closed");
});
}, 'Decode empty VP9 frame'); }, 'Decode empty VP9 frame');
promise_test(t => { promise_test(t => {
let decoder = new VideoDecoder({ let decoder = new VideoDecoder(getDefaultCodecInit(t));
output(frame) {
t.step(() => {
throw "unexpected output";
});
},
error(e) {
t.step(() => {
// TODO(sandersd): Change to 'throw e' once e is defined.
throw "decode error";
});
}
});
decoder.configure({codec: vp9.codec}); decoder.configure({codec: vp9.codec});
...@@ -296,9 +185,7 @@ promise_test(t => { ...@@ -296,9 +185,7 @@ promise_test(t => {
// TODO(sandersd): Wait for a bit in case there is a lingering output // TODO(sandersd): Wait for a bit in case there is a lingering output
// or error coming. // or error coming.
return flushPromise.then( return promise_rejects_exactly(t, undefined, flushPromise);
() => { throw "flush succeeded unexpectedly"; },
() => {});
}, 'Close while decoding corrupt VP9 frame'); }, 'Close while decoding corrupt VP9 frame');
</script> </script>
</html> </html>
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="/common/media.js"></script> <script src="/common/media.js"></script>
<script src="/webcodecs/utils.js"></script>
<script> <script>
const defaultConfig = { const defaultConfig = {
...@@ -27,113 +28,42 @@ async function createVideoFrame(width, height, timestamp) { ...@@ -27,113 +28,42 @@ async function createVideoFrame(width, height, timestamp) {
return new VideoFrame(bitmap, { timestamp: timestamp }); return new VideoFrame(bitmap, { timestamp: timestamp });
} }
// Calls done after giving async output/error callbacks a final chance to run. promise_test(t => {
async function asyncDone(test) {
test.step_timeout(test.done.bind(test), 0);
}
async_test(async (t) => {
// VideoEncoderInit lacks required fields. // VideoEncoderInit lacks required fields.
assert_throws_js(TypeError, () => { new VideoEncoder({}); }); assert_throws_js(TypeError, () => { new VideoEncoder({}); });
// VideoEncoderInit has required fields. // VideoEncoderInit has required fields.
let encoder = new VideoEncoder({ let encoder = new VideoEncoder(getDefaultCodecInit(t));
output(chunk) { t.unreached_func("Unexpected output").call(); },
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
});
assert_equals(encoder.state, "unconfigured"); assert_equals(encoder.state, "unconfigured");
encoder.close(); encoder.close();
asyncDone(t); return endAfterEventLoopTurn();
}, 'Test VideoEncoder construction'); }, 'Test VideoEncoder construction');
async_test(async (t) => { promise_test(t => {
let encoder = new VideoEncoder({ let encoder = new VideoEncoder(getDefaultCodecInit(t));
output(chunk) { t.unreached_func("Unexpected output").call(); },
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
});
const requiredConfigPairs = defaultConfig;
let incrementalConfig = {};
for (let key in requiredConfigPairs) {
// Configure should fail while required keys are missing.
assert_throws_js(TypeError, () => { encoder.configure(incrementalConfig); });
incrementalConfig[key] = requiredConfigPairs[key];
assert_equals(encoder.state, "unconfigured");
}
// Configure should pass once incrementalConfig meets all requirements.
encoder.configure(incrementalConfig);
assert_equals(encoder.state, "configured");
// We should be able to reconfigure the encoder.
encoder.configure(incrementalConfig);
assert_equals(encoder.state, "configured");
let config = incrementalConfig;
// Bogus codec rejected.
config.codec = 'bogus';
assert_throws_js(TypeError, () => { encoder.configure(config); });
// Audio codec rejected.
config.codec = 'vorbis';
assert_throws_js(TypeError, () => { encoder.configure(config); });
// Ambiguous codec rejected.
config.codec = 'vp9';
assert_throws_js(TypeError, () => { encoder.configure(config); });
// Codec with mime type rejected. let badCodecsList = [
config.codec = 'video/webm; codecs="vp9"'; '', // Empty codec
assert_throws_js(TypeError, () => { encoder.configure(config); }); 'bogus', // Non exsitent codec
'vorbis', // Audio codec
'vp9', // Ambiguous codec
'video/webm; codecs="vp9"' // Codec with mime type
]
// The failed configures should not affect the current config. testConfigurations(encoder, defaultConfig, badCodecsList);
assert_equals(encoder.state, "configured");
// Test we can configure after a reset.
encoder.reset()
assert_equals(encoder.state, "unconfigured");
encoder.configure(defaultConfig); return endAfterEventLoopTurn();
assert_equals(encoder.state, "configured");
encoder.close();
asyncDone(t);
}, 'Test VideoEncoder.configure()'); }, 'Test VideoEncoder.configure()');
async_test(async (t) => { promise_test(async t => {
let encoder = new VideoEncoder({
output(chunk) { t.unreached_func("Unexpected output").call(); },
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
});
let videoFrame = await createVideoFrame(640, 480, 0);
assert_throws_dom('InvalidStateError',
() => { encoder.encode(videoFrame); },
'first encode');
// Once more for good measure.
assert_throws_dom('InvalidStateError',
() => { encoder.encode(videoFrame); },
'second encode');
encoder.close();
asyncDone(t);
}, 'Test encode() before configure() throws InvalidStateError.');
async_test(async (t) => {
let output_chunks = []; let output_chunks = [];
let encoder = new VideoEncoder({ let codecInit = getDefaultCodecInit(t);
output(chunk) { output_chunks.push(chunk); }, codecInit.output = chunk => output_chunks.push(chunk);
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
}); let encoder = new VideoEncoder(codecInit);
// No encodes yet. // No encodes yet.
assert_equals(encoder.encodeQueueSize, 0); assert_equals(encoder.encodeQueueSize, 0);
...@@ -161,18 +91,14 @@ async_test(async (t) => { ...@@ -161,18 +91,14 @@ async_test(async (t) => {
assert_equals(output_chunks.length, 2); assert_equals(output_chunks.length, 2);
assert_equals(output_chunks[0].timestamp, frame1.timestamp); assert_equals(output_chunks[0].timestamp, frame1.timestamp);
assert_equals(output_chunks[1].timestamp, frame2.timestamp); assert_equals(output_chunks[1].timestamp, frame2.timestamp);
encoder.close();
asyncDone(t);
}, 'Test successful configure(), encode(), and flush()'); }, 'Test successful configure(), encode(), and flush()');
async_test(async (t) => { promise_test(async t => {
let output_chunks = []; let output_chunks = [];
let encoder = new VideoEncoder({ let codecInit = getDefaultCodecInit(t);
output(chunk) { output_chunks.push(chunk); }, codecInit.output = chunk => output_chunks.push(chunk);
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
}); let encoder = new VideoEncoder(codecInit);
// No encodes yet. // No encodes yet.
assert_equals(encoder.encodeQueueSize, 0); assert_equals(encoder.encodeQueueSize, 0);
...@@ -217,8 +143,9 @@ async_test(async (t) => { ...@@ -217,8 +143,9 @@ async_test(async (t) => {
encoder.encode(frame3.clone()); encoder.encode(frame3.clone());
// Verify that a failed call to configure does not change the encoder's state. // Verify that a failed call to configure does not change the encoder's state.
config.codec = 'bogus'; let badConfig = {...defaultConfig};
assert_throws_js(TypeError, () => encoder.configure(config)); badConfig.codec = 'bogus';
assert_throws_js(TypeError, () => encoder.configure(badConfig));
encoder.encode(frame4.clone()); encoder.encode(frame4.clone());
...@@ -226,101 +153,47 @@ async_test(async (t) => { ...@@ -226,101 +153,47 @@ async_test(async (t) => {
assert_equals(output_chunks[0].timestamp, frame3.timestamp); assert_equals(output_chunks[0].timestamp, frame3.timestamp);
assert_equals(output_chunks[1].timestamp, frame4.timestamp); assert_equals(output_chunks[1].timestamp, frame4.timestamp);
encoder.close();
asyncDone(t);
}, 'Test successful encode() after re-configure().'); }, 'Test successful encode() after re-configure().');
async_test(async (t) => { promise_test(async t => {
let output_chunks = []; let output_chunks = [];
let encoder = new VideoEncoder({ let codecInit = getDefaultCodecInit(t);
output(chunk) { output_chunks.push(chunk); }, codecInit.output = chunk => output_chunks.push(chunk);
error(error) { t.unreached_func("Unexpected error:" + error).call(); },
}); let encoder = new VideoEncoder(codecInit);
let timestamp = 33333; let timestamp = 33333;
let frame = await createVideoFrame(640, 480, timestamp); let frame = await createVideoFrame(640, 480, timestamp);
t.step(() => { encoder.configure(defaultConfig);
// No encodes yet. assert_equals(encoder.state, "configured");
assert_equals(encoder.encodeQueueSize, 0);
encoder.configure(defaultConfig);
encoder.encode(frame); encoder.encode(frame);
assert_not_equals(frame.timestamp, timestamp); // |frame| is not longer valid since it has been destroyed.
assert_throws_dom("InvalidStateError", () => frame.clone()); assert_not_equals(frame.timestamp, timestamp);
assert_throws_dom("InvalidStateError", () => frame.clone());
encoder.close(); encoder.close();
});
asyncDone(t); return endAfterEventLoopTurn();
}, 'Test encoder consumes (destroys) frames.'); }, 'Test encoder consumes (destroys) frames.');
promise_test(async t => { promise_test(async t => {
let encoder = new VideoEncoder({ let encoder = new VideoEncoder(getDefaultCodecInit(t));
output(frame) {
t.step(() => {
throw "unexpected output";
});
},
error(e) {
t.step(() => {
throw "unexpected error";
});
}
});
encoder.close();
assert_equals(encoder.state, "closed")
let frame = await createVideoFrame(640, 480, 0); let frame = await createVideoFrame(640, 480, 0);
assert_throws_dom("InvalidStateError", return testClosedCodec(t, encoder, defaultConfig, frame);
() => encoder.configure(defaultConfig), }, 'Verify closed VideoEncoder operations');
"configure");
assert_throws_dom("InvalidStateError",
() => encoder.encode(frame),
"encode");
assert_throws_dom("InvalidStateError",
() => encoder.reset(),
"reset");
assert_throws_dom("InvalidStateError",
() => encoder.close(),
"close");
return promise_rejects_dom(t, 'InvalidStateError', encoder.flush(), 'flush');
}, 'Closed encoder');
promise_test(async t => { promise_test(async t => {
let encoder = new VideoEncoder({ let encoder = new VideoEncoder(getDefaultCodecInit(t));
output(frame) {
t.step(() => {
throw "unexpected output";
});
},
error(e) {
t.step(() => {
throw "unexpected error";
});
}
});
assert_equals(encoder.state, "unconfigured");
let frame = await createVideoFrame(640, 480, 0); let frame = await createVideoFrame(640, 480, 0);
// Resetting an unconfigured encoder is a no-op. return testUnconfiguredCodec(t, encoder, frame);
encoder.reset(); }, 'Verify unconfigured VideoEncoder operations');
assert_equals(encoder.state, "unconfigured");
assert_throws_dom("InvalidStateError",
() => encoder.encode(frame),
"encode");
return promise_rejects_dom(t, 'InvalidStateError', encoder.flush(), 'flush');
}, 'Unconfigured encoder');
</script> </script>
</html> </html>
...@@ -5,33 +5,22 @@ ...@@ -5,33 +5,22 @@
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="/common/media.js"></script> <script src="/common/media.js"></script>
<script src="/webcodecs/utils.js"></script>
<script> <script>
function makeImageBitmap(width, height) {
let canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgba(50, 100, 150, 255)';
ctx.fillRect(0, 0, width, height);
return createImageBitmap(canvas);
}
var defaultInit = { var defaultInit = {
timestamp : 100, timestamp : 100,
duration : 33, duration : 33,
} }
async function createDefaultVideoFrame() { function createDefaultVideoFrame() {
let image = await makeImageBitmap(32,16); let image = makeImageBitmap(32,16);
return new VideoFrame(image, defaultInit); return new VideoFrame(image, defaultInit);
} }
async_test(async function(t) { test(t => {
let frame = await createDefaultVideoFrame(); let frame = createDefaultVideoFrame();
let clone = frame.clone(); let clone = frame.clone();
...@@ -44,12 +33,10 @@ async_test(async function(t) { ...@@ -44,12 +33,10 @@ async_test(async function(t) {
frame.destroy(); frame.destroy();
clone.destroy(); clone.destroy();
t.done();
}, 'Test we can clone a VideoFrame.'); }, 'Test we can clone a VideoFrame.');
async_test(async function(t) { test(t => {
let frame = await createDefaultVideoFrame(); let frame = createDefaultVideoFrame();
let copy = frame; let copy = frame;
let clone = frame.clone(); let clone = frame.clone();
...@@ -60,24 +47,20 @@ async_test(async function(t) { ...@@ -60,24 +47,20 @@ async_test(async function(t) {
assert_equals(clone.timestamp, defaultInit.timestamp); assert_equals(clone.timestamp, defaultInit.timestamp);
clone.destroy(); clone.destroy();
t.done();
}, 'Verify destroying a frame doesn\'t affect its clones.'); }, 'Verify destroying a frame doesn\'t affect its clones.');
async_test(async function(t) { test(t => {
let frame = await createDefaultVideoFrame(); let frame = createDefaultVideoFrame();
frame.destroy(); frame.destroy();
assert_throws_dom("InvalidStateError", () => { assert_throws_dom("InvalidStateError", () => {
let clone = frame.clone(); let clone = frame.clone();
}); });
t.done();
}, 'Verify cloning a destroyed frame throws.'); }, 'Verify cloning a destroyed frame throws.');
async_test(async function(t) { async_test(t => {
let localFrame = await createDefaultVideoFrame(); let localFrame = createDefaultVideoFrame();
let channel = new MessageChannel(); let channel = new MessageChannel();
let localPort = channel.port1; let localPort = channel.port1;
...@@ -97,8 +80,8 @@ async_test(async function(t) { ...@@ -97,8 +80,8 @@ async_test(async function(t) {
}, 'Verify destroying frames propagates accross contexts.'); }, 'Verify destroying frames propagates accross contexts.');
async_test(async function(t) { async_test(t => {
let localFrame = await createDefaultVideoFrame(); let localFrame = createDefaultVideoFrame();
let channel = new MessageChannel(); let channel = new MessageChannel();
let localPort = channel.port1; let localPort = channel.port1;
...@@ -119,8 +102,8 @@ async_test(async function(t) { ...@@ -119,8 +102,8 @@ async_test(async function(t) {
}, 'Verify destroying cloned frames doesn\'t propagate accross contexts.'); }, 'Verify destroying cloned frames doesn\'t propagate accross contexts.');
async_test(async function(t) { async_test(t => {
let localFrame = await createDefaultVideoFrame(); let localFrame = createDefaultVideoFrame();
let channel = new MessageChannel(); let channel = new MessageChannel();
let localPort = channel.port1; let localPort = channel.port1;
......
...@@ -4,16 +4,8 @@ ...@@ -4,16 +4,8 @@
<body></body> <body></body>
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="/webcodecs/utils.js"></script>
<script> <script>
function makeImageBitmap(width, height) {
let canvas = new OffscreenCanvas(width, height);
let ctx = canvas.getContext('2d');
ctx.fillStyle = 'rgba(50, 100, 150, 255)';
ctx.fillRect(0, 0, width, height);
return canvas.transferToImageBitmap();
}
test(t => { test(t => {
let image = makeImageBitmap(32, 16); let image = makeImageBitmap(32, 16);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment