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
|
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
*/
"use strict";
const { TelemetryUtils } = ChromeUtils.importESModule(
"resource://gre/modules/TelemetryUtils.sys.mjs"
);
const { TelemetryArchiveTesting } = ChromeUtils.importESModule(
"resource://testing-common/TelemetryArchiveTesting.sys.mjs"
);
add_task(async function test_updatePing() {
const TEST_VERSION = "37.85";
const TEST_BUILDID = "20150711123724";
const XML_UPDATE = `<?xml version="1.0"?>
<updates xmlns="http://www.mozilla.org/2005/app-update">
<update appVersion="${Services.appinfo.version}" buildID="20080811053724"
channel="nightly" displayVersion="Version 1.0"
installDate="1238441400314" isCompleteUpdate="true" type="minor"
name="Update Test 1.0" detailsURL="http://example.com/"
previousAppVersion="${TEST_VERSION}"
serviceURL="https://example.com/" foregroundDownload="true"
statusText="The Update was successfully installed">
<patch type="complete" URL="http://example.com/" size="775"
selected="true" state="succeeded"/>
</update>
</updates>`;
// Set the preferences needed for the test: they will be cleared up
// after it runs.
await SpecialPowers.pushPrefEnv({
set: [
[TelemetryUtils.Preferences.UpdatePing, true],
["browser.startup.homepage_override.mstone", TEST_VERSION],
["browser.startup.homepage_override.buildID", TEST_BUILDID],
["toolkit.telemetry.log.level", "Trace"],
],
});
registerCleanupFunction(async () => {
let activeUpdateFile = getActiveUpdateFile();
activeUpdateFile.remove(false);
reloadUpdateManagerData(true);
});
writeUpdatesToXMLFile(XML_UPDATE);
reloadUpdateManagerData(false);
// Start monitoring the ping archive.
let archiveChecker = new TelemetryArchiveTesting.Checker();
await archiveChecker.promiseInit();
// Manually call the BrowserContentHandler: this automatically gets called when
// the browser is started and an update was applied successfully in order to
// display the "update" info page.
Cc["@mozilla.org/browser/clh;1"].getService(Ci.nsIBrowserHandler).defaultArgs;
// We cannot control when the ping will be generated/archived after we trigger
// an update, so let's make sure to have one before moving on with validation.
let updatePing;
await BrowserTestUtils.waitForCondition(
async function () {
// Check that the ping made it into the Telemetry archive.
// The test data is defined in ../data/sharedUpdateXML.js
updatePing = await archiveChecker.promiseFindPing("update", [
[["payload", "reason"], "success"],
[["payload", "previousBuildId"], TEST_BUILDID],
[["payload", "previousVersion"], TEST_VERSION],
]);
return !!updatePing;
},
"Make sure the ping is generated before trying to validate it.",
500,
100
);
ok(updatePing, "The 'update' ping must be correctly sent.");
// We have no easy way to simulate a previously applied update from toolkit/telemetry.
// Instead of moving this test to mozapps/update as well, just test that the
// "previousChannel" field is present and either a string or null.
ok(
"previousChannel" in updatePing.payload,
"The payload must contain the 'previousChannel' field"
);
const channelField = updatePing.payload.previousChannel;
if (channelField != null) {
Assert.equal(
typeof channelField,
"string",
"'previousChannel' must be a string, if available."
);
}
// Also make sure that the ping contains both a client id and an
// environment section.
ok("clientId" in updatePing, "The update ping must report a client id.");
ok(
"environment" in updatePing,
"The update ping must report the environment."
);
});
/**
* Removes the updates.xml file and returns the nsIFile for the
* active-update.xml file.
*
* @return The nsIFile for the active-update.xml file.
*/
function getActiveUpdateFile() {
let updateRootDir = Services.dirsvc.get("UpdRootD", Ci.nsIFile);
let updatesFile = updateRootDir.clone();
updatesFile.append("updates.xml");
if (updatesFile.exists()) {
// The following is non-fatal.
try {
updatesFile.remove(false);
} catch (e) {}
}
let activeUpdateFile = updateRootDir.clone();
activeUpdateFile.append("active-update.xml");
return activeUpdateFile;
}
/**
* Reloads the update xml files.
*
* @param skipFiles (optional)
* If true, the update xml files will not be read and the metadata will
* be reset. If false (the default), the update xml files will be read
* to populate the update metadata.
*/
function reloadUpdateManagerData(skipFiles = false) {
Cc["@mozilla.org/updates/update-manager;1"]
.getService(Ci.nsIUpdateManager)
.QueryInterface(Ci.nsIObserver)
.observe(null, "um-reload-update-data", skipFiles ? "skip-files" : "");
}
/**
* Writes the updates specified to the active-update.xml file.
*
* @param aText
* The updates represented as a string to write to the active-update.xml
* file.
*/
function writeUpdatesToXMLFile(aText) {
const PERMS_FILE = 0o644;
const MODE_WRONLY = 0x02;
const MODE_CREATE = 0x08;
const MODE_TRUNCATE = 0x20;
let activeUpdateFile = getActiveUpdateFile();
if (!activeUpdateFile.exists()) {
activeUpdateFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE);
}
let fos = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(
Ci.nsIFileOutputStream
);
let flags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
fos.init(activeUpdateFile, flags, PERMS_FILE, 0);
fos.write(aText, aText.length);
fos.close();
}
|