summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/IndexedDB/structured-clone.any.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/IndexedDB/structured-clone.any.js')
-rw-r--r--testing/web-platform/tests/IndexedDB/structured-clone.any.js324
1 files changed, 324 insertions, 0 deletions
diff --git a/testing/web-platform/tests/IndexedDB/structured-clone.any.js b/testing/web-platform/tests/IndexedDB/structured-clone.any.js
new file mode 100644
index 0000000000..15ab0359e2
--- /dev/null
+++ b/testing/web-platform/tests/IndexedDB/structured-clone.any.js
@@ -0,0 +1,324 @@
+// META: title=Indexed DB and Structured Serializing/Deserializing
+// META: timeout=long
+// META: script=resources/support-promises.js
+// META: script=/common/subset-tests.js
+// META: variant=?1-20
+// META: variant=?21-40
+// META: variant=?41-60
+// META: variant=?61-80
+// META: variant=?81-100
+// META: variant=?101-last
+
+// Tests Indexed DB coverage of HTML's Safe "passing of structured data"
+// https://html.spec.whatwg.org/multipage/structured-data.html
+
+function describe(value) {
+ let type, str;
+ if (typeof value === 'object' && value) {
+ type = Object.getPrototypeOf(value).constructor.name;
+ // Handle Number(-0), etc.
+ str = Object.is(value.valueOf(), -0) ? '-0' : String(value);
+ } else {
+ type = typeof value;
+ // Handle primitive -0.
+ str = Object.is(value, -0) ? '-0' : String(value);
+ }
+ return `${type}: ${str}`;
+}
+
+function cloneTest(value, verifyFunc) {
+ subsetTest(promise_test, async t => {
+ const db = await createDatabase(t, db => {
+ const store = db.createObjectStore('store');
+ // This index is not used, but evaluating key path on each put()
+ // call will exercise (de)serialization.
+ store.createIndex('index', 'dummyKeyPath');
+ });
+ t.add_cleanup(() => {
+ if (db) {
+ db.close();
+ indexedDB.deleteDatabase(db.name);
+ }
+ });
+ const tx = db.transaction('store', 'readwrite', {durability: 'relaxed'});
+ const store = tx.objectStore('store');
+ await promiseForRequest(t, store.put(value, 'key'));
+ const result = await promiseForRequest(t, store.get('key'));
+ await verifyFunc(value, result);
+ await promiseForTransaction(t, tx);
+ }, describe(value));
+}
+
+// Specialization of cloneTest() for objects, with common asserts.
+function cloneObjectTest(value, verifyFunc) {
+ cloneTest(value, async (orig, clone) => {
+ assert_not_equals(orig, clone);
+ assert_equals(typeof clone, 'object');
+ assert_equals(Object.getPrototypeOf(orig), Object.getPrototypeOf(clone));
+ await verifyFunc(orig, clone);
+ });
+}
+
+function cloneFailureTest(value) {
+ subsetTest(promise_test, async t => {
+ const db = await createDatabase(t, db => {
+ db.createObjectStore('store');
+ });
+ t.add_cleanup(() => {
+ if (db) {
+ db.close();
+ indexedDB.deleteDatabase(db.name);
+ }
+ });
+ const tx = db.transaction('store', 'readwrite', {durability: 'relaxed'});
+ const store = tx.objectStore('store');
+ assert_throws_dom('DataCloneError', () => store.put(value, 'key'));
+ }, 'Not serializable: ' + describe(value));
+}
+
+//
+// ECMAScript types
+//
+
+// Primitive values: Undefined, Null, Boolean, Number, BigInt, String
+const booleans = [false, true];
+const numbers = [
+ NaN,
+ -Infinity,
+ -Number.MAX_VALUE,
+ -0xffffffff,
+ -0x80000000,
+ -0x7fffffff,
+ -1,
+ -Number.MIN_VALUE,
+ -0,
+ 0,
+ 1,
+ Number.MIN_VALUE,
+ 0x7fffffff,
+ 0x80000000,
+ 0xffffffff,
+ Number.MAX_VALUE,
+ Infinity,
+];
+const bigints = [
+ -12345678901234567890n,
+ -1n,
+ 0n,
+ 1n,
+ 12345678901234567890n,
+];
+const strings = [
+ '',
+ 'this is a sample string',
+ 'null(\0)',
+];
+
+[undefined, null].concat(booleans, numbers, bigints, strings)
+ .forEach(value => cloneTest(value, (orig, clone) => {
+ assert_equals(orig, clone);
+ }));
+
+// "Primitive" Objects (Boolean, Number, BigInt, String)
+[].concat(booleans, numbers, bigints, strings)
+ .forEach(value => cloneObjectTest(Object(value), (orig, clone) => {
+ assert_equals(orig.valueOf(), clone.valueOf());
+ }));
+
+// Dates
+[
+ new Date(-1e13),
+ new Date(-1e12),
+ new Date(-1e9),
+ new Date(-1e6),
+ new Date(-1e3),
+ new Date(0),
+ new Date(1e3),
+ new Date(1e6),
+ new Date(1e9),
+ new Date(1e12),
+ new Date(1e13)
+].forEach(value => cloneTest(value, (orig, clone) => {
+ assert_not_equals(orig, clone);
+ assert_equals(typeof clone, 'object');
+ assert_equals(Object.getPrototypeOf(orig), Object.getPrototypeOf(clone));
+ assert_equals(orig.valueOf(), clone.valueOf());
+ }));
+
+// Regular Expressions
+[
+ new RegExp(),
+ /abc/,
+ /abc/g,
+ /abc/i,
+ /abc/gi,
+ /abc/m,
+ /abc/mg,
+ /abc/mi,
+ /abc/mgi,
+ /abc/gimsuy,
+].forEach(value => cloneObjectTest(value, (orig, clone) => {
+ assert_equals(orig.toString(), clone.toString());
+}));
+
+// ArrayBuffer
+cloneObjectTest(new Uint8Array([0, 1, 254, 255]).buffer, (orig, clone) => {
+ assert_array_equals(new Uint8Array(orig), new Uint8Array(clone));
+});
+
+// TODO SharedArrayBuffer
+
+// Array Buffer Views
+[
+ new Uint8Array([]),
+ new Uint8Array([0, 1, 254, 255]),
+ new Uint16Array([0x0000, 0x0001, 0xFFFE, 0xFFFF]),
+ new Uint32Array([0x00000000, 0x00000001, 0xFFFFFFFE, 0xFFFFFFFF]),
+ new Int8Array([0, 1, 254, 255]),
+ new Int16Array([0x0000, 0x0001, 0xFFFE, 0xFFFF]),
+ new Int32Array([0x00000000, 0x00000001, 0xFFFFFFFE, 0xFFFFFFFF]),
+ new Uint8ClampedArray([0, 1, 254, 255]),
+ new Float32Array([-Infinity, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, Infinity, NaN]),
+ new Float64Array([-Infinity, -Number.MAX_VALUE, -Number.MIN_VALUE, 0,
+ Number.MIN_VALUE, Number.MAX_VALUE, Infinity, NaN])
+].forEach(value => cloneObjectTest(value, (orig, clone) => {
+ assert_array_equals(orig, clone);
+}));
+
+// Map
+cloneObjectTest(new Map([[1,2],[3,4]]), (orig, clone) => {
+ assert_array_equals([...orig.keys()], [...clone.keys()]);
+ assert_array_equals([...orig.values()], [...clone.values()]);
+});
+
+// Set
+cloneObjectTest(new Set([1,2,3,4]), (orig, clone) => {
+ assert_array_equals([...orig.values()], [...clone.values()]);
+});
+
+// Error
+[
+ new Error(),
+ new Error('abc', 'def'),
+ new EvalError(),
+ new EvalError('ghi', 'jkl'),
+ new RangeError(),
+ new RangeError('ghi', 'jkl'),
+ new ReferenceError(),
+ new ReferenceError('ghi', 'jkl'),
+ new SyntaxError(),
+ new SyntaxError('ghi', 'jkl'),
+ new TypeError(),
+ new TypeError('ghi', 'jkl'),
+ new URIError(),
+ new URIError('ghi', 'jkl'),
+].forEach(value => cloneObjectTest(value, (orig, clone) => {
+ assert_equals(orig.name, clone.name);
+ assert_equals(orig.message, clone.message);
+}));
+
+// Arrays
+[
+ [],
+ [1,2,3],
+ Object.assign(
+ ['foo', 'bar'],
+ {10: true, 11: false, 20: 123, 21: 456, 30: null}),
+ Object.assign(
+ ['foo', 'bar'],
+ {a: true, b: false, foo: 123, bar: 456, '': null}),
+].forEach(value => cloneObjectTest(value, (orig, clone) => {
+ assert_array_equals(orig, clone);
+ assert_array_equals(Object.keys(orig), Object.keys(clone));
+ Object.keys(orig).forEach(key => {
+ assert_equals(orig[key], clone[key], `Property ${key}`);
+ });
+}));
+
+// Objects
+cloneObjectTest({foo: true, bar: false}, (orig, clone) => {
+ assert_array_equals(Object.keys(orig), Object.keys(clone));
+ Object.keys(orig).forEach(key => {
+ assert_equals(orig[key], clone[key], `Property ${key}`);
+ });
+});
+
+//
+// [Serializable] Platform objects
+//
+
+// TODO: Test these additional interfaces:
+// * DOMQuad
+// * DOMException
+// * RTCCertificate
+
+// Geometry types
+[
+ new DOMMatrix(),
+ new DOMMatrixReadOnly(),
+ new DOMPoint(),
+ new DOMPointReadOnly(),
+ new DOMRect,
+ new DOMRectReadOnly(),
+].forEach(value => cloneObjectTest(value, (orig, clone) => {
+ Object.keys(Object.getPrototypeOf(orig)).forEach(key => {
+ assert_equals(orig[key], clone[key], `Property ${key}`);
+ });
+}));
+
+// ImageData
+const image_data = new ImageData(8, 8);
+for (let i = 0; i < 256; ++i) {
+ image_data.data[i] = i;
+}
+cloneObjectTest(image_data, (orig, clone) => {
+ assert_equals(orig.width, clone.width);
+ assert_equals(orig.height, clone.height);
+ assert_array_equals(orig.data, clone.data);
+});
+
+// Blob
+cloneObjectTest(
+ new Blob(['This is a test.'], {type: 'a/b'}),
+ async (orig, clone) => {
+ assert_equals(orig.size, clone.size);
+ assert_equals(orig.type, clone.type);
+ assert_equals(await orig.text(), await clone.text());
+ });
+
+// File
+cloneObjectTest(
+ new File(['This is a test.'], 'foo.txt', {type: 'c/d'}),
+ async (orig, clone) => {
+ assert_equals(orig.size, clone.size);
+ assert_equals(orig.type, clone.type);
+ assert_equals(orig.name, clone.name);
+ assert_equals(orig.lastModified, clone.lastModified);
+ assert_equals(await orig.text(), await clone.text());
+ });
+
+
+// FileList - exposed in Workers, but not constructable.
+if ('document' in self) {
+ // TODO: Test with populated list.
+ cloneObjectTest(
+ Object.assign(document.createElement('input'),
+ {type: 'file', multiple: true}).files,
+ async (orig, clone) => {
+ assert_equals(orig.length, clone.length);
+ });
+}
+
+//
+// Non-serializable types
+//
+[
+ // ECMAScript types
+ function() {},
+ Symbol('desc'),
+
+ // Non-[Serializable] platform objects
+ self,
+ new Event(''),
+ new MessageChannel()
+].forEach(cloneFailureTest);