tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

request-event-ordering-common.js (9860B)


      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 function populateStore(store) {
      8  store.put({id: 1, key: 'k1', value: largeValue(wrapThreshold, 1)});
      9  store.put({id: 2, key: 'k2', value: ['small-2']});
     10  store.put({id: 3, key: 'k3', value: largeValue(wrapThreshold, 3)});
     11  store.put({id: 4, key: 'k4', value: ['small-4']});
     12 }
     13 
     14 // Assigns cursor indexes for operations that require open cursors.
     15 //
     16 // Returns the number of open cursors required to perform all operations.
     17 function assignCursors(operations) {
     18  return cursorCount;
     19 }
     20 
     21 // Opens index cursors for operations that require open cursors.
     22 //
     23 // onsuccess is called if all cursors are opened successfully. Otherwise,
     24 // onerror will be called at least once.
     25 function openCursors(testCase, index, operations, onerror, onsuccess) {
     26  let pendingCursors = 0;
     27 
     28  for (let operation of operations) {
     29    const opcode = operation[0];
     30    const primaryKey = operation[1];
     31    let request;
     32    switch (opcode) {
     33      case 'continue':
     34        request =
     35            index.openCursor(IDBKeyRange.lowerBound(`k${primaryKey - 1}`));
     36        break;
     37      case 'continue-empty':
     38        // k4 is the last key in the data set, so calling continue() will get
     39        // the cursor past the end of the store.
     40        request = index.openCursor(IDBKeyRange.lowerBound('k4'));
     41        break;
     42      default:
     43        continue;
     44    }
     45 
     46    operation[2] = request;
     47    ++pendingCursors;
     48 
     49    request.onsuccess = testCase.step_func(() => {
     50      --pendingCursors;
     51      if (!pendingCursors)
     52        onsuccess();
     53    });
     54    request.onerror = testCase.step_func(onerror);
     55  }
     56 
     57  if (!pendingCursors)
     58    onsuccess();
     59 }
     60 
     61 function doOperation(testCase, store, index, operation, requestId, results) {
     62  const opcode = operation[0];
     63  const primaryKey = operation[1];
     64  const cursor = operation[2];
     65 
     66  return new Promise((resolve, reject) => {
     67    let request;
     68    switch (opcode) {
     69      case 'add':  // Tests returning a primary key.
     70        request =
     71            store.add({key: `k${primaryKey}`, value: [`small-${primaryKey}`]});
     72        break;
     73      case 'put':  // Tests returning a primary key.
     74        request =
     75            store.put({key: `k${primaryKey}`, value: [`small-${primaryKey}`]});
     76        break;
     77      case 'put-with-id':  // Tests returning success or a primary key.
     78        request = store.put({
     79          key: `k${primaryKey}`,
     80          value: [`small-${primaryKey}`],
     81          id: primaryKey
     82        });
     83        break;
     84      case 'get':        // Tests returning a value.
     85      case 'get-empty':  // Tests returning undefined.
     86        request = store.get(primaryKey);
     87        break;
     88      case 'getall':  // Tests returning an array of values.
     89        request = store.getAll();
     90        break;
     91      case 'error':  // Tests returning an error.
     92        request =
     93            store.put({key: `k${primaryKey}`, value: [`small-${primaryKey}`]});
     94        request.onerror = testCase.step_func(event => {
     95          event.preventDefault();
     96          results.push([requestId, request.error]);
     97          resolve();
     98        });
     99        request.onsuccess = testCase.step_func(() => {
    100          reject(new Error('put with duplicate primary key succeded'));
    101        });
    102        break;
    103      case 'continue':  // Tests returning a key, primary key, and value.
    104        request = cursor;
    105        cursor.result.continue();
    106        request.onsuccess = testCase.step_func(() => {
    107          const result = request.result;
    108          results.push(
    109              [requestId, result.key, result.primaryKey, result.value]);
    110          resolve();
    111        });
    112        request.onerror = null;
    113        break;
    114      case 'open':  // Tests returning a cursor, key, primary key, and value.
    115        request = index.openCursor(IDBKeyRange.lowerBound(`k${primaryKey}`));
    116        request.onsuccess = testCase.step_func(() => {
    117          const result = request.result;
    118          results.push(
    119              [requestId, result.key, result.primaryKey, result.value]);
    120          resolve();
    121        });
    122        break;
    123      case 'continue-empty':  // Tests returning a null result.
    124        request = cursor;
    125        cursor.result.continue();
    126        request.onsuccess = testCase.step_func(() => {
    127          results.push([requestId, request.result]);
    128          resolve();
    129        });
    130        request.onerror = null;
    131        break;
    132      case 'open-empty':  // Tests returning a null cursor.
    133        request = index.openCursor(IDBKeyRange.lowerBound(`k${primaryKey}`));
    134        request.onsuccess = testCase.step_func(() => {
    135          const result = request.result;
    136          results.push([requestId, request.result]);
    137          resolve();
    138        });
    139        break;
    140      case 'count':  // Tests returning a numeric result.
    141        request = index.count();
    142        request.onsuccess = testCase.step_func(() => {
    143          results.push([requestId, request.result]);
    144          resolve();
    145        });
    146        break;
    147    };
    148 
    149    if (!request.onsuccess) {
    150      request.onsuccess = testCase.step_func(() => {
    151        results.push([requestId, request.result]);
    152        resolve();
    153      });
    154    }
    155    if (!request.onerror)
    156      request.onerror = testCase.step_func(event => {
    157        event.preventDefault();
    158        reject(request.error);
    159      });
    160  });
    161 }
    162 
    163 function checkOperationResult(operation, result, requestId) {
    164  const opcode = operation[0];
    165  const primaryKey = operation[1];
    166 
    167  const expectedValue = (primaryKey == 1 || primaryKey == 3) ?
    168      largeValue(wrapThreshold, primaryKey) :
    169      [`small-${primaryKey}`];
    170 
    171  const requestIndex = result[0];
    172  assert_equals(
    173      requestIndex, requestId, 'result event order should match request order');
    174  switch (opcode) {
    175    case 'put':
    176    case 'put-with-id':
    177    case 'add':
    178      assert_equals(
    179          result[1], primaryKey,
    180          `${opcode} result should be the new object's primary key`);
    181      break;
    182    case 'get':
    183      assert_equals(
    184          result[1].id, primaryKey,
    185          'get result should match put value (primary key)');
    186      assert_equals(
    187          result[1].key, `k${primaryKey}`,
    188          'get result should match put value (key)');
    189      assert_equals(
    190          result[1].value.join(','), expectedValue.join(','),
    191          'get result should match put value (nested value)');
    192      break;
    193    case 'getall':
    194      assert_equals(
    195          result[1].length, primaryKey,
    196          'getAll should return all the objects in the store');
    197      for (let i = 0; i < primaryKey; ++i) {
    198        const object = result[1][i];
    199        assert_equals(
    200            object.id, i + 1,
    201            `getAll result ${i + 1} should match put value (primary key)`);
    202        assert_equals(
    203            object.key, `k${i + 1}`,
    204            `get result ${i + 1} should match put value (key)`);
    205 
    206        const expectedValue = (i == 0 || i == 2) ?
    207            largeValue(wrapThreshold, i + 1) :
    208            [`small-${i + 1}`];
    209        assert_equals(
    210            object.value.join(','), object.value.join(','),
    211            `get result ${i + 1} should match put value (nested value)`);
    212      }
    213      break;
    214    case 'get-empty':
    215      assert_equals(
    216          result[1], undefined, 'get-empty result should be undefined');
    217      break;
    218    case 'error':
    219      assert_equals(
    220          result[1].name, 'ConstraintError',
    221          'incorrect error from put with duplicate primary key');
    222      break;
    223    case 'continue':
    224    case 'open':
    225      assert_equals(
    226          result[1], `k${primaryKey}`,
    227          `${opcode} key should match the key in the put value`);
    228      assert_equals(
    229          result[2], primaryKey,
    230          `${opcode} primary key should match the put value's primary key`);
    231      assert_equals(
    232          result[3].id, primaryKey,
    233          `${opcode} value should match put value (primary key)`);
    234      assert_equals(
    235          result[3].key, `k${primaryKey}`,
    236          `${opcode} value should match put value (key)`);
    237      assert_equals(
    238          result[3].value.join(','), expectedValue.join(','),
    239          `${opcode} value should match put value (nested value)`);
    240      break;
    241    case 'continue-empty':
    242    case 'open-empty':
    243      assert_equals(result[1], null, `${opcode} result should be null`);
    244      break;
    245  }
    246 }
    247 
    248 function eventsTest(label, operations) {
    249  promise_test(testCase => {
    250    return createDatabase(
    251               testCase,
    252               (database, transaction) => {
    253                 const store = database.createObjectStore(
    254                     'test-store', {autoIncrement: true, keyPath: 'id'});
    255                 store.createIndex('test-index', 'key', {unique: true});
    256                 populateStore(store);
    257               })
    258        .then(database => {
    259          const transaction = database.transaction(['test-store'], 'readwrite');
    260          const store = transaction.objectStore('test-store');
    261          const index = store.index('test-index');
    262          return new Promise((resolve, reject) => {
    263            openCursors(testCase, index, operations, reject, () => {
    264              const results = [];
    265              const promises = [];
    266              for (let i = 0; i < operations.length; ++i) {
    267                const promise = doOperation(
    268                    testCase, store, index, operations[i], i, results);
    269                promises.push(promise);
    270              };
    271              resolve(Promise.all(promises).then(() => results));
    272            });
    273          });
    274        })
    275        .then(results => {
    276          assert_equals(
    277              results.length, operations.length,
    278              'Promise.all should resolve after all sub-promises resolve');
    279          for (let i = 0; i < operations.length; ++i)
    280            checkOperationResult(operations[i], results[i], i);
    281        });
    282  }, label);
    283 }