Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test,stream: enable compression WPTs #50631

Closed
wants to merge 2 commits into from
Closed
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
41 changes: 5 additions & 36 deletions lib/internal/crypto/webidl.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,44 +19,29 @@ const {
MathTrunc,
Number,
NumberIsFinite,
ObjectAssign,
ObjectPrototypeIsPrototypeOf,
SafeArrayIterator,
SafeSet,
String,
SymbolIterator,
TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetSymbolToStringTag,
TypeError,
globalThis: {
SharedArrayBuffer,
},
} = primordials;

const {
makeException,
createEnumConverter,
} = require('internal/webidl');

const {
kEmptyObject,
setOwnProperty,
} = require('internal/util');
const { CryptoKey } = require('internal/crypto/webcrypto');
const { getDataViewOrTypedArrayBuffer } = require('internal/crypto/util');

function codedTypeError(message, errorProperties = kEmptyObject) {
// eslint-disable-next-line no-restricted-syntax
const err = new TypeError(message);
ObjectAssign(err, errorProperties);
return err;
}

function makeException(message, opts = kEmptyObject) {
const prefix = opts.prefix ? opts.prefix + ': ' : '';
const context = opts.context?.length === 0 ?
'' : (opts.context ?? 'Value') + ' ';
return codedTypeError(
`${prefix}${context}${message}`,
{ code: opts.code || 'ERR_INVALID_ARG_TYPE' },
);
}

// https://tc39.es/ecma262/#sec-tonumber
function toNumber(value, opts = kEmptyObject) {
switch (typeof value) {
Expand Down Expand Up @@ -308,22 +293,6 @@ function createDictionaryConverter(name, dictionaries) {
};
}

function createEnumConverter(name, values) {
const E = new SafeSet(values);

return function(V, opts = kEmptyObject) {
const S = String(V);

if (!E.has(S)) {
throw makeException(
`value '${S}' is not a valid enum value of type ${name}.`,
{ __proto__: null, ...opts, code: 'ERR_INVALID_ARG_VALUE' });
}

return S;
};
}

function createSequenceConverter(converter) {
return function(V, opts = kEmptyObject) {
if (type(V) !== 'Object') {
Expand Down
40 changes: 39 additions & 1 deletion lib/internal/webidl.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ const {
NumberIsNaN,
NumberMAX_SAFE_INTEGER,
NumberMIN_SAFE_INTEGER,
ObjectAssign,
SafeSet,
String,
TypeError,
} = primordials;

const {
Expand Down Expand Up @@ -173,8 +176,43 @@ converters.DOMString = function DOMString(V) {
return String(V);
};

function codedTypeError(message, errorProperties = kEmptyObject) {
// eslint-disable-next-line no-restricted-syntax
const err = new TypeError(message);
ObjectAssign(err, errorProperties);
return err;
}

function makeException(message, opts = kEmptyObject) {
const prefix = opts.prefix ? opts.prefix + ': ' : '';
const context = opts.context?.length === 0 ?
'' : (opts.context ?? 'Value') + ' ';
return codedTypeError(
`${prefix}${context}${message}`,
{ code: opts.code || 'ERR_INVALID_ARG_TYPE' },
);
}

function createEnumConverter(name, values) {
const E = new SafeSet(values);

return function(V, opts = kEmptyObject) {
const S = String(V);

if (!E.has(S)) {
throw makeException(
`value '${S}' is not a valid enum value of type ${name}.`,
{ __proto__: null, ...opts, code: 'ERR_INVALID_ARG_VALUE' });
}

return S;
};
}

module.exports = {
converters,
convertToInt,
createEnumConverter,
evenRound,
converters,
makeException,
};
24 changes: 16 additions & 8 deletions lib/internal/webstreams/compression.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ const {
ObjectDefineProperties,
} = primordials;

const {
codes: { ERR_INVALID_ARG_VALUE },
} = require('internal/errors');

const {
newReadableWritablePairFromDuplex,
} = require('internal/webstreams/adapters');
Expand All @@ -19,12 +15,20 @@ const {
kEnumerableProperty,
} = require('internal/util');

const { createEnumConverter } = require('internal/webidl');

let zlib;
function lazyZlib() {
zlib ??= require('zlib');
return zlib;
}

const formatConverter = createEnumConverter('CompressionFormat', [
'deflate',
'deflate-raw',
'gzip',
]);

/**
* @typedef {import('./readablestream').ReadableStream} ReadableStream
* @typedef {import('./writablestream').WritableStream} WritableStream
Expand All @@ -38,6 +42,10 @@ class CompressionStream {
* @param {'deflate'|'deflate-raw'|'gzip'} format
*/
constructor(format) {
format = formatConverter(format, {
prefix: "Failed to construct 'CompressionStream'",
context: '1st argument',
});
switch (format) {
case 'deflate':
this.#handle = lazyZlib().createDeflate();
Expand All @@ -48,8 +56,6 @@ class CompressionStream {
case 'gzip':
this.#handle = lazyZlib().createGzip();
break;
default:
throw new ERR_INVALID_ARG_VALUE('format', format);
}
this.#transform = newReadableWritablePairFromDuplex(this.#handle);
}
Expand Down Expand Up @@ -86,6 +92,10 @@ class DecompressionStream {
* @param {'deflate'|'deflate-raw'|'gzip'} format
*/
constructor(format) {
format = formatConverter(format, {
prefix: "Failed to construct 'DecompressionStream'",
context: '1st argument',
});
switch (format) {
case 'deflate':
this.#handle = lazyZlib().createInflate();
Expand All @@ -96,8 +106,6 @@ class DecompressionStream {
case 'gzip':
this.#handle = lazyZlib().createGunzip();
break;
default:
throw new ERR_INVALID_ARG_VALUE('format', format);
}
this.#transform = newReadableWritablePairFromDuplex(this.#handle);
}
Expand Down
19 changes: 13 additions & 6 deletions test/common/wpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ class ResourceLoader {
const data = await fsPromises.readFile(file);
return {
ok: true,
arrayBuffer() { return data.buffer; },
json() { return JSON.parse(data.toString()); },
text() { return data.toString(); },
};
Expand Down Expand Up @@ -382,7 +383,7 @@ const kIntlRequirement = {
// TODO(joyeecheung): we may need to deal with --with-intl=system-icu
};

class IntlRequirement {
class BuildRequirement {
constructor() {
this.currentIntl = kIntlRequirement.none;
if (process.config.variables.v8_enable_i18n_support === 0) {
Expand All @@ -395,6 +396,9 @@ class IntlRequirement {
} else {
this.currentIntl = kIntlRequirement.full;
}
// Not using common.hasCrypto because of the global leak checks
this.hasCrypto = Boolean(process.versions.openssl) &&
!process.env.NODE_SKIP_CRYPTO;
}

/**
Expand All @@ -409,11 +413,14 @@ class IntlRequirement {
if (requires.has('small-icu') && current < kIntlRequirement.small) {
return 'small-icu';
}
if (requires.has('crypto') && !this.hasCrypto) {
return 'crypto';
}
return false;
}
}

const intlRequirements = new IntlRequirement();
const buildRequirements = new BuildRequirement();

class StatusLoader {
/**
Expand All @@ -440,7 +447,7 @@ class StatusLoader {
const list = this.grep(filepath);
result = result.concat(list);
} else {
if (!(/\.\w+\.js$/.test(filepath)) || filepath.endsWith('.helper.js')) {
if (!(/\.\w+\.js$/.test(filepath))) {
continue;
}
result.push(filepath);
Expand Down Expand Up @@ -945,9 +952,9 @@ class WPTRunner {
continue;
}

const lackingIntl = intlRequirements.isLacking(spec.requires);
if (lackingIntl) {
this.skip(spec, [ `requires ${lackingIntl}` ]);
const lackingSupport = buildRequirements.isLacking(spec.requires);
if (lackingSupport) {
this.skip(spec, [ `requires ${lackingSupport}` ]);
continue;
}

Expand Down
1 change: 1 addition & 0 deletions test/fixtures/wpt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ See [test/wpt](../../wpt/README.md) for information on how these tests are run.
Last update:

- common: https://github.com/web-platform-tests/wpt/tree/dbd648158d/common
- compression: https://github.com/web-platform-tests/wpt/tree/c82521cfa5/compression
- console: https://github.com/web-platform-tests/wpt/tree/767ae35464/console
- dom/abort: https://github.com/web-platform-tests/wpt/tree/d1f1ecbd52/dom/abort
- dom/events: https://github.com/web-platform-tests/wpt/tree/ab8999891c/dom/events
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/wpt/compression/META.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
spec: https://wicg.github.io/compression/
suggested_reviewers:
- ricea
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// META: global=window,worker,shadowrealm

'use strict';

const badChunks = [
{
name: 'undefined',
value: undefined
},
{
name: 'null',
value: null
},
{
name: 'numeric',
value: 3.14
},
{
name: 'object, not BufferSource',
value: {}
},
{
name: 'array',
value: [65]
},
{
name: 'SharedArrayBuffer',
// Use a getter to postpone construction so that all tests don't fail where
// SharedArrayBuffer is not yet implemented.
get value() {
// See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
return new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer;
}
},
{
name: 'shared Uint8Array',
get value() {
// See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()`
return new Uint8Array(new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer)
}
},
];

for (const chunk of badChunks) {
promise_test(async t => {
const cs = new CompressionStream('gzip');
const reader = cs.readable.getReader();
const writer = cs.writable.getWriter();
const writePromise = writer.write(chunk.value);
const readPromise = reader.read();
await promise_rejects_js(t, TypeError, writePromise, 'write should reject');
await promise_rejects_js(t, TypeError, readPromise, 'read should reject');
}, `chunk of type ${chunk.name} should error the stream for gzip`);

promise_test(async t => {
const cs = new CompressionStream('deflate');
const reader = cs.readable.getReader();
const writer = cs.writable.getWriter();
const writePromise = writer.write(chunk.value);
const readPromise = reader.read();
await promise_rejects_js(t, TypeError, writePromise, 'write should reject');
await promise_rejects_js(t, TypeError, readPromise, 'read should reject');
}, `chunk of type ${chunk.name} should error the stream for deflate`);

promise_test(async t => {
const cs = new CompressionStream('deflate-raw');
const reader = cs.readable.getReader();
const writer = cs.writable.getWriter();
const writePromise = writer.write(chunk.value);
const readPromise = reader.read();
await promise_rejects_js(t, TypeError, writePromise, 'write should reject');
await promise_rejects_js(t, TypeError, readPromise, 'read should reject');
}, `chunk of type ${chunk.name} should error the stream for deflate-raw`);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// META: global=window,worker,shadowrealm

'use strict';

test(t => {
assert_throws_js(TypeError, () => new CompressionStream('a'), 'constructor should throw');
}, '"a" should cause the constructor to throw');

test(t => {
assert_throws_js(TypeError, () => new CompressionStream(), 'constructor should throw');
}, 'no input should cause the constructor to throw');

test(t => {
assert_throws_js(Error, () => new CompressionStream({ toString() { throw Error(); } }), 'constructor should throw');
}, 'non-string input should cause the constructor to throw');
Loading