diff options
Diffstat (limited to 'testing/web-platform/tests/IndexedDB/structured-clone.any.js')
-rw-r--r-- | testing/web-platform/tests/IndexedDB/structured-clone.any.js | 324 |
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); |