Bug 2063316: Record the time of the most recent update in the installation directory. r=application-update-reviewers,iholmes
Differential Revision: https://phabricator.services.mozilla.com/D322904
This commit is contained in:
committed by
dtownsend@mozilla.com
parent
29f1aafc3c
commit
732bf65881
@@ -1379,6 +1379,51 @@ function checkAppBundleModTime() {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the updater wrote update_telemetry.json to the install directory
|
||||
* with a valid, recent install_timestamp.
|
||||
*/
|
||||
function checkUpdateTelemetry() {
|
||||
let telemetryFile = getApplyDirFile("update_telemetry.json");
|
||||
Assert.ok(
|
||||
telemetryFile.exists(),
|
||||
"update_telemetry.json should exist in the install directory"
|
||||
);
|
||||
let contents = readFile(telemetryFile);
|
||||
Assert.ok(contents, "update_telemetry.json should not be empty");
|
||||
Assert.ok(
|
||||
!contents.includes("\0"),
|
||||
"update_telemetry.json should be UTF-8 encoded (no null bytes)"
|
||||
);
|
||||
let data = JSON.parse(contents);
|
||||
Assert.ok(
|
||||
"install_timestamp" in data,
|
||||
"update_telemetry.json should contain install_timestamp"
|
||||
);
|
||||
let ts = parseInt(data.install_timestamp, 10);
|
||||
Assert.ok(
|
||||
!isNaN(ts) && ts > 0,
|
||||
"install_timestamp should be a positive number"
|
||||
);
|
||||
let nowMs = Date.now();
|
||||
Assert.less(
|
||||
nowMs - ts,
|
||||
300000,
|
||||
"install_timestamp should be within the last 5 minutes"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that update_telemetry.json was NOT written to the install directory.
|
||||
*/
|
||||
function checkNoUpdateTelemetry() {
|
||||
let telemetryFile = getApplyDirFile("update_telemetry.json");
|
||||
Assert.ok(
|
||||
!telemetryFile.exists(),
|
||||
"update_telemetry.json should not exist in the install directory"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs Update Manager checks to verify that the update metadata is correct
|
||||
* and that it is the same after the update xml files are reloaded.
|
||||
|
||||
@@ -23,6 +23,7 @@ async function run_test() {
|
||||
true
|
||||
);
|
||||
checkAppBundleModTime();
|
||||
checkNoUpdateTelemetry();
|
||||
await testPostUpdateProcessing();
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkFilesAfterUpdateFailure(getApplyDirFile);
|
||||
|
||||
@@ -23,6 +23,7 @@ async function run_test() {
|
||||
true
|
||||
);
|
||||
checkAppBundleModTime();
|
||||
checkNoUpdateTelemetry();
|
||||
await testPostUpdateProcessing();
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkFilesAfterUpdateFailure(getApplyDirFile);
|
||||
|
||||
@@ -17,6 +17,7 @@ async function run_test() {
|
||||
await setupUpdaterTest(FILE_PARTIAL_MAR, false);
|
||||
await stageUpdate(STATE_AFTER_STAGE, true, true);
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkNoUpdateTelemetry();
|
||||
checkFilesAfterUpdateFailure(getApplyDirFile);
|
||||
checkUpdateLogContains(ERR_LOADSOURCEFILE_FAILED);
|
||||
await waitForUpdateXMLFiles();
|
||||
|
||||
@@ -17,6 +17,7 @@ async function run_test() {
|
||||
await setupUpdaterTest(FILE_PARTIAL_ZUCCHINI_MAR, false);
|
||||
await stageUpdate(STATE_AFTER_STAGE, true, true);
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkNoUpdateTelemetry();
|
||||
checkFilesAfterUpdateFailure(getApplyDirFile);
|
||||
checkUpdateLogContains(ERR_LOADSOURCEFILE_FAILED);
|
||||
await waitForUpdateXMLFiles();
|
||||
|
||||
@@ -20,12 +20,14 @@ async function run_test() {
|
||||
await setupUpdaterTest(FILE_COMPLETE_MAR, false);
|
||||
await stageUpdate(STATE_AFTER_STAGE, true);
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkNoUpdateTelemetry();
|
||||
checkFilesAfterUpdateSuccess(getStageDirFile, true);
|
||||
checkUpdateLogContents(LOG_COMPLETE_SUCCESS, true);
|
||||
// Switch the application to the staged application that was updated.
|
||||
runUpdate(STATE_SUCCEEDED, true, 0, true);
|
||||
await checkPostUpdateAppLog();
|
||||
checkAppBundleModTime();
|
||||
checkUpdateTelemetry();
|
||||
checkSymLinks();
|
||||
await testPostUpdateProcessing();
|
||||
checkPostUpdateRunningFile(true);
|
||||
|
||||
@@ -20,12 +20,14 @@ async function run_test() {
|
||||
await setupUpdaterTest(FILE_PARTIAL_MAR, true);
|
||||
await stageUpdate(STATE_AFTER_STAGE, true);
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkNoUpdateTelemetry();
|
||||
checkFilesAfterUpdateSuccess(getStageDirFile, true);
|
||||
checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true, false, true);
|
||||
// Switch the application to the staged application that was updated.
|
||||
runUpdate(STATE_SUCCEEDED, true, 0, true);
|
||||
await checkPostUpdateAppLog();
|
||||
checkAppBundleModTime();
|
||||
checkUpdateTelemetry();
|
||||
await testPostUpdateProcessing();
|
||||
checkPostUpdateRunningFile(true);
|
||||
checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
|
||||
|
||||
@@ -20,12 +20,14 @@ async function run_test() {
|
||||
await setupUpdaterTest(FILE_PARTIAL_ZUCCHINI_MAR, true);
|
||||
await stageUpdate(STATE_AFTER_STAGE, true);
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkNoUpdateTelemetry();
|
||||
checkFilesAfterUpdateSuccess(getStageDirFile, true);
|
||||
checkUpdateLogContents(LOG_PARTIAL_SUCCESS, true, false, true);
|
||||
// Switch the application to the staged application that was updated.
|
||||
runUpdate(STATE_SUCCEEDED, true, 0, true);
|
||||
await checkPostUpdateAppLog();
|
||||
checkAppBundleModTime();
|
||||
checkUpdateTelemetry();
|
||||
await testPostUpdateProcessing();
|
||||
checkPostUpdateRunningFile(true);
|
||||
checkFilesAfterUpdateSuccess(getApplyDirFile, false, true);
|
||||
|
||||
@@ -16,6 +16,7 @@ async function run_test() {
|
||||
runUpdate(STATE_SUCCEEDED, false, 0, true);
|
||||
await checkPostUpdateAppLog();
|
||||
checkAppBundleModTime();
|
||||
checkUpdateTelemetry();
|
||||
await testPostUpdateProcessing();
|
||||
checkPostUpdateRunningFile(true);
|
||||
checkFilesAfterUpdateSuccess(getApplyDirFile);
|
||||
|
||||
@@ -20,6 +20,7 @@ async function run_test() {
|
||||
await setupUpdaterTest(FILE_PARTIAL_MAR, false, "test/../");
|
||||
runUpdate(STATE_SUCCEEDED, false, 0, true);
|
||||
checkAppBundleModTime();
|
||||
checkUpdateTelemetry();
|
||||
await testPostUpdateProcessing();
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkFilesAfterUpdateSuccess(getApplyDirFile);
|
||||
|
||||
@@ -20,6 +20,7 @@ async function run_test() {
|
||||
await setupUpdaterTest(FILE_PARTIAL_ZUCCHINI_MAR, false, "test/../");
|
||||
runUpdate(STATE_SUCCEEDED, false, 0, true);
|
||||
checkAppBundleModTime();
|
||||
checkUpdateTelemetry();
|
||||
await testPostUpdateProcessing();
|
||||
checkPostUpdateRunningFile(false);
|
||||
checkFilesAfterUpdateSuccess(getApplyDirFile);
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <chrono>
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -2802,6 +2804,24 @@ static void LaunchCallbackApp(const NS_tchar* workingDir, int argc,
|
||||
#endif
|
||||
}
|
||||
|
||||
static void WriteUpdateTelemetry(const NS_tchar* aInstallDir) {
|
||||
NS_tchar path[MAXPATHLEN];
|
||||
NS_tsnprintf(path, sizeof(path) / sizeof(path[0]),
|
||||
NS_T("%s/update_telemetry.json"), aInstallDir);
|
||||
|
||||
auto nowMs = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
char content[128];
|
||||
snprintf(content, sizeof(content) / sizeof(content[0]),
|
||||
"{\"install_timestamp\":\"%lld\"}", (long long)nowMs);
|
||||
|
||||
AutoFile file(CreateAndOpenFile(path, true));
|
||||
if (file != nullptr) {
|
||||
fwrite(content, strlen(content), 1, file);
|
||||
}
|
||||
}
|
||||
|
||||
static bool WriteToFile(const NS_tchar* aFilename, const char* aStatus) {
|
||||
LOG(("Writing status to file: %s", aStatus));
|
||||
|
||||
@@ -3277,6 +3297,7 @@ static int ProcessReplaceRequest() {
|
||||
#endif
|
||||
|
||||
gSucceeded = true;
|
||||
WriteUpdateTelemetry(gInstallDirPath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3473,6 +3494,9 @@ static void UpdateThreadFunc(void* param) {
|
||||
LOG(("Couldn't set access/modification time on application bundle."));
|
||||
}
|
||||
#endif
|
||||
if (!sStagedUpdate) {
|
||||
WriteUpdateTelemetry(gInstallDirPath);
|
||||
}
|
||||
LOG(("succeeded"));
|
||||
}
|
||||
WriteStatusFile(rv);
|
||||
|
||||
Reference in New Issue
Block a user