OLD | NEW |
---|---|
(Empty) | |
1 <!doctype html> | |
2 <meta charset="utf8"> | |
3 <meta name="timeout" content="long"> | |
4 <title>IndexedDB: large nested objects are cloned correctly</title> | |
5 <link rel="help" href="https://w3c.github.io/IndexedDB/#abort-transaction"> | |
6 <link rel="author" href="pwnall@chromium.org" title="Victor Costan"> | |
7 <script src="/resources/testharness.js"></script> | |
8 <script src="/resources/testharnessreport.js"></script> | |
9 <script src="support-promises.js"></script> | |
10 <script> | |
11 'use strict'; | |
12 | |
13 // Should be large enough to trigger large value handling in the IndexedDB | |
14 // engines that have special code paths for large values. | |
15 const wrapThreshold = 128 * 1024; | |
dmurph
2017/05/23 18:16:39
Point to the code location where this is held
pwnall
2017/05/25 13:27:11
This is a WPT test, so it should in theory be brow
| |
16 | |
17 // Returns an IndexedDB value created from a descriptor. | |
18 // | |
19 // See the bottom of the file for descriptor samples. | |
20 function createValue(descriptor) { | |
21 if (typeof(descriptor) != 'object') | |
22 return descriptor; | |
23 | |
24 if (Array.isArray(descriptor)) | |
25 return descriptor.map((element) => createValue(element)); | |
26 | |
27 if (!descriptor.hasOwnProperty('type')) { | |
28 const value = {}; | |
29 for (let property of Object.getOwnPropertyNames(descriptor)) | |
30 value[property] = createValue(descriptor[property]); | |
31 return value; | |
32 } | |
33 | |
34 switch (descriptor.type) { | |
35 case 'blob': | |
36 return new Blob( | |
37 [largeValue(descriptor.size, descriptor.seed)], | |
38 { type: descriptor.mimeType }); | |
39 case 'buffer': | |
40 return largeValue(descriptor.size, descriptor.seed); | |
41 } | |
42 } | |
43 | |
44 // Checks an IndexedDB value against a descriptor. | |
45 // | |
46 // Returns a Promise that resolves if the value passes the check. | |
47 // | |
48 // See the bottom of the file for descriptor samples. | |
49 function checkValue(testCase, value, descriptor) { | |
50 if (typeof(descriptor) != 'object') { | |
51 assert_equals( | |
52 descriptor, value, | |
53 'IndexedDB result should match put() argument'); | |
jsbell
2017/05/22 21:54:19
In theory we should also verify that add() and upd
pwnall
2017/05/25 13:27:11
Done (for now).
I added coverage for IDBObjectStor
| |
54 return Promise.resolve(); | |
55 } | |
56 | |
57 if (Array.isArray(descriptor)) { | |
58 assert_true( | |
59 Array.isArray(value), | |
60 'IndexedDB result type should match put() argument'); | |
61 assert_equals( | |
62 descriptor.length, value.length, | |
63 'IndexedDB result array size should match put() argument'); | |
64 | |
65 const subChecks = []; | |
66 for (let i = 0; i < descriptor.length; ++i) | |
67 subChecks.push(checkValue(testCase, value[i], descriptor[i])); | |
68 return Promise.all(subChecks); | |
69 } | |
70 | |
71 if (!descriptor.hasOwnProperty('type')) { | |
72 assert_array_equals( | |
73 Object.getOwnPropertyNames(value).sort(), | |
74 Object.getOwnPropertyNames(descriptor).sort(), | |
75 'IndexedDB result object properties should match put() argument'); | |
76 const subChecks = []; | |
77 return Promise.all(Object.getOwnPropertyNames(descriptor).map(property => | |
78 checkValue(testCase, value[property], descriptor[property]))); | |
79 } | |
80 | |
81 switch (descriptor.type) { | |
82 case 'blob': | |
83 assert_class_string( | |
84 value, 'Blob', | |
85 'IndexedDB result class should match put() argument'); | |
86 assert_equals( | |
87 descriptor.mimeType, value.type, | |
88 'IndexedDB result Blob MIME type should match put() argument'); | |
89 assert_equals(descriptor.size, value.size, 'incorrect Blob size'); | |
90 return new Promise((resolve, reject) => { | |
91 const reader = new FileReader(); | |
92 reader.onloadend = testCase.step_func(() => { | |
93 if (reader.error) { | |
94 reject(reader.error); | |
95 return; | |
96 } | |
97 const view = new Uint8Array(reader.result); | |
98 assert_equals( | |
99 view.join(','), | |
100 largeValue(descriptor.size, descriptor.seed).join(','), | |
101 'IndexedDB result Blob content should match put() argument'); | |
102 resolve(); | |
103 }); | |
104 reader.readAsArrayBuffer(value); | |
105 }); | |
106 | |
107 case 'buffer': | |
108 assert_class_string( | |
109 value, 'Uint8Array', | |
110 'IndexedDB result type should match put() argument'); | |
111 assert_equals( | |
112 value.join(','), | |
113 largeValue(descriptor.size, descriptor.seed).join(','), | |
114 'IndexedDB result typed array content should match put() argument'); | |
115 return Promise.resolve(); | |
116 } | |
117 } | |
118 | |
119 // Performs a series of put()s and verifies that get()s and getAll() match. | |
120 // | |
121 // Each element of the valueDescriptors array is fed into createValue(), and the | |
122 // resulting value is written to IndexedDB via a put() request. After the writes | |
123 // complete, the values are read in the same order in which they were written. | |
124 // Last, all the results are read one more time via a getAll(). | |
125 // | |
126 // The test verifies that the get() / getAll() results match the arguments to | |
127 // put() and that the order in which the get() result events are fired matches | |
128 // the order of the get() requests. | |
129 function cloningTest(label, valueDescriptors) { | |
130 promise_test(testCase => { | |
131 return createDatabase(testCase, (database, transaction) => { | |
132 const store = database.createObjectStore('test-store'); | |
133 for (let i = 0; i < valueDescriptors.length; ++i) { | |
134 store.put(createValue(valueDescriptors[i]), i); | |
135 } | |
136 }).then(database => { | |
137 const transaction = database.transaction(['test-store'], 'readonly'); | |
138 const store = transaction.objectStore('test-store'); | |
139 const subChecks = []; | |
140 let resultIndex = 0; | |
141 for (let i = 0; i < valueDescriptors.length; ++i) { | |
142 subChecks.push(new Promise((resolve, reject) => { | |
143 const requestIndex = i; | |
144 const request = store.get(requestIndex); | |
145 request.onerror = | |
146 testCase.step_func(() => { reject(request.error); }); | |
147 request.onsuccess = testCase.step_func(() => { | |
148 assert_equals( | |
149 resultIndex, requestIndex, | |
150 'IDBRequest success events should be fired in request order'); | |
151 ++resultIndex; | |
152 resolve(checkValue( | |
153 testCase, request.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 resolve(checkValue( | |
169 testCase, request.result, valueDescriptors)); | |
170 }); | |
171 })); | |
172 | |
173 return Promise.all(subChecks); | |
174 }); | |
175 }, label); | |
176 } | |
177 | |
178 cloningTest('small typed array', [ | |
179 { type: 'buffer', size: 64, seed: 1 }, | |
180 ]); | |
181 | |
182 cloningTest('large typed array', [ | |
183 { type: 'buffer', size: wrapThreshold, seed: 1 }, | |
184 ]) | |
185 | |
186 cloningTest('blob', [ | |
187 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-1', seed: 1 }, | |
188 ]); | |
189 | |
190 cloningTest('blob with small typed array', [ | |
191 { | |
192 blob: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-01', | |
193 seed: 1 }, | |
194 buffer: { type: 'buffer', size: 64, seed: 2 }, | |
195 }, | |
196 ]); | |
197 | |
198 cloningTest('blob with large typed array', [ | |
199 { | |
200 blob: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-01', | |
201 seed: 1 }, | |
202 buffer: { type: 'buffer', size: wrapThreshold, seed: 2 }, | |
203 }, | |
204 ]); | |
205 | |
206 cloningTest('blob array', [ | |
207 [ | |
208 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-1', seed: 1 }, | |
209 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-2', seed: 2 }, | |
210 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-3', seed: 3 }, | |
211 ], | |
212 ]); | |
213 | |
214 cloningTest('array of blobs and small typed arrays', [ | |
215 [ | |
216 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-01', seed: 1 }, | |
217 { type: 'buffer', size: 64, seed: 2 }, | |
218 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-03', seed: 3 }, | |
219 { type: 'buffer', size: 64, seed: 4 }, | |
220 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-05', seed: 5 }, | |
221 ], | |
222 ]); | |
223 | |
224 cloningTest('array of blobs and large typed arrays', [ | |
225 [ | |
226 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-01', seed: 1 }, | |
227 { type: 'buffer', size: wrapThreshold, seed: 2 }, | |
228 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-03', seed: 3 }, | |
229 { type: 'buffer', size: wrapThreshold, seed: 4 }, | |
230 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-05', seed: 5 }, | |
231 ], | |
232 ]); | |
233 | |
234 cloningTest('object with blobs and large typed arrays', [ | |
235 { | |
236 blob: { type: 'blob', size: wrapThreshold, | |
237 mimeType: 'text/x-blink1', seed: 1 }, | |
238 more: [ | |
239 { type: 'buffer', size: wrapThreshold, seed: 2 }, | |
240 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink3', seed: 3 }, | |
241 { type: 'buffer', size: wrapThreshold, seed: 4 }, | |
242 ], | |
243 blob2: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink5', | |
244 seed: 5 }, | |
245 }, | |
246 ]); | |
247 | |
248 cloningTest('multiple requests of objects with blobs and large typed arrays', [ | |
249 { | |
250 blob: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink1', | |
251 seed: 1 }, | |
252 more: [ | |
253 { type: 'buffer', size: wrapThreshold, seed: 2 }, | |
254 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink3', seed: 3 }, | |
255 { type: 'buffer', size: wrapThreshold, seed: 4 }, | |
256 ], | |
257 blob2: { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink5', | |
258 seed: 5 }, | |
259 }, | |
260 [ | |
261 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink06', seed: 6 }, | |
262 { type: 'buffer', size: wrapThreshold, seed: 7 }, | |
263 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink08', seed: 8 }, | |
264 { type: 'buffer', size: wrapThreshold, seed: 9 }, | |
265 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink10', seed: 10 }, | |
266 ], | |
267 { | |
268 data: [ | |
269 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-11', | |
270 seed: 11 }, | |
271 { type: 'buffer', size: wrapThreshold, seed: 12 }, | |
272 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-13', | |
273 seed: 13 }, | |
274 { type: 'buffer', size: wrapThreshold, seed: 14 }, | |
275 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink-15', | |
276 seed: 15 }, | |
277 ], | |
278 }, | |
279 [ | |
280 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink16', seed: 16 }, | |
281 { type: 'buffer', size: wrapThreshold, seed: 17 }, | |
282 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink18', seed: 18 }, | |
283 { type: 'buffer', size: wrapThreshold, seed: 19 }, | |
284 { type: 'blob', size: wrapThreshold, mimeType: 'text/x-blink20', seed: 20 }, | |
285 ], | |
286 ]); | |
287 | |
288 </script> | |
OLD | NEW |