diff options
Diffstat (limited to '')
-rw-r--r-- | testing/web-platform/tests/webcodecs/utils.js | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webcodecs/utils.js b/testing/web-platform/tests/webcodecs/utils.js new file mode 100644 index 0000000000..aea658d3f2 --- /dev/null +++ b/testing/web-platform/tests/webcodecs/utils.js @@ -0,0 +1,209 @@ +function make_audio_data(timestamp, channels, sampleRate, frames) { + + let data = new Float32Array(frames*channels); + + // This generates samples in a planar format. + for (var channel = 0; channel < channels; channel++) { + let hz = 100 + channel * 50; // sound frequency + let base_index = channel * frames; + for (var i = 0; i < frames; i++) { + let t = (i / sampleRate) * hz * (Math.PI * 2); + data[base_index + i] = Math.sin(t); + } + } + + return new AudioData({ + timestamp: timestamp, + data: data, + numberOfChannels: channels, + numberOfFrames: frames, + sampleRate: sampleRate, + format: "f32-planar", + }); +} + +function makeOffscreenCanvas(width, height, options) { + let canvas = new OffscreenCanvas(width, height); + let ctx = canvas.getContext('2d', options); + ctx.fillStyle = 'rgba(50, 100, 150, 255)'; + ctx.fillRect(0, 0, width, height); + return canvas; +} + +function makeImageBitmap(width, height) { + return makeOffscreenCanvas(width, height).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'); +} + +// Reference values generated by: +// https://fiddle.skia.org/c/f100d4d5f085a9e09896aabcbc463868 + +const kSRGBPixel = [50, 100, 150, 255]; +const kP3Pixel = [62, 99, 146, 255]; +const kRec2020Pixel = [87, 106, 151, 255]; + +const kCanvasOptionsP3Uint8 = { + colorSpace: 'display-p3', + pixelFormat: 'uint8' +}; + +const kImageSettingOptionsP3Uint8 = { + colorSpace: 'display-p3', + storageFormat: 'uint8' +}; + +const kCanvasOptionsRec2020Uint8 = { + colorSpace: 'rec2020', + pixelFormat: 'uint8' +}; + +const kImageSettingOptionsRec2020Uint8 = { + colorSpace: 'rec2020', + storageFormat: 'uint8' +}; + +function testCanvas(ctx, width, height, expected_pixel, imageSetting, assert_compares) { + // The dup getImageData is to workaournd crbug.com/1100233 + let imageData = ctx.getImageData(0, 0, width, height, imageSetting); + let colorData = ctx.getImageData(0, 0, width, height, imageSetting).data; + const kMaxPixelToCheck = 128 * 96; + let step = width * height / kMaxPixelToCheck; + step = Math.round(step); + step = (step < 1) ? 1 : step; + for (let i = 0; i < 4 * width * height; i += (4 * step)) { + assert_compares(colorData[i], expected_pixel[0]); + assert_compares(colorData[i + 1], expected_pixel[1]); + assert_compares(colorData[i + 2], expected_pixel[2]); + assert_compares(colorData[i + 3], expected_pixel[3]); + } +} + +function makeDetachedArrayBuffer() { + const buffer = new ArrayBuffer(10); + const view = new Uint8Array(buffer); + new MessageChannel().port1.postMessage(buffer, [buffer]); + return view; +} + +function isFrameClosed(frame) { + return frame.format == null && frame.codedWidth == 0 && + frame.codedHeight == 0 && frame.displayWidth == 0 && + frame.displayHeight == 0 && frame.codedRect == null && + frame.visibleRect == null; +} |