| Index: third_party/WebKit/LayoutTests/external/wpt/IndexedDB/large-nested-cloning.html
|
| diff --git a/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/large-nested-cloning.html b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/large-nested-cloning.html
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e882f6aa5a8918da6d2dd26869b2ca468b625311
|
| --- /dev/null
|
| +++ b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/large-nested-cloning.html
|
| @@ -0,0 +1,270 @@
|
| +<!doctype html>
|
| +<meta charset="utf8">
|
| +<title>IndexedDB: large nested objects are cloned correctly</title>
|
| +<link rel="help" href="https://w3c.github.io/IndexedDB/#abort-transaction">
|
| +<link rel="author" href="pwnall@chromium.org" title="Victor Costan">
|
| +<script src="/resources/testharness.js"></script>
|
| +<script src="/resources/testharnessreport.js"></script>
|
| +<script src="support-promises.js"></script>
|
| +<script>
|
| +'use strict';
|
| +
|
| +// Should be large enough to trigger value wrapping in the IndexedDB engines
|
| +// that implement wrapping.
|
| +const wrapThreshold = 128 * 1024;
|
| +
|
| +// Returns an Uint8Array with pseudorandom data.
|
| +function largeValue(size, seed) {
|
| + const buffer = new Uint8Array(size);
|
| +
|
| + // 32-bit xorshift - the seed can't be zero
|
| + let state = 1000 + seed;
|
| +
|
| + for (let i = 0; i < size; ++i) {
|
| + state ^= state << 13;
|
| + state ^= state >> 17;
|
| + state ^= state << 5;
|
| + buffer[i] = state & 0xff;
|
| + }
|
| +
|
| + return buffer;
|
| +}
|
| +
|
| +// Returns an IndexedDB value created from a descriptor.
|
| +//
|
| +// See the bottom of the file for descriptor samples.
|
| +function createValue(descriptor) {
|
| + if (typeof(descriptor) != 'object')
|
| + return descriptor;
|
| +
|
| + if (Array.isArray(descriptor))
|
| + return descriptor.map((element) => createValue(element));
|
| +
|
| + if (!descriptor.hasOwnProperty('type')) {
|
| + const value = {};
|
| + for (let property of Object.getOwnPropertyNames(descriptor))
|
| + value[property] = createValue(descriptor[property]);
|
| + return value;
|
| + }
|
| +
|
| + switch (descriptor.type) {
|
| + case 'blob':
|
| + return new Blob(
|
| + [largeValue(descriptor.size, descriptor.seed)],
|
| + { type: descriptor.type });
|
| + case 'buffer':
|
| + return largeValue(descriptor.size, descriptor.seed);
|
| + }
|
| +}
|
| +
|
| +
|
| +// Checks an IndexedDB value against a descriptor.
|
| +//
|
| +// Returns a Promise that resolves if the value passes the check.
|
| +//
|
| +// See the bottom of the file for descriptor samples.
|
| +function checkValue(testCase, value, descriptor) {
|
| + if (typeof(descriptor) != 'object') {
|
| + assert_equals(descriptor, value, 'incorrect value');
|
| + return Promise.resolve();
|
| + }
|
| +
|
| + if (Array.isArray(descriptor)) {
|
| + assert_true(Array.isArray(value), 'incorrect value type');
|
| + assert_equals(descriptor.length, value.length, 'incorrect array size');
|
| +
|
| + const subChecks = [];
|
| + for (let i = 0; i < descriptor.length; ++i)
|
| + subChecks.push(checkValue(testCase, value[i], descriptor[i]));
|
| + return Promise.all(subChecks);
|
| + }
|
| +
|
| + if (!descriptor.hasOwnProperty('type')) {
|
| + assert_array_equals(
|
| + Object.getOwnPropertyNames(value).sort(),
|
| + Object.getOwnPropertyNames(descriptor).sort(),
|
| + 'incorrect object properties');
|
| + const subChecks = [];
|
| + for (let property of Object.getOwnPropertyNames(descriptor)) {
|
| + subChecks.push(
|
| + checkValue(testCase, value[property], descriptor[property]));
|
| + }
|
| + return Promise.all(subChecks);
|
| + }
|
| +
|
| + switch (descriptor.type) {
|
| + case 'blob':
|
| + assert_class_string(value, 'Blob', 'incorrect value type');
|
| + assert_equals(descriptor.type, value.type, 'incorrect Blob type');
|
| + assert_equals(descriptor.size, value.size, 'incorrect Blob size');
|
| + return new Promise((resolve, reject) => {
|
| + const reader = new FileReader();
|
| + reader.onloadend = testCase.step_func(() => {
|
| + if (reader.error) {
|
| + reject(reader.error);
|
| + return;
|
| + }
|
| + const view = new Uint8Array(reader.result);
|
| + assert_equals(
|
| + view.join(','),
|
| + largeValue(descriptor.size, descriptor.seed).join(','),
|
| + 'incorrect Blob content from IndexedDB');
|
| + resolve();
|
| + });
|
| + reader.readAsArrayBuffer(value);
|
| + });
|
| +
|
| + case 'buffer':
|
| + assert_class_string(value, 'Uint8Array', 'incorrect value type');
|
| + assert_equals(
|
| + value.join(','),
|
| + largeValue(descriptor.size, descriptor.seed).join(','),
|
| + 'incorrect typed array content');
|
| + return Promise.resolve();
|
| + }
|
| +}
|
| +
|
| +// Test that performs a series of put()s and verifies that gets() match.
|
| +//
|
| +// Each element of the valueDescriptors array is fed into createValue(), and the
|
| +// resulting value is written to IndexedDB via a put() request. After the writes
|
| +// complete, the values are read in the same order in which they were written.
|
| +//
|
| +// The test verifies that the get() values match the arguments to put() and that
|
| +// the order in which the get() result events are fired matches the order of the
|
| +// get() requests.
|
| +function cloningTest(label, valueDescriptors) {
|
| + promise_test(testCase => {
|
| + return createDatabase(testCase, (database, transaction) => {
|
| + const store = database.createObjectStore('test-store', { keyPath: null });
|
| + for (let i = 0; i < valueDescriptors.length; ++i) {
|
| + store.put(createValue(valueDescriptors[i]), i);
|
| + }
|
| + }).then(database => {
|
| + const transaction = database.transaction(['test-store'], 'readonly');
|
| + const store = transaction.objectStore('test-store');
|
| + const subChecks = [];
|
| + let resultIndex = 0;
|
| + for (let i = 0; i < valueDescriptors.length; ++i) {
|
| + subChecks.push(new Promise((resolve, reject) => {
|
| + const requestIndex = i;
|
| + const request = store.get(requestIndex);
|
| + request.onerror =
|
| + testCase.step_func(() => { reject(request.error); });
|
| + request.onsuccess = testCase.step_func(() => {
|
| + assert_equals(
|
| + resultIndex, requestIndex,
|
| + 'IDBRequest success events should be fired in request order');
|
| + ++resultIndex;
|
| + resolve(checkValue(
|
| + testCase, request.result, valueDescriptors[requestIndex]));
|
| + });
|
| + }));
|
| + }
|
| + return Promise.all(subChecks);
|
| + });
|
| + }, label);
|
| +}
|
| +
|
| +cloningTest('small typed array', [
|
| + { type: 'buffer', size: 64, seed: 1 },
|
| +]);
|
| +
|
| +cloningTest('large typed array', [
|
| + { type: 'buffer', size: wrapThreshold, seed: 1 },
|
| +])
|
| +
|
| +cloningTest('blob', [
|
| + { type: 'blob', size: wrapThreshold, seed: 1 },
|
| +]);
|
| +
|
| +cloningTest('blob with small typed array', [
|
| + {
|
| + blob: { type: 'blob', size: wrapThreshold, seed: 1 },
|
| + buffer: { type: 'buffer', size: 64, seed: 2 },
|
| + },
|
| +]);
|
| +
|
| +cloningTest('blob with large typed array', [
|
| + {
|
| + blob: { type: 'blob', size: wrapThreshold, seed: 1 },
|
| + buffer: { type: 'buffer', size: wrapThreshold, seed: 2 },
|
| + },
|
| +]);
|
| +
|
| +cloningTest('blob array', [
|
| + [
|
| + { type: 'blob', size: wrapThreshold, seed: 1 },
|
| + { type: 'blob', size: wrapThreshold, seed: 2 },
|
| + { type: 'blob', size: wrapThreshold, seed: 3 },
|
| + ],
|
| +]);
|
| +
|
| +cloningTest('array of blobs and small typed arrays', [
|
| + [
|
| + { type: 'blob', size: wrapThreshold, seed: 1 },
|
| + { type: 'buffer', size: 64, seed: 2 },
|
| + { type: 'blob', size: wrapThreshold, seed: 3 },
|
| + { type: 'buffer', size: 64, seed: 4 },
|
| + { type: 'blob', size: wrapThreshold, seed: 5 },
|
| + ],
|
| +]);
|
| +
|
| +cloningTest('array of blobs and large typed arrays', [
|
| + [
|
| + { type: 'blob', size: wrapThreshold, seed: 1 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 2 },
|
| + { type: 'blob', size: wrapThreshold, seed: 3 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 4 },
|
| + { type: 'blob', size: wrapThreshold, seed: 5 },
|
| + ],
|
| +]);
|
| +
|
| +cloningTest('object with blobs and large typed arrays', [
|
| + {
|
| + blob: { type: 'blob', size: wrapThreshold, seed: 1 },
|
| + more: [
|
| + { type: 'buffer', size: wrapThreshold, seed: 2 },
|
| + { type: 'blob', size: wrapThreshold, seed: 3 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 4 },
|
| + ],
|
| + blob2: { type: 'blob', size: wrapThreshold, seed: 5 },
|
| + },
|
| +]);
|
| +
|
| +cloningTest('multiple requests of objects with blobs and large typed arrays', [
|
| + {
|
| + blob: { type: 'blob', size: wrapThreshold, seed: 1 },
|
| + more: [
|
| + { type: 'buffer', size: wrapThreshold, seed: 2 },
|
| + { type: 'blob', size: wrapThreshold, seed: 3 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 4 },
|
| + ],
|
| + blob2: { type: 'blob', size: wrapThreshold, seed: 5 },
|
| + },
|
| + [
|
| + { type: 'blob', size: wrapThreshold, seed: 6 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 7 },
|
| + { type: 'blob', size: wrapThreshold, seed: 8 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 9 },
|
| + { type: 'blob', size: wrapThreshold, seed: 10 },
|
| + ],
|
| + {
|
| + data: [
|
| + { type: 'blob', size: wrapThreshold, seed: 11 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 12 },
|
| + { type: 'blob', size: wrapThreshold, seed: 13 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 14 },
|
| + { type: 'blob', size: wrapThreshold, seed: 15 },
|
| + ],
|
| + },
|
| + [
|
| + { type: 'blob', size: wrapThreshold, seed: 16 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 17 },
|
| + { type: 'blob', size: wrapThreshold, seed: 18 },
|
| + { type: 'buffer', size: wrapThreshold, seed: 19 },
|
| + { type: 'blob', size: wrapThreshold, seed: 20 },
|
| + ],
|
| +]);
|
| +
|
| +</script>
|
|
|