nested-cloning-common.js (7855B)
1 'use strict'; 2 3 // Should be large enough to trigger large value handling in the IndexedDB 4 // engines that have special code paths for large values. 5 const wrapThreshold = 128 * 1024; 6 7 // Returns an IndexedDB value created from a descriptor. 8 // 9 // See the bottom of the file for descriptor samples. 10 function createValue(descriptor) { 11 if (typeof(descriptor) != 'object') 12 return descriptor; 13 14 if (Array.isArray(descriptor)) 15 return descriptor.map((element) => createValue(element)); 16 17 if (!descriptor.hasOwnProperty('type')) { 18 const value = {}; 19 for (let property of Object.getOwnPropertyNames(descriptor)) 20 value[property] = createValue(descriptor[property]); 21 return value; 22 } 23 24 switch (descriptor.type) { 25 case 'blob': 26 return new Blob( 27 [largeValue(descriptor.size, descriptor.seed)], 28 { type: descriptor.mimeType }); 29 case 'buffer': 30 return largeValue(descriptor.size, descriptor.seed); 31 } 32 } 33 34 // Checks an IndexedDB value against a descriptor. 35 // 36 // Returns a Promise that resolves if the value passes the check. 37 // 38 // See the bottom of the file for descriptor samples. 39 function checkValue(testCase, value, descriptor) { 40 if (typeof(descriptor) != 'object') { 41 assert_equals( 42 descriptor, value, 43 'IndexedDB result should match put() argument'); 44 return Promise.resolve(); 45 } 46 47 if (Array.isArray(descriptor)) { 48 assert_true( 49 Array.isArray(value), 50 'IndexedDB result type should match put() argument'); 51 assert_equals( 52 descriptor.length, value.length, 53 'IndexedDB result array size should match put() argument'); 54 55 const subChecks = []; 56 for (let i = 0; i < descriptor.length; ++i) 57 subChecks.push(checkValue(testCase, value[i], descriptor[i])); 58 return Promise.all(subChecks); 59 } 60 61 if (!descriptor.hasOwnProperty('type')) { 62 assert_array_equals( 63 Object.getOwnPropertyNames(value).sort(), 64 Object.getOwnPropertyNames(descriptor).sort(), 65 'IndexedDB result object properties should match put() argument'); 66 const subChecks = []; 67 return Promise.all(Object.getOwnPropertyNames(descriptor).map(property => 68 checkValue(testCase, value[property], descriptor[property]))); 69 } 70 71 switch (descriptor.type) { 72 case 'blob': 73 assert_class_string( 74 value, 'Blob', 75 'IndexedDB result class should match put() argument'); 76 assert_equals( 77 descriptor.mimeType, value.type, 78 'IndexedDB result Blob MIME type should match put() argument'); 79 assert_equals(descriptor.size, value.size, 'incorrect Blob size'); 80 return new Promise((resolve, reject) => { 81 const reader = new FileReader(); 82 reader.onloadend = testCase.step_func(() => { 83 if (reader.error) { 84 reject(reader.error); 85 return; 86 } 87 const view = new Uint8Array(reader.result); 88 assert_equals( 89 view.join(','), 90 largeValue(descriptor.size, descriptor.seed).join(','), 91 'IndexedDB result Blob content should match put() argument'); 92 resolve(); 93 }); 94 reader.readAsArrayBuffer(value); 95 }); 96 97 case 'buffer': 98 assert_class_string( 99 value, 'Uint8Array', 100 'IndexedDB result type should match put() argument'); 101 assert_equals( 102 value.join(','), 103 largeValue(descriptor.size, descriptor.seed).join(','), 104 'IndexedDB result typed array content should match put() argument'); 105 return Promise.resolve(); 106 } 107 } 108 109 function cloningTestInternal(label, valueDescriptors, options) { 110 promise_test(testCase => { 111 return createDatabase(testCase, (database, transaction) => { 112 testCase.add_cleanup(() => database.close()); 113 let store; 114 if (options.useKeyGenerator) { 115 store = database.createObjectStore( 116 'test-store', { keyPath: 'primaryKey', autoIncrement: true }); 117 } else { 118 store = database.createObjectStore('test-store'); 119 } 120 for (let i = 0; i < valueDescriptors.length; ++i) { 121 if (options.useKeyGenerator) { 122 store.put(createValue(valueDescriptors[i])); 123 } else { 124 store.put(createValue(valueDescriptors[i]), i + 1); 125 } 126 } 127 }).then(database => { 128 const transaction = database.transaction(['test-store'], 'readonly'); 129 const store = transaction.objectStore('test-store'); 130 const subChecks = []; 131 let resultIndex = 0; 132 for (let i = 0; i < valueDescriptors.length; ++i) { 133 subChecks.push(new Promise((resolve, reject) => { 134 const requestIndex = i; 135 const primaryKey = requestIndex + 1; 136 const request = store.get(primaryKey); 137 request.onerror = 138 testCase.step_func(() => { reject(request.error); }); 139 request.onsuccess = testCase.step_func(() => { 140 assert_equals( 141 resultIndex, requestIndex, 142 'IDBRequest success events should be fired in request order'); 143 ++resultIndex; 144 145 const result = request.result; 146 if (options.useKeyGenerator) { 147 assert_equals( 148 result.primaryKey, primaryKey, 149 'IndexedDB result should have auto-incremented primary key'); 150 delete result.primaryKey; 151 } 152 resolve(checkValue( 153 testCase, result, valueDescriptors[requestIndex])); 154 }); 155 })); 156 } 157 158 subChecks.push(new Promise((resolve, reject) => { 159 const requestIndex = valueDescriptors.length; 160 const request = store.getAll(); 161 request.onerror = 162 testCase.step_func(() => { reject(request.error); }); 163 request.onsuccess = testCase.step_func(() => { 164 assert_equals( 165 resultIndex, requestIndex, 166 'IDBRequest success events should be fired in request order'); 167 ++resultIndex; 168 const result = request.result; 169 if (options.useKeyGenerator) { 170 for (let i = 0; i < valueDescriptors.length; ++i) { 171 const primaryKey = i + 1; 172 assert_equals( 173 result[i].primaryKey, primaryKey, 174 'IndexedDB result should have auto-incremented primary key'); 175 delete result[i].primaryKey; 176 } 177 } 178 resolve(checkValue(testCase, result, valueDescriptors)); 179 }); 180 })); 181 182 return Promise.all(subChecks); 183 }); 184 }, label); 185 } 186 187 // Performs a series of put()s and verifies that get()s and getAll() match. 188 // 189 // Each element of the valueDescriptors array is fed into createValue(), and the 190 // resulting value is written to IndexedDB via a put() request. After the writes 191 // complete, the values are read in the same order in which they were written. 192 // Last, all the results are read one more time via a getAll(). 193 // 194 // The test verifies that the get() / getAll() results match the arguments to 195 // put() and that the order in which the get() result events are fired matches 196 // the order of the get() requests. 197 function cloningTest(label, valueDescriptors) { 198 cloningTestInternal(label, valueDescriptors, { useKeyGenerator: false }); 199 } 200 201 // cloningTest, with coverage for key generators. 202 // 203 // This creates two tests. One test performs a series of put()s and verifies 204 // that get()s and getAll() match, exactly like cloningTestWithoutKeyGenerator. 205 // The other test performs the same put()s in an object store with a key 206 // generator, and checks that the key generator works properly. 207 function cloningTestWithKeyGenerator(label, valueDescriptors) { 208 cloningTestInternal(label, valueDescriptors, { useKeyGenerator: false }); 209 cloningTestInternal( 210 label + " with key generator", valueDescriptors, 211 { useKeyGenerator: true }); 212 }