Chromium Code Reviews| 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> |
|
jsbell
2017/05/15 23:37:37
aside: I wonder if we should create a subdir for w
pwnall
2017/05/19 18:27:33
That sounds like a very reasonable proposal. So, w
|
| +<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> |
|
jsbell
2017/05/15 23:37:37
Since this test involves multiple sequential trans
pwnall
2017/05/19 18:27:33
Done.
Both tests can probably use that tag. Thank
|
| +'use strict'; |
| + |
| +// Should be large enough to trigger value wrapping in the IndexedDB engines |
|
jsbell
2017/05/15 23:37:37
Maybe explain "wrapping" or say "that have special
pwnall
2017/05/19 18:27:33
Done.
|
| +// 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)) { |
|
jsbell
2017/05/15 23:37:37
If you're feeling fancy, you could do this as:
re
pwnall
2017/05/19 18:27:33
Done.
|
| + 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'); |
|
jsbell
2017/05/15 23:37:37
nit: Word the message in terms of the *expected* o
pwnall
2017/05/19 18:27:33
Done.
|
| + 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 |
|
jsbell
2017/05/15 23:37:37
This tests get() but not getAll()
pwnall
2017/05/19 18:27:33
Done.
Thanks, I don't know how I managed to miss t
|
| +// 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 }); |
|
jsbell
2017/05/15 23:37:37
Why specify keyPath: null which is the default?
pwnall
2017/05/19 18:27:33
Done.
|
| + 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> |