summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/test/unit/test_idle_maintenance.js
blob: b73dfb98484a5cec6d6f675ff7d7e6c0ac456e50 (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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/**
 * Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */
/* eslint-disable mozilla/no-arbitrary-setTimeout */

/* exported testGenerator */
var testGenerator = testSteps();

function* testSteps() {
  let uri = Services.io.newURI("https://www.example.com");
  let principal = Services.scriptSecurityManager.createContentPrincipal(
    uri,
    {}
  );

  info("Setting permissions");

  Services.perms.addFromPrincipal(
    principal,
    "indexedDB",
    Ci.nsIPermissionManager.ALLOW_ACTION
  );

  // The idle-daily notification is disabled in xpchsell tests, so we don't
  // need to do anything special to disable it for this test.

  info("Activating real idle service");

  do_get_idle();

  info("Creating databases");

  // Keep at least one database open.
  let req = indexedDB.open("foo-a", 1);
  req.onerror = errorHandler;
  req.onsuccess = grabEventAndContinueHandler;
  let event = yield undefined;

  // Keep at least one factory operation alive by deleting a database that is
  // stil open.
  req = indexedDB.open("foo-b", 1);
  req.onerror = errorHandler;
  req.onsuccess = grabEventAndContinueHandler;
  event = yield undefined;

  indexedDB.deleteDatabase("foo-b");

  // Create a database which we will later try to open while maintenance is
  // performed.
  req = indexedDB.open("foo-c", 1);
  req.onerror = errorHandler;
  req.onsuccess = grabEventAndContinueHandler;
  event = yield undefined;

  let dbC = event.target.result;
  dbC.close();

  let dbCount = 0;

  for (let persistence of ["persistent", "temporary", "default"]) {
    for (let i = 1; i <= 5; i++) {
      let dbName = "foo-" + i;
      let dbPersistence = persistence;
      let req = indexedDB.openForPrincipal(principal, dbName, {
        version: 1,
        storage: dbPersistence,
      });
      req.onerror = event => {
        if (dbPersistence != "persistent") {
          errorHandler(event);
          return;
        }

        // Explicit persistence is currently blocked on mobile.
        info(
          "Failed to create persistent database '" +
            dbPersistence +
            "/" +
            dbName +
            "', hopefully this is on mobile!"
        );

        event.preventDefault();

        if (!--dbCount) {
          continueToNextStep();
        }
      };
      req.onupgradeneeded = event => {
        let db = event.target.result;
        let objectStore = db.createObjectStore("foo");

        // Add lots of data...
        for (let j = 0; j < 100; j++) {
          objectStore.add("abcdefghijklmnopqrstuvwxyz0123456789", j);
        }

        // And then clear it so that maintenance has some space to reclaim.
        objectStore.clear();
      };
      req.onsuccess = event => {
        let db = event.target.result;
        ok(db, "Created database '" + dbPersistence + "/" + dbName + "'");

        db.close();

        if (!--dbCount) {
          continueToNextStep();
        }
      };
      dbCount++;
    }
  }
  yield undefined;

  info("Getting usage before maintenance");

  let usageBeforeMaintenance;

  Services.qms.getUsageForPrincipal(principal, request => {
    let usage = request.result.usage;
    ok(usage > 0, "Usage is non-zero");
    usageBeforeMaintenance = usage;
    continueToNextStep();
  });
  yield undefined;

  info("Sending fake 'idle-daily' notification to QuotaManager");

  let observer = Services.qms.QueryInterface(Ci.nsIObserver);
  observer.observe(null, "idle-daily", "");

  info("Opening database while maintenance is performed");

  req = indexedDB.open("foo-c", 1);
  req.onerror = errorHandler;
  req.onsuccess = grabEventAndContinueHandler;
  yield undefined;

  info("Waiting for maintenance to start");

  // This time is totally arbitrary. Most likely directory scanning will have
  // completed, QuotaManager locks will be acquired, and  maintenance tasks will
  // be scheduled before this time has elapsed, so we will be testing the
  // maintenance code. However, if something is slow then this will test
  // shutting down in the middle of maintenance.
  setTimeout(continueToNextStep, 10000);
  yield undefined;

  info("Getting usage after maintenance");

  let usageAfterMaintenance;

  Services.qms.getUsageForPrincipal(principal, request => {
    let usage = request.result.usage;
    ok(usage > 0, "Usage is non-zero");
    usageAfterMaintenance = usage;
    continueToNextStep();
  });
  yield undefined;

  info(
    "Usage before: " +
      usageBeforeMaintenance +
      ". " +
      "Usage after: " +
      usageAfterMaintenance
  );

  ok(
    usageAfterMaintenance <= usageBeforeMaintenance,
    "Maintenance decreased file sizes or left them the same"
  );

  finishTest();
  yield undefined;
}