summaryrefslogtreecommitdiffstats
path: root/services/settings/test/unit/test_shutdown_handling.js
blob: 2c98f0ab9ba22041c9554080ea21cfaffb1de1f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const { TestUtils } = ChromeUtils.importESModule(
  "resource://testing-common/TestUtils.sys.mjs"
);

const { Database } = ChromeUtils.importESModule(
  "resource://services-settings/Database.sys.mjs"
);
const { RemoteSettingsWorker } = ChromeUtils.importESModule(
  "resource://services-settings/RemoteSettingsWorker.sys.mjs"
);
const { RemoteSettingsClient } = ChromeUtils.importESModule(
  "resource://services-settings/RemoteSettingsClient.sys.mjs"
);

add_task(async function test_shutdown_abort_after_start() {
  // Start a forever transaction:
  let counter = 0;
  let transactionStarted;
  let startedPromise = new Promise(r => {
    transactionStarted = r;
  });
  let promise = Database._executeIDB(
    "records",
    store => {
      // Signal we've started.
      transactionStarted();
      function makeRequest() {
        if (++counter > 1000) {
          Assert.ok(
            false,
            "We ran 1000 requests and didn't get aborted, what?"
          );
          return;
        }
        dump("Making request " + counter + "\n");
        const request = store
          .index("cid")
          .openCursor(IDBKeyRange.only("foopydoo/foo"));
        request.onsuccess = () => {
          makeRequest();
        };
      }
      makeRequest();
    },
    { mode: "readonly" }
  );

  // Wait for the transaction to start.
  await startedPromise;

  Database._shutdownHandler(); // should abort the readonly transaction.

  let rejection;
  await promise.catch(e => {
    rejection = e;
  });
  ok(rejection, "Promise should have rejected.");

  // Now clear the shutdown flag and rejection error:
  Database._cancelShutdown();
  rejection = null;
});

add_task(async function test_shutdown_immediate_abort() {
  // Now abort directly from the successful request.
  let promise = Database._executeIDB(
    "records",
    store => {
      let request = store
        .index("cid")
        .openCursor(IDBKeyRange.only("foopydoo/foo"));
      request.onsuccess = () => {
        // Abort immediately.
        Database._shutdownHandler();
        request = store
          .index("cid")
          .openCursor(IDBKeyRange.only("foopydoo/foo"));
        Assert.ok(false, "IndexedDB allowed opening a cursor after aborting?!");
      };
    },
    { mode: "readonly" }
  );

  let rejection;
  // Wait for the abort
  await promise.catch(e => {
    rejection = e;
  });
  ok(rejection, "Directly aborted promise should also have rejected.");
  // Now clear the shutdown flag and rejection error:
  Database._cancelShutdown();
});

add_task(async function test_shutdown_worker() {
  let client = new RemoteSettingsClient("language-dictionaries");
  const before = await client.get({ syncIfEmpty: false });
  Assert.equal(before.length, 0);

  let records = [{}];
  let importPromise = RemoteSettingsWorker._execute(
    "_test_only_import",
    ["main", "language-dictionaries", records, 0],
    { mustComplete: true }
  );
  let stringifyPromise = RemoteSettingsWorker.canonicalStringify(
    [],
    [],
    Date.now()
  );
  // Change the idle time so we shut the worker down even though we can't
  // set gShutdown from outside of the worker management code.
  Services.prefs.setIntPref(
    "services.settings.worker_idle_max_milliseconds",
    1
  );
  RemoteSettingsWorker._abortCancelableRequests();
  await Assert.rejects(
    stringifyPromise,
    /Shutdown/,
    "Should have aborted the stringify request at shutdown."
  );
  await Assert.rejects(
    importPromise,
    /shutting down/,
    "Ensure imports get aborted during shutdown"
  );
  const after = await client.get({ syncIfEmpty: false });
  Assert.equal(after.length, 0);
  await TestUtils.waitForCondition(() => !RemoteSettingsWorker.worker);
  Assert.ok(
    !RemoteSettingsWorker.worker,
    "Worker should have been terminated."
  );
});