test_InstallationTelemetry.js (6463B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ 3 */ 4 "use strict"; 5 6 const { AppConstants } = ChromeUtils.importESModule( 7 "resource://gre/modules/AppConstants.sys.mjs" 8 ); 9 const { BrowserUsageTelemetry } = ChromeUtils.importESModule( 10 "resource:///modules/BrowserUsageTelemetry.sys.mjs" 11 ); 12 const { TelemetryTestUtils } = ChromeUtils.importESModule( 13 "resource://testing-common/TelemetryTestUtils.sys.mjs" 14 ); 15 ChromeUtils.defineESModuleGetters(this, { 16 FileUtils: "resource://gre/modules/FileUtils.sys.mjs", 17 }); 18 19 const TIMESTAMP_PREF = "app.installation.timestamp"; 20 21 function encodeUtf16(str) { 22 const buf = new ArrayBuffer(str.length * 2); 23 const utf16 = new Uint16Array(buf); 24 for (let i = 0; i < str.length; i++) { 25 utf16[i] = str.charCodeAt(i); 26 } 27 return new Uint8Array(buf); 28 } 29 30 // Returns Promise 31 function writeJsonUtf16(fileName, obj) { 32 const str = JSON.stringify(obj); 33 return IOUtils.write(fileName, encodeUtf16(str)); 34 } 35 36 async function runReport( 37 dataFile, 38 installType, 39 { clearTS, setTS, assertRejects, expectExtra, expectTS, msixPrefixes } 40 ) { 41 // Setup timestamp 42 if (clearTS) { 43 Services.prefs.clearUserPref(TIMESTAMP_PREF); 44 } 45 if (typeof setTS == "string") { 46 Services.prefs.setStringPref(TIMESTAMP_PREF, setTS); 47 } 48 49 // Init events 50 Services.telemetry.clearEvents(); 51 52 // Exercise reportInstallationTelemetry 53 if (typeof assertRejects != "undefined") { 54 await Assert.rejects( 55 BrowserUsageTelemetry.reportInstallationTelemetry(dataFile), 56 assertRejects 57 ); 58 } else if (!msixPrefixes) { 59 await BrowserUsageTelemetry.reportInstallationTelemetry(dataFile); 60 } else { 61 await BrowserUsageTelemetry.reportInstallationTelemetry( 62 dataFile, 63 msixPrefixes 64 ); 65 } 66 67 // Check events 68 TelemetryTestUtils.assertEvents( 69 expectExtra 70 ? [{ object: installType, value: null, extra: expectExtra }] 71 : [], 72 { category: "installation", method: "first_seen" } 73 ); 74 75 // Check timestamp 76 if (typeof expectTS == "string") { 77 Assert.equal(expectTS, Services.prefs.getStringPref(TIMESTAMP_PREF)); 78 } 79 } 80 81 let condition = { 82 skip_if: () => 83 AppConstants.platform !== "win" || 84 !Services.sysinfo.getProperty("hasWinPackageId"), 85 }; 86 add_task(condition, async function testInstallationTelemetryMSIX() { 87 // Unfortunately, we have no way to inject different installation ping data 88 // into the system in a way that doesn't just completely override the code 89 // under test - so other than a basic test of the happy path, there's 90 // nothing we can do here. 91 let msixExtra = { 92 version: AppConstants.MOZ_APP_VERSION, 93 build_id: AppConstants.MOZ_BULIDID, 94 admin_user: "false", 95 from_msi: "false", 96 silent: "false", 97 default_path: "true", 98 install_existed: "false", 99 other_inst: "false", 100 other_msix_inst: "false", 101 profdir_existed: "false", 102 }; 103 104 await runReport("fake", "msix", { 105 expectExtra: msixExtra, 106 }); 107 }); 108 condition = { 109 skip_if: () => 110 AppConstants.platform === "win" && 111 Services.sysinfo.getProperty("hasWinPackageId"), 112 }; 113 add_task(condition, async function testInstallationTelemetry() { 114 let dataFilePath = await IOUtils.createUniqueFile( 115 Services.dirsvc.get("TmpD", Ci.nsIFile).path, 116 "installation-telemetry-test-data" + Math.random() + ".json" 117 ); 118 let dataFile = new FileUtils.File(dataFilePath); 119 120 registerCleanupFunction(async () => { 121 try { 122 await IOUtils.remove(dataFilePath); 123 } catch (ex) { 124 // Ignore remove failure, file may not exist by now 125 } 126 127 Services.prefs.clearUserPref(TIMESTAMP_PREF); 128 }); 129 130 // Test with normal stub data 131 let stubData = { 132 version: "99.0abc", 133 build_id: "123", 134 installer_type: "stub", 135 admin_user: true, 136 install_existed: false, 137 profdir_existed: false, 138 install_timestamp: "0", 139 }; 140 let stubExtra = { 141 version: "99.0abc", 142 build_id: "123", 143 admin_user: "true", 144 install_existed: "false", 145 other_inst: "false", 146 other_msix_inst: "false", 147 profdir_existed: "false", 148 }; 149 150 await writeJsonUtf16(dataFilePath, stubData); 151 await runReport(dataFile, "stub", { 152 clearTS: true, 153 expectExtra: stubExtra, 154 expectTS: "0", 155 }); 156 157 // Check that it doesn't generate another event when the timestamp is unchanged 158 await runReport(dataFile, "stub", { expectTS: "0" }); 159 160 // New timestamp 161 stubData.install_timestamp = "1"; 162 await writeJsonUtf16(dataFilePath, stubData); 163 await runReport(dataFile, "stub", { 164 expectExtra: stubExtra, 165 expectTS: "1", 166 }); 167 168 // Test with normal full data 169 let fullData = { 170 version: "99.0abc", 171 build_id: "123", 172 installer_type: "full", 173 admin_user: false, 174 install_existed: true, 175 profdir_existed: true, 176 silent: false, 177 from_msi: false, 178 default_path: true, 179 180 install_timestamp: "1", 181 }; 182 let fullExtra = { 183 version: "99.0abc", 184 build_id: "123", 185 admin_user: "false", 186 install_existed: "true", 187 other_inst: "false", 188 other_msix_inst: "false", 189 profdir_existed: "true", 190 silent: "false", 191 from_msi: "false", 192 default_path: "true", 193 }; 194 195 await writeJsonUtf16(dataFilePath, fullData); 196 await runReport(dataFile, "full", { 197 clearTS: true, 198 expectExtra: fullExtra, 199 expectTS: "1", 200 }); 201 202 // Check that it doesn't generate another event when the timestamp is unchanged 203 await runReport(dataFile, "full", { expectTS: "1" }); 204 205 // New timestamp and a check to make sure we can find installed MSIX packages 206 // by overriding the prefixes a bit further down. 207 fullData.install_timestamp = "2"; 208 // This check only works on Windows 209 if (AppConstants.platform == "win") { 210 fullExtra.other_msix_inst = "true"; 211 } 212 await writeJsonUtf16(dataFilePath, fullData); 213 await runReport(dataFile, "full", { 214 expectExtra: fullExtra, 215 expectTS: "2", 216 msixPrefixes: ["Microsoft"], 217 }); 218 219 // Missing field 220 delete fullData.install_existed; 221 fullData.install_timestamp = "3"; 222 await writeJsonUtf16(dataFilePath, fullData); 223 await runReport(dataFile, "full", { assertRejects: /install_existed/ }); 224 225 // Malformed JSON 226 await IOUtils.write(dataFilePath, encodeUtf16("hello")); 227 await runReport(dataFile, "stub", { 228 assertRejects: /unexpected character/, 229 }); 230 231 // Missing file, should return with no exception 232 await IOUtils.remove(dataFilePath); 233 await runReport(dataFile, "stub", { setTS: "3", expectTS: "3" }); 234 });