Skip to content

Commit

Permalink
Add additional structured clone tests
Browse files Browse the repository at this point in the history
The following behaviors mandated by the spec weren't tested:

- Serializing a non-serializable platform object, or transferring a
  non-transferable platform object, should fail.
- Platform objects must deserialize even if their interface has been
  deleted from the realm's global.
- Objects which have serializable or transferable interfaces in their
  inheritance hierarchy must deserialize as instances of the closest
  such interface.
- Transferring again a detached object will fail.

This change also passes the test object as an additional parameter to
the `f` function of structured clone tests, to make it possible to test
that a promise rejects.
  • Loading branch information
Andreu Botella authored Sep 16, 2021
1 parent ababf6c commit 90c5aa6
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ function runStructuredCloneBatteryOfTests(runner) {
}

return new Promise(resolve => {
promise_test(async _ => {
promise_test(async t => {
test = await test;
await setupPromise;
await runner.preTest(test);
await test.f(runner)
await test.f(runner, t)
await runner.postTest(test);
resolve();
}, test.description);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,80 @@ structuredCloneBatteryOfTests.push({
});

// TODO: ImageBitmap

structuredCloneBatteryOfTests.push({
description: 'A detached ArrayBuffer cannot be transferred',
async f(runner, t) {
const buffer = new ArrayBuffer();
await runner.structuredClone(buffer, [buffer]);
await promise_rejects_dom(
t,
"DataCloneError",
runner.structuredClone(buffer, [buffer])
);
}
});

structuredCloneBatteryOfTests.push({
description: 'A detached platform object cannot be transferred',
async f(runner, t) {
const {port1} = new MessageChannel();
await runner.structuredClone(port1, [port1]);
await promise_rejects_dom(
t,
"DataCloneError",
runner.structuredClone(port1, [port1])
);
}
});

structuredCloneBatteryOfTests.push({
description: 'Transferring a non-transferable platform object fails',
async f(runner, t) {
const blob = new Blob();
await promise_rejects_dom(
t,
"DataCloneError",
runner.structuredClone(blob, [blob])
);
}
});

structuredCloneBatteryOfTests.push({
description: 'An object whose interface is deleted from the global object must still be received',
async f(runner) {
const {port1} = new MessageChannel();
const messagePortInterface = globalThis.MessagePort;
delete globalThis.MessagePort;
try {
const transfer = await runner.structuredClone(port1, [port1]);
assert_true(transfer instanceof messagePortInterface);
} finally {
globalThis.MessagePort = messagePortInterface;
}
}
});

structuredCloneBatteryOfTests.push({
description: 'A subclass instance will be received as its closest transferable superclass',
async f(runner) {
// MessagePort doesn't have a constructor, so we must use something else.

// Make sure that ReadableStream is transferable before we test its subclasses.
try {
const stream = new ReadableStream();
await runner.structuredClone(stream, [stream]);
} catch(err) {
if (err instanceof DOMException && err.code === DOMException.DATA_CLONE_ERR) {
throw new OptionalFeatureUnsupportedError("ReadableStream isn't transferable");
} else {
throw err;
}
}

class ReadableStreamSubclass extends ReadableStream {}
const original = new ReadableStreamSubclass();
const transfer = await runner.structuredClone(original, [original]);
assert_equals(Object.getPrototypeOf(transfer), ReadableStream.prototype);
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -616,3 +616,41 @@ check('ObjectPrototype must lose its exotic-ness when cloned',
assert_equals(Object.getPrototypeOf(copy), newProto);
}
);

structuredCloneBatteryOfTests.push({
description: 'Serializing a non-serializable platform object fails',
async f(runner, t) {
const request = new Response();
await promise_rejects_dom(
t,
"DataCloneError",
runner.structuredClone(request)
);
}
});

structuredCloneBatteryOfTests.push({
description: 'An object whose interface is deleted from the global must still deserialize',
async f(runner) {
const blob = new Blob();
const blobInterface = globalThis.Blob;
delete globalThis.Blob;
try {
const copy = await runner.structuredClone(blob);
assert_true(copy instanceof blobInterface);
} finally {
globalThis.Blob = blobInterface;
}
}
});

check(
'A subclass instance will deserialize as its closest serializable superclass',
() => {
class FileSubclass extends File {}
return new FileSubclass([], "");
},
(copy) => {
assert_equals(Object.getPrototypeOf(copy), File.prototype);
}
);

0 comments on commit 90c5aa6

Please sign in to comment.