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
|
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the Shutdown Terminator records durations correctly
var Cu = Components.utils;
var Cc = Components.classes;
var Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/osfile.jsm", this);
Cu.import("resource://gre/modules/Timer.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
var {Path, File, Constants} = OS;
var PATH;
var PATH_TMP;
var terminator;
add_task(function* init() {
do_get_profile();
PATH = Path.join(Constants.Path.localProfileDir, "ShutdownDuration.json");
PATH_TMP = PATH + ".tmp";
// Initialize the terminator
// (normally, this is done through the manifest file, but xpcshell
// doesn't take them into account).
do_print("Initializing the Terminator");
terminator = Cc["@mozilla.org/toolkit/shutdown-terminator;1"].
createInstance(Ci.nsIObserver);
terminator.observe(null, "profile-after-change", null);
});
var promiseShutdownDurationData = Task.async(function*() {
// Wait until PATH exists.
// Timeout if it is never created.
do_print("Waiting for file creation: " + PATH);
while (true) {
if ((yield OS.File.exists(PATH))) {
break;
}
do_print("The file does not exist yet. Waiting 1 second.");
yield new Promise(resolve => setTimeout(resolve, 1000));
}
do_print("The file has been created");
let raw = yield OS.File.read(PATH, { encoding: "utf-8"} );
do_print(raw);
return JSON.parse(raw);
});
add_task(function* test_record() {
let PHASE0 = "profile-change-teardown";
let PHASE1 = "profile-before-change";
let PHASE2 = "xpcom-will-shutdown";
let t0 = Date.now();
do_print("Starting shutdown");
terminator.observe(null, "profile-change-teardown", null);
do_print("Moving to next phase");
terminator.observe(null, PHASE1, null);
let data = yield promiseShutdownDurationData();
let t1 = Date.now();
Assert.ok(PHASE0 in data, "The file contains the expected key");
let duration = data[PHASE0];
Assert.equal(typeof duration, "number");
Assert.ok(duration >= 0, "Duration is a non-negative number");
Assert.ok(duration <= Math.ceil((t1 - t0) / 1000) + 1,
"Duration is reasonable");
Assert.equal(Object.keys(data).length, 1, "Data does not contain other durations");
do_print("Cleaning up and moving to next phase");
yield File.remove(PATH);
yield File.remove(PATH_TMP);
do_print("Waiting at least one tick");
let WAIT_MS = 2000;
yield new Promise(resolve => setTimeout(resolve, WAIT_MS));
terminator.observe(null, PHASE2, null);
data = yield promiseShutdownDurationData();
let t2 = Date.now();
Assert.equal(Object.keys(data).sort().join(", "),
[PHASE0, PHASE1].sort().join(", "),
"The file contains the expected keys");
Assert.equal(data[PHASE0], duration, "Duration of phase 0 hasn't changed");
let duration2 = data[PHASE1];
Assert.equal(typeof duration2, "number");
Assert.ok(duration2 >= WAIT_MS / 2000, "We have waited at least " + (WAIT_MS / 2000) + " ticks");
Assert.ok(duration2 <= Math.ceil((t2 - t1) / 1000) + 1,
"Duration is reasonable");
});
function run_test() {
run_next_test();
}
|