Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: third_party/WebKit/LayoutTests/external/wpt/IndexedDB/request-event-ordering.html

Issue 2822453003: Wrap large IndexedDB values into Blobs before writing to LevelDB. (Closed)
Patch Set: Addressed last round of feedback. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!doctype html>
2 <meta charset="utf8">
3 <meta name="timeout" content="long">
4 <title>IndexedDB: request result events are delivered in order</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;
16
17 function populateStore(store) {
18 store.put({id: 1, key: 'k1', value: largeValue(wrapThreshold, 1) });
19 store.put({id: 2, key: 'k2', value: ['small-2'] });
20 store.put({id: 3, key: 'k3', value: largeValue(wrapThreshold, 3) });
21 store.put({id: 4, key: 'k4', value: ['small-4'] });
22 }
23
24 // Assigns cursor indexes for operations that require open cursors.
25 //
26 // Returns the number of open cursors required to perform all operations.
27 function assignCursors(operations) {
28 return cursorCount;
29 }
30
31 // Opens index cursors for operations that require open cursors.
32 //
33 // onsuccess is called if all cursors are opened successfully. Otherwise,
34 // onerror will be called at least once.
35 function openCursors(testCase, index, operations, onerror, onsuccess) {
36 let pendingCursors = 0;
37
38 for (let operation of operations) {
39 const opcode = operation[0];
40 const primaryKey = operation[1];
41 let request;
42 switch (opcode) {
43 case 'continue':
44 request = index.openCursor(
45 IDBKeyRange.lowerBound(`k${primaryKey - 1}`));
46 break;
47 case 'continue-empty':
48 // k4 is the last key in the data set, so calling continue() will get
49 // the cursor past the end of the store.
50 request = index.openCursor(IDBKeyRange.lowerBound('k4'));
51 break;
52 default:
53 continue;
54 }
55
56 operation[2] = request;
57 ++pendingCursors;
58
59 request.onsuccess = testCase.step_func(() => {
60 --pendingCursors;
61 if (!pendingCursors)
62 onsuccess();
63 });
64 request.onerror = testCase.step_func(onerror);
65 }
66
67 if (!pendingCursors)
68 onsuccess();
69 }
70
71 function doOperation(testCase, store, index, operation, requestId, results) {
72 const opcode = operation[0];
73 const primaryKey = operation[1];
74 const cursor = operation[2];
75
76 return new Promise((resolve, reject) => {
77 let request;
78 switch (opcode) {
79 case 'add': // Tests returning a primary key.
80 request = store.add(
81 { key: `k${primaryKey}`, value: [`small-${primaryKey}`] });
82 break;
83 case 'put': // Tests returning a primary key.
84 request = store.put(
85 { key: `k${primaryKey}`, value: [`small-${primaryKey}`] });
86 break;
87 case 'put-with-id': // Tests returning success or a primary key.
88 request = store.put(
89 { key: `k${primaryKey}`, value: [`small-${primaryKey}`],
90 id: primaryKey });
91 break;
92 case 'get': // Tests returning a value.
93 case 'get-empty': // Tests returning undefined.
94 request = store.get(primaryKey);
95 break;
96 case 'getall': // Tests returning an array of values.
97 request = store.getAll();
98 break;
99 case 'error': // Tests returning an error.
100 request = store.put(
101 { key: `k${primaryKey}`, value: [`small-${primaryKey}`] });
102 request.onerror = testCase.step_func(event => {
103 event.preventDefault();
104 results.push([requestId, request.error]);
105 resolve();
106 });
107 request.onsuccess = testCase.step_func(() => {
108 reject(new Error('put with duplicate primary key succeded'));
109 });
110 break;
111 case 'continue': // Tests returning a key, primary key, and value.
112 request = cursor;
113 cursor.result.continue();
114 request.onsuccess = testCase.step_func(() => {
115 const result = request.result;
116 results.push(
117 [requestId, result.key, result.primaryKey, result.value]);
118 resolve();
119 });
120 request.onerror = null;
121 break;
122 case 'open': // Tests returning a cursor, key, primary key, and value.
123 request = index.openCursor(IDBKeyRange.lowerBound(`k${primaryKey}`));
124 request.onsuccess = testCase.step_func(() => {
125 const result = request.result;
126 results.push(
127 [requestId, result.key, result.primaryKey, result.value]);
128 resolve();
129 });
130 break;
131 case 'continue-empty': // Tests returning a null result.
132 request = cursor;
133 cursor.result.continue();
134 request.onsuccess = testCase.step_func(() => {
135 results.push([requestId, request.result]);
136 resolve();
137 });
138 request.onerror = null;
139 break;
140 case 'open-empty': // Tests returning a null cursor.
141 request = index.openCursor(IDBKeyRange.lowerBound(`k${primaryKey}`));
142 request.onsuccess = testCase.step_func(() => {
143 const result = request.result;
144 results.push([requestId, request.result]);
145 resolve();
146 });
147 break;
148 case 'count': // Tests returning a numeric result.
149 request = index.count();
150 request.onsuccess = testCase.step_func(() => {
151 results.push([requestId, request.result]);
152 resolve();
153 });
154 break;
155 };
156
157 if (!request.onsuccess) {
158 request.onsuccess = testCase.step_func(() => {
159 results.push([requestId, request.result]);
160 resolve();
161 });
162 }
163 if (!request.onerror)
164 request.onerror = testCase.step_func(event => {
165 event.preventDefault();
166 reject(request.error);
167 });
168 });
169 }
170
171 function checkOperationResult(operation, result, requestId) {
172 const opcode = operation[0];
173 const primaryKey = operation[1];
174
175 const expectedValue = (primaryKey == 1 || primaryKey == 3) ?
176 largeValue(wrapThreshold, primaryKey) : [`small-${primaryKey}`];
177
178 const requestIndex = result[0];
179 assert_equals(
180 requestIndex, requestId, 'result event order should match request order');
181 switch (opcode) {
182 case 'put':
183 case 'put-with-id':
184 case 'add':
185 assert_equals(
186 result[1], primaryKey,
187 `${opcode} result should be the new object's primary key`);
188 break;
189 case 'get':
190 assert_equals(
191 result[1].id, primaryKey,
192 'get result should match put value (primary key)');
193 assert_equals(
194 result[1].key, `k${primaryKey}`,
195 'get result should match put value (key)');
196 assert_equals(
197 result[1].value.join(','), expectedValue.join(','),
198 'get result should match put value (nested value)');
199 break;
200 case 'getall':
201 assert_equals(
202 result[1].length, primaryKey,
203 'getAll should return all the objects in the store');
204 for (let i = 0; i < primaryKey; ++i) {
205 const object = result[1][i];
206 assert_equals(
207 object.id, i + 1,
208 `getAll result ${i + 1} should match put value (primary key)`);
209 assert_equals(
210 object.key, `k${i + 1}`,
211 `get result ${i + 1} should match put value (key)`);
212
213 const expectedValue = (i == 0 || i == 2) ?
214 largeValue(wrapThreshold, i + 1) : [`small-${i + 1}`];
215 assert_equals(
216 object.value.join(','), object.value.join(','),
217 `get result ${i + 1} should match put value (nested value)`);
218 }
219 break;
220 case 'get-empty':
221 assert_equals(
222 result[1], undefined, 'get-empty result should be undefined');
223 break;
224 case 'error':
225 assert_equals(
226 result[1].name, 'ConstraintError',
227 'incorrect error from put with duplicate primary key');
228 break;
229 case 'continue':
230 case 'open':
231 assert_equals(
232 result[1], `k${primaryKey}`,
233 `${opcode} key should match the key in the put value`);
234 assert_equals(
235 result[2], primaryKey,
236 `${opcode} primary key should match the put value's primary key`);
237 assert_equals(
238 result[3].id, primaryKey,
239 `${opcode} value should match put value (primary key)`);
240 assert_equals(
241 result[3].key, `k${primaryKey}`,
242 `${opcode} value should match put value (key)`);
243 assert_equals(
244 result[3].value.join(','), expectedValue.join(','),
245 `${opcode} value should match put value (nested value)`);
246 break;
247 case 'continue-empty':
248 case 'open-empty':
249 assert_equals(result[1], null, `${opcode} result should be null`);
250 break;
251 }
252 }
253
254 function eventsTest(label, operations) {
255 promise_test(testCase => {
256 return createDatabase(testCase, (database, transaction) => {
257 const store = database.createObjectStore(
258 'test-store', { autoIncrement: true, keyPath: 'id' });
259 store.createIndex('test-index', 'key', { unique: true });
260 populateStore(store);
261 }).then(database => {
262 const transaction = database.transaction(['test-store'], 'readwrite');
263 const store = transaction.objectStore('test-store');
264 const index = store.index('test-index');
265 return new Promise((resolve, reject) => {
266 openCursors(testCase, index, operations, reject, () => {
267 const results = [];
268 const promises = [];
269 for (let i = 0; i < operations.length; ++i) {
270 const promise = doOperation(
271 testCase, store, index, operations[i], i, results);
272 promises.push(promise);
273 };
274 resolve(Promise.all(promises).then(() => results));
275 });
276 });
277 }).then(results => {
278 assert_equals(
279 results.length, operations.length,
280 'Promise.all should resolve after all sub-promises resolve');
281 for (let i = 0; i < operations.length; ++i)
282 checkOperationResult(operations[i], results[i], i);
283 });
284 }, label);
285 }
286
287 eventsTest('small values', [
288 ['get', 2],
289 ['count', 4],
290 ['continue-empty', null],
291 ['get-empty', 5],
292 ['add', 5],
293 ['open', 2],
294 ['continue', 2],
295 ['get', 4],
296 ['get-empty', 6],
297 ['count', 5],
298 ['put-with-id', 5],
299 ['put', 6],
300 ['error', 3],
301 ['continue', 4],
302 ['count', 6],
303 ['get-empty', 7],
304 ['open', 4],
305 ['open-empty', 7],
306 ['add', 7],
307 ]);
308
309 eventsTest('large values', [
310 ['open', 1],
311 ['get', 1],
312 ['getall', 4],
313 ['get', 3],
314 ['continue', 3],
315 ['open', 3],
316 ]);
317
318 eventsTest('large value followed by small values', [
319 ['get', 1],
320 ['getall', 4],
321 ['open', 2],
322 ['continue-empty', null],
323 ['get', 2],
324 ['get-empty', 5],
325 ['count', 4],
326 ['continue-empty', null],
327 ['open-empty', 5],
328 ['add', 5],
329 ['error', 1],
330 ['continue', 2],
331 ['get-empty', 6],
332 ['put-with-id', 5],
333 ['put', 6],
334 ]);
335
336 eventsTest('large values mixed with small values', [
337 ['get', 1],
338 ['get', 2],
339 ['get-empty', 5],
340 ['count', 4],
341 ['continue-empty', null],
342 ['open', 1],
343 ['continue', 2],
344 ['open-empty', 5],
345 ['getall', 4],
346 ['open', 2],
347 ['continue-empty', null],
348 ['add', 5],
349 ['get', 3],
350 ['count', 5],
351 ['get-empty', 6],
352 ['put-with-id', 5],
353 ['getall', 5],
354 ['continue', 3],
355 ['open-empty', 6],
356 ['put', 6],
357 ['error', 1],
358 ['continue', 2],
359 ['open', 4],
360 ['get-empty', 7],
361 ['count', 6],
362 ['continue', 3],
363 ['add', 7],
364 ['getall', 7],
365 ['error', 3],
366 ['count', 7],
367 ]);
368
369 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698