tor-browser

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

test_connection_online_backup.js (8238B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * This test file tests the backupToFileAsync function on
      8 * mozIStorageAsyncConnection, which is implemented for mozStorageConnection.
      9 * (but not implemented for mozilla::dom::cache::Connection).
     10 */
     11 
     12 // The name of the backup database file that will be created.
     13 const BACKUP_FILE_NAME = "test_storage.sqlite.backup";
     14 // The number of rows to insert into the test table in the source
     15 // database.
     16 const TEST_ROWS = 10;
     17 // The page size to set on the source database. During setup, we assert that
     18 // this does not match the default page size.
     19 const TEST_PAGE_SIZE = 512;
     20 
     21 /**
     22 * This setup function creates a table inside of the test database and inserts
     23 * some test rows. Critically, it keeps the connection to the database _open_
     24 * so that we can test the scenario where a database is copied with existing
     25 * open connections.
     26 *
     27 * The database is closed in a cleanup function.
     28 */
     29 add_setup(async () => {
     30  let conn = await openAsyncDatabase(getTestDB());
     31 
     32  Assert.notEqual(
     33    conn.defaultPageSize,
     34    TEST_PAGE_SIZE,
     35    "Should not default to having the TEST_PAGE_SIZE"
     36  );
     37 
     38  await executeSimpleSQLAsync(conn, "PRAGMA page_size = " + TEST_PAGE_SIZE);
     39 
     40  let createStmt = conn.createAsyncStatement("CREATE TABLE test(name TEXT)");
     41  await executeAsync(createStmt);
     42  createStmt.finalize();
     43 
     44  registerCleanupFunction(async () => {
     45    await asyncClose(conn);
     46  });
     47 });
     48 
     49 /**
     50 * Erases the test table and inserts TEST_ROWS rows into it.
     51 *
     52 * @param {mozIStorageAsyncConnection} connection
     53 *   The connection to use to prepare the database.
     54 * @returns {Promise<undefined>}
     55 */
     56 async function prepareSourceDatabase(connection) {
     57  await executeSimpleSQLAsync(connection, "DELETE from test");
     58  for (let i = 0; i < TEST_ROWS; ++i) {
     59    let name = `Database row #${i}`;
     60    let stmt = connection.createAsyncStatement(
     61      "INSERT INTO test (name) VALUES (:name)"
     62    );
     63    stmt.params.name = name;
     64    let result = await executeAsync(stmt);
     65    stmt.finalize();
     66    Assert.ok(Components.isSuccessCode(result), `Inserted test row #${i}`);
     67  }
     68 }
     69 
     70 /**
     71 * Gets the test DB prepared with the testing table and rows.
     72 *
     73 * @returns {Promise<mozIStorageAsyncConnection>}
     74 */
     75 async function getPreparedAsyncDatabase() {
     76  let connection = await openAsyncDatabase(getTestDB());
     77  await prepareSourceDatabase(connection);
     78  return connection;
     79 }
     80 
     81 /**
     82 * Creates a copy of the database connected to via connection, and
     83 * returns an nsIFile pointing at the created copy file once the
     84 * copy is complete.
     85 *
     86 * @param {mozIStorageAsyncConnection} connection
     87 *   A connection to a database that should be copied.
     88 * @param {number} [pagesPerStep]
     89 *   The number of pages to copy per step. If not supplied or is 0, falls back
     90 *   to the platform default which is currently 5.
     91 * @param {number} [stepDelayMs]
     92 *   The number of milliseconds to wait between copying step. If not supplied
     93 *   or is 0, falls back to the platform default which is currently 250.
     94 * @returns {Promise<nsIFile>}
     95 */
     96 async function createCopy(connection, pagesPerStep, stepDelayMs) {
     97  let destFilePath = PathUtils.join(PathUtils.profileDir, BACKUP_FILE_NAME);
     98  let destFile = await IOUtils.getFile(destFilePath);
     99  Assert.ok(
    100    !(await IOUtils.exists(destFilePath)),
    101    "Backup file shouldn't exist yet."
    102  );
    103 
    104  await new Promise(resolve => {
    105    connection.backupToFileAsync(
    106      destFile,
    107      result => {
    108        Assert.ok(Components.isSuccessCode(result));
    109        resolve(result);
    110      },
    111      pagesPerStep,
    112      stepDelayMs
    113    );
    114  });
    115 
    116  return destFile;
    117 }
    118 
    119 /**
    120 * Opens up the database at file, asserts that the page_size matches
    121 * TEST_PAGE_SIZE, and that the number of rows in the test table matches
    122 * expectedEntries. Closes the connection after these assertions.
    123 *
    124 * @param {nsIFile} file
    125 *   The database file to be opened and queried.
    126 * @param {number} [expectedEntries=TEST_ROWS]
    127 *   The expected number of rows in the test table. Defaults to TEST_ROWS.
    128 * @returns {Promise<undefined>}
    129 */
    130 async function assertSuccessfulCopy(file, expectedEntries = TEST_ROWS) {
    131  let conn = await openAsyncDatabase(file);
    132 
    133  await executeSimpleSQLAsync(conn, "PRAGMA page_size", resultSet => {
    134    let result = resultSet.getNextRow();
    135    Assert.equal(TEST_PAGE_SIZE, result.getResultByIndex(0));
    136  });
    137 
    138  let stmt = conn.createAsyncStatement("SELECT COUNT(*) FROM test");
    139  let results = await new Promise(resolve => {
    140    executeAsync(stmt, resolve);
    141  });
    142  stmt.finalize();
    143  let row = results.getNextRow();
    144  let count = row.getResultByName("COUNT(*)");
    145  Assert.equal(count, expectedEntries, "Got the expected entries");
    146 
    147  Assert.ok(
    148    !file.leafName.endsWith(".tmp"),
    149    "Should not end in .tmp extension"
    150  );
    151 
    152  await asyncClose(conn);
    153 }
    154 
    155 /**
    156 * Test the basic behaviour of backupToFileAsync, and ensure that the copied
    157 * database has the same characteristics and contents as the source database.
    158 */
    159 add_task(async function test_backupToFileAsync() {
    160  let newConnection = await getPreparedAsyncDatabase();
    161  let copyFile = await createCopy(newConnection);
    162  Assert.ok(
    163    await IOUtils.exists(copyFile.path),
    164    "A new file was created by backupToFileAsync"
    165  );
    166 
    167  await assertSuccessfulCopy(copyFile);
    168  await IOUtils.remove(copyFile.path);
    169  await asyncClose(newConnection);
    170 });
    171 
    172 /**
    173 * Tests that if insertions are underway during a copy, that those insertions
    174 * show up in the copied database.
    175 */
    176 add_task(async function test_backupToFileAsync_during_insert() {
    177  let newConnection = await getPreparedAsyncDatabase();
    178  const NEW_ENTRIES = 5;
    179 
    180  let copyFilePromise = createCopy(newConnection);
    181  let inserts = [];
    182  for (let i = 0; i < NEW_ENTRIES; ++i) {
    183    let name = `New database row #${i}`;
    184    let stmt = newConnection.createAsyncStatement(
    185      "INSERT INTO test (name) VALUES (:name)"
    186    );
    187    stmt.params.name = name;
    188    inserts.push(executeAsync(stmt));
    189    stmt.finalize();
    190  }
    191  await Promise.all(inserts);
    192  let copyFile = await copyFilePromise;
    193 
    194  Assert.ok(
    195    await IOUtils.exists(copyFile.path),
    196    "A new file was created by backupToFileAsync"
    197  );
    198 
    199  await assertSuccessfulCopy(copyFile, TEST_ROWS + NEW_ENTRIES);
    200  await IOUtils.remove(copyFile.path);
    201  await asyncClose(newConnection);
    202 });
    203 
    204 /**
    205 * Tests that alternative pages-per-step and step delay values can be set when
    206 * calling backupToFileAsync.
    207 */
    208 add_task(async function test_backupToFileAsync_during_insert() {
    209  let newConnection = await getPreparedAsyncDatabase();
    210 
    211  // Let's try some higher values...
    212  let copyFile = await createCopy(newConnection, 15, 500);
    213  Assert.ok(
    214    await IOUtils.exists(copyFile.path),
    215    "A new file was created by backupToFileAsync"
    216  );
    217 
    218  await assertSuccessfulCopy(copyFile);
    219  await IOUtils.remove(copyFile.path);
    220 
    221  // And now we'll try some lower values...
    222  copyFile = await createCopy(newConnection, 1, 25);
    223  Assert.ok(
    224    await IOUtils.exists(copyFile.path),
    225    "A new file was created by backupToFileAsync"
    226  );
    227 
    228  await assertSuccessfulCopy(copyFile);
    229  await IOUtils.remove(copyFile.path);
    230 
    231  await asyncClose(newConnection);
    232 });
    233 
    234 /**
    235 * Tests the behaviour of backupToFileAsync as exposed through Sqlite.sys.mjs.
    236 */
    237 add_task(async function test_backupToFileAsync_via_Sqlite_module() {
    238  let xpcomConnection = await getPreparedAsyncDatabase();
    239  let moduleConnection = await Sqlite.openConnection({
    240    path: xpcomConnection.databaseFile.path,
    241  });
    242 
    243  let copyFilePath = PathUtils.join(PathUtils.profileDir, BACKUP_FILE_NAME);
    244  await moduleConnection.backup(copyFilePath);
    245  let copyFile = await IOUtils.getFile(copyFilePath);
    246  Assert.ok(await IOUtils.exists(copyFilePath), "A new file was created");
    247 
    248  await assertSuccessfulCopy(copyFile);
    249  await IOUtils.remove(copyFile.path);
    250 
    251  // Also check that we can plumb through pagesPerStep and stepDelayMs.
    252  await moduleConnection.backup(copyFilePath, 15, 500);
    253  Assert.ok(await IOUtils.exists(copyFilePath), "A new file was created");
    254  await assertSuccessfulCopy(copyFile);
    255  await IOUtils.remove(copyFile.path);
    256 
    257  await moduleConnection.close();
    258  await asyncClose(xpcomConnection);
    259 });