Bug 2063316: Add additional properties to downgrade ping. r=nika,niklas,janerik
Differential Revision: https://phabricator.services.mozilla.com/D320429
This commit is contained in:
committed by
dtownsend@mozilla.com
parent
732bf65881
commit
490a0964f7
@@ -19,11 +19,17 @@ Structure:
|
||||
profileGroupId: <UUID>,
|
||||
payload: {
|
||||
lastVersion: "", // The last version of the application that ran this profile
|
||||
lastBuildId: "", // The last build ID of the application that ran this profile
|
||||
hasSync: <bool>, // Whether the profile is signed in to sync
|
||||
hasBinary: <bool>, // Whether the last version of the application is available to run
|
||||
button: <int> // The button the user chose to click from the UI:
|
||||
// 0 - Quit
|
||||
// 1 - Create new profile
|
||||
isMSIX: <bool>, // Whether this install is an MSIX package
|
||||
profileSelectionReason: "", // How the profile was selected during startup (see the startup.profile_selection_reason metric)
|
||||
daysSinceLock: <int>, // (optional) Days since the profile lock was last held
|
||||
isNewInstall: <bool>, // (Windows only, optional) Whether the current install happened after the profile was last locked
|
||||
isNewUpdate: <bool>, // (optional) Whether the last update was applied after the profile was last locked
|
||||
isDifferentInstall: <bool>, // Whether the profile was last used by a different install of the application
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -30,7 +30,8 @@ interface nsIProfileLock : nsISupports
|
||||
readonly attribute nsIFile localDirectory;
|
||||
|
||||
/**
|
||||
* The timestamp of an existing profile lock at lock time.
|
||||
* The timestamp of an existing profile lock at lock time in milliseconds
|
||||
* from midnight (00:00:00), January 1, 1970 Greenwich Mean Time (GMT).
|
||||
*/
|
||||
readonly attribute PRTime replacedLockTime;
|
||||
|
||||
|
||||
@@ -756,6 +756,10 @@ void nsToolkitProfileService::CompleteStartup() {
|
||||
}
|
||||
}
|
||||
|
||||
const nsACString& nsToolkitProfileService::ProfileSelectionReason() {
|
||||
return mStartupReason;
|
||||
}
|
||||
|
||||
// Tests whether the passed profile was last used by this install.
|
||||
bool nsToolkitProfileService::IsProfileForCurrentInstall(
|
||||
nsToolkitProfile* aProfile) {
|
||||
|
||||
@@ -95,6 +95,7 @@ class nsToolkitProfileService final : public nsIToolkitProfileService {
|
||||
bool HasShowProfileSelector();
|
||||
void UpdateCurrentProfile();
|
||||
void CompleteStartup();
|
||||
const nsACString& ProfileSelectionReason();
|
||||
|
||||
using AsyncFlushPromise =
|
||||
mozilla::MozPromise<bool /* ignored */, nsresult, false>;
|
||||
|
||||
@@ -256,6 +256,7 @@ LOCAL_INCLUDES += [
|
||||
"/js/xpconnect/loader",
|
||||
"/testing/gtest/mozilla",
|
||||
"/third_party/sqlite3/ext/",
|
||||
"/toolkit/components/jsoncpp/include",
|
||||
"/toolkit/crashreporter",
|
||||
"/xpcom/build",
|
||||
]
|
||||
|
||||
+176
-35
@@ -26,6 +26,7 @@
|
||||
#include "mozilla/ProcessType.h"
|
||||
#include "mozilla/ResultExtensions.h"
|
||||
#include "mozilla/RuntimeExceptionModule.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/StaticPrefs_browser.h"
|
||||
#include "mozilla/StaticPrefs_fission.h"
|
||||
@@ -155,6 +156,7 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "json/json.h"
|
||||
#include "nsCRT.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
@@ -179,6 +181,7 @@
|
||||
#include "mozilla/LateWriteChecks.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string_view>
|
||||
|
||||
#ifdef XP_UNIX
|
||||
# include <errno.h>
|
||||
@@ -3322,10 +3325,66 @@ struct FileWriteFunc final : public JSONWriteFunc {
|
||||
}
|
||||
};
|
||||
|
||||
Maybe<PathString> GenerateDowngradeTelemetry(const nsACString& aPingId,
|
||||
const nsCString& aLastVersion,
|
||||
bool aHasSync, int32_t aButton,
|
||||
const nsACString& aChannel) {
|
||||
// Reads aJsonFile and returns the raw install_timestamp value, or Nothing() if
|
||||
// the file is absent, unreadable, or lacks the property.
|
||||
static mozilla::Maybe<uint64_t> ReadInstallTimestamp(nsIFile* aJsonFile,
|
||||
bool aIsUTF16LE) {
|
||||
FILE* raw = nullptr;
|
||||
if (NS_FAILED(aJsonFile->OpenANSIFileDesc("rb", &raw)) || !raw) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
ScopedCloseFile f(raw);
|
||||
|
||||
fseek(f.get(), 0, SEEK_END);
|
||||
auto len = ftell(f.get());
|
||||
if (len <= 0) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
rewind(f.get());
|
||||
|
||||
auto buf = MakeUnique<uint8_t[]>(len);
|
||||
if (fread(buf.get(), 1, len, f.get()) != (size_t)len) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
|
||||
nsAutoCString converted;
|
||||
std::string_view utf8View;
|
||||
if (aIsUTF16LE) {
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
const char16_t* chars = reinterpret_cast<const char16_t*>(buf.get());
|
||||
uint32_t charLen = len / 2;
|
||||
CopyUTF16toUTF8(Span(chars, charLen), converted);
|
||||
utf8View = std::string_view(converted.get(), converted.Length());
|
||||
#else
|
||||
MOZ_ASSERT_UNREACHABLE(
|
||||
"UTF-16LE reading not supported on big-endian architectures");
|
||||
return mozilla::Nothing();
|
||||
#endif
|
||||
} else {
|
||||
utf8View = std::string_view(reinterpret_cast<const char*>(buf.get()), len);
|
||||
}
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
if (!reader.parse(utf8View.data(), utf8View.data() + utf8View.size(), root) ||
|
||||
!root.isMember("install_timestamp")) {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
|
||||
std::string tsStr = root["install_timestamp"].asString();
|
||||
char* end = nullptr;
|
||||
uint64_t val = strtoull(tsStr.c_str(), &end, 10);
|
||||
if (*end != '\0') {
|
||||
return mozilla::Nothing();
|
||||
}
|
||||
return mozilla::Some(val);
|
||||
}
|
||||
|
||||
Maybe<mozilla::PathString> GenerateDowngradeTelemetry(
|
||||
const nsACString& aPingId, const nsCString& aLastVersion, bool aHasSync,
|
||||
int32_t aButton, const nsACString& aChannel,
|
||||
const nsACString& aProfileSelectionReason,
|
||||
mozilla::Maybe<PRTime> aReplacedLockTime, bool aIsDifferentInstall) {
|
||||
nsCOMPtr<nsIPrefService> prefSvc =
|
||||
do_GetService("@mozilla.org/preferences-service;1");
|
||||
NS_ENSURE_TRUE(prefSvc, Nothing());
|
||||
@@ -3358,10 +3417,47 @@ Maybe<PathString> GenerateDowngradeTelemetry(const nsACString& aPingId,
|
||||
}
|
||||
# endif
|
||||
|
||||
time_t now;
|
||||
time(&now);
|
||||
mozilla::Maybe<PRTime> maybeInstallTime;
|
||||
mozilla::Maybe<PRTime> maybeUpdateTime;
|
||||
nsCOMPtr<nsIFile> greDir;
|
||||
if (NS_SUCCEEDED(
|
||||
NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir)))) {
|
||||
# ifdef XP_WIN
|
||||
// installation_telemetry.json uses a Windows FILETIME (100ns intervals
|
||||
// since Jan 1, 1601 UTC).
|
||||
nsCOMPtr<nsIFile> installTelemetry;
|
||||
if (NS_SUCCEEDED(greDir->Clone(getter_AddRefs(installTelemetry))) &&
|
||||
NS_SUCCEEDED(
|
||||
installTelemetry->Append(u"installation_telemetry.json"_ns))) {
|
||||
if (auto filetime =
|
||||
ReadInstallTimestamp(installTelemetry, /* aIsUTF16LE */ true)) {
|
||||
constexpr uint64_t kEpochOffset = 116444736000000000ULL;
|
||||
if (*filetime > kEpochOffset) {
|
||||
// The offset converts to an epoch of 1970, divide by 10 to convert
|
||||
// from 100ns to usec
|
||||
maybeInstallTime =
|
||||
mozilla::Some(PRTime((*filetime - kEpochOffset) / 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
// update_telemetry.json uses a Unix timestamp in milliseconds.
|
||||
nsCOMPtr<nsIFile> updateTelemetry;
|
||||
if (NS_SUCCEEDED(greDir->Clone(getter_AddRefs(updateTelemetry))) &&
|
||||
NS_SUCCEEDED(updateTelemetry->Append(u"update_telemetry.json"_ns))) {
|
||||
if (auto msTime =
|
||||
ReadInstallTimestamp(updateTelemetry, /* aIsUTF16LE */ false)) {
|
||||
maybeUpdateTime =
|
||||
mozilla::Some(PRTime(int64_t(*msTime) * PR_USEC_PER_MSEC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PRTime nowUsec = PR_Now();
|
||||
time_t nowTime = time_t(nowUsec / PR_USEC_PER_SEC);
|
||||
char date[sizeof "YYYY-MM-DDThh:mm:ss.000Z"];
|
||||
strftime(date, sizeof date, "%FT%T.000Z", gmtime(&now));
|
||||
strftime(date, sizeof date, "%FT%T.000Z", gmtime(&nowTime));
|
||||
|
||||
constexpr auto pingType = "downgrade"_ns;
|
||||
|
||||
@@ -3431,6 +3527,27 @@ Maybe<PathString> GenerateDowngradeTelemetry(const nsACString& aPingId,
|
||||
w.BoolProperty("hasSync", aHasSync);
|
||||
w.IntProperty("button", aButton);
|
||||
w.BoolProperty("isMSIX", isMSIX);
|
||||
w.StringProperty("profileSelectionReason",
|
||||
PromiseFlatCString(aProfileSelectionReason));
|
||||
|
||||
w.BoolProperty("isDifferentInstall", aIsDifferentInstall);
|
||||
|
||||
if (aReplacedLockTime) {
|
||||
// aReplacedLockTime is in milliseconds.
|
||||
PRTime lockTimeUsec = *aReplacedLockTime * PR_USEC_PER_MSEC;
|
||||
constexpr int64_t kUsecsPerDay =
|
||||
int64_t(PR_USEC_PER_SEC) * 60 * 60 * 24;
|
||||
int64_t elapsedUsec = nowUsec - lockTimeUsec;
|
||||
if (elapsedUsec >= 0) {
|
||||
w.IntProperty("daysSinceLock", elapsedUsec / kUsecsPerDay);
|
||||
}
|
||||
if (maybeInstallTime) {
|
||||
w.BoolProperty("isNewInstall", *maybeInstallTime > lockTimeUsec);
|
||||
}
|
||||
if (maybeUpdateTime) {
|
||||
w.BoolProperty("isNewUpdate", *maybeUpdateTime > lockTimeUsec);
|
||||
}
|
||||
}
|
||||
}
|
||||
w.EndObject();
|
||||
}
|
||||
@@ -3462,8 +3579,11 @@ bool BuildDowngradePingUrl(const nsACString& aPingId,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SubmitDowngradeTelemetry(const nsCString& aLastVersion,
|
||||
bool aHasSync, int32_t aButton) {
|
||||
static void SubmitDowngradeTelemetry(const nsACString& aProfileSelectionReason,
|
||||
mozilla::Maybe<PRTime> aReplacedLockTime,
|
||||
const nsCString& aLastVersion,
|
||||
bool aHasSync, int32_t aButton,
|
||||
bool aIsDifferentInstall) {
|
||||
nsCOMPtr<nsIPrefService> prefSvc =
|
||||
do_GetService("@mozilla.org/preferences-service;1");
|
||||
NS_ENSURE_TRUE_VOID(prefSvc);
|
||||
@@ -3513,7 +3633,8 @@ static void SubmitDowngradeTelemetry(const nsCString& aLastVersion,
|
||||
}
|
||||
|
||||
Maybe<PathString> filePath = GenerateDowngradeTelemetry(
|
||||
pingId, aLastVersion, aHasSync, aButton, channel);
|
||||
pingId, aLastVersion, aHasSync, aButton, channel, aProfileSelectionReason,
|
||||
aReplacedLockTime, aIsDifferentInstall);
|
||||
if (!filePath) {
|
||||
return;
|
||||
}
|
||||
@@ -3546,7 +3667,8 @@ static const char kProfileDowngradeURL[] =
|
||||
|
||||
static ReturnAbortOnError HandleDetectedDowngrade(
|
||||
nsIFile* aProfileDir, nsINativeAppSupport* aNative,
|
||||
nsIToolkitProfileService* aProfileSvc, const nsCString& aLastVersion) {
|
||||
nsToolkitProfileService* aProfileSvc, nsIProfileLock* aProfileLock,
|
||||
const nsCString& aLastVersion, bool aIsDifferentInstall) {
|
||||
int32_t result = 0;
|
||||
nsresult rv;
|
||||
|
||||
@@ -3625,7 +3747,15 @@ static ReturnAbortOnError HandleDetectedDowngrade(
|
||||
|
||||
paramBlock->GetInt(1, &result);
|
||||
|
||||
SubmitDowngradeTelemetry(aLastVersion, hasSync, result);
|
||||
PRTime replacedLockTime = 0;
|
||||
mozilla::Maybe<PRTime> maybeReplacedLockTime;
|
||||
if (NS_SUCCEEDED(aProfileLock->GetReplacedLockTime(&replacedLockTime)) &&
|
||||
replacedLockTime != 0) {
|
||||
maybeReplacedLockTime = mozilla::Some(replacedLockTime);
|
||||
}
|
||||
SubmitDowngradeTelemetry(aProfileSvc->ProfileSelectionReason(),
|
||||
maybeReplacedLockTime, aLastVersion, hasSync,
|
||||
result, aIsDifferentInstall);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3757,6 +3887,36 @@ CompatCheckResult CheckCompatibility(nsIFile* aProfileDir,
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check whether this is the same install by comparing the platform and app
|
||||
// directories. Done before the version comparison so it is set even for
|
||||
// downgrades.
|
||||
result.isDifferentInstall = ![&]() {
|
||||
nsAutoCString dirBuf;
|
||||
nsCOMPtr<nsIFile> lf;
|
||||
bool eq = false;
|
||||
|
||||
if (NS_FAILED(
|
||||
parser.GetString("Compatibility", "LastPlatformDir", dirBuf)) ||
|
||||
NS_FAILED(NS_NewLocalFileWithPersistentDescriptor(
|
||||
dirBuf, getter_AddRefs(lf))) ||
|
||||
NS_FAILED(lf->Equals(aXULRunnerDir, &eq)) || !eq) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aAppDir) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NS_FAILED(parser.GetString("Compatibility", "LastAppDir", dirBuf)) ||
|
||||
NS_FAILED(NS_NewLocalFileWithPersistentDescriptor(
|
||||
dirBuf, getter_AddRefs(lf))) ||
|
||||
NS_FAILED(lf->Equals(aAppDir, &eq)) || !eq) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}();
|
||||
|
||||
if (!result.lastVersion.Equals(aVersion)) {
|
||||
// The version is not the same. Whether it's a downgrade depends on an
|
||||
// actual comparison:
|
||||
@@ -3777,27 +3937,7 @@ CompatCheckResult CheckCompatibility(nsIFile* aProfileDir,
|
||||
rv = parser.GetString("Compatibility", "LastOSABI", buf);
|
||||
if (NS_FAILED(rv) || !aOSABI.Equals(buf)) return result;
|
||||
|
||||
rv = parser.GetString("Compatibility", "LastPlatformDir", buf);
|
||||
if (NS_FAILED(rv)) return result;
|
||||
|
||||
nsCOMPtr<nsIFile> lf;
|
||||
rv = NS_NewLocalFileWithPersistentDescriptor(buf, getter_AddRefs(lf));
|
||||
if (NS_FAILED(rv)) return result;
|
||||
|
||||
bool eq;
|
||||
rv = lf->Equals(aXULRunnerDir, &eq);
|
||||
if (NS_FAILED(rv) || !eq) return result;
|
||||
|
||||
if (aAppDir) {
|
||||
rv = parser.GetString("Compatibility", "LastAppDir", buf);
|
||||
if (NS_FAILED(rv)) return result;
|
||||
|
||||
rv = NS_NewLocalFileWithPersistentDescriptor(buf, getter_AddRefs(lf));
|
||||
if (NS_FAILED(rv)) return result;
|
||||
|
||||
rv = lf->Equals(aAppDir, &eq);
|
||||
if (NS_FAILED(rv) || !eq) return result;
|
||||
}
|
||||
if (result.isDifferentInstall) return result;
|
||||
|
||||
// If we see this flag, caches are invalid.
|
||||
rv = parser.GetString("Compatibility", "InvalidateCaches", buf);
|
||||
@@ -5707,8 +5847,9 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
|
||||
# ifdef XP_MACOSX
|
||||
InitializeMacApp();
|
||||
# endif
|
||||
rv = HandleDetectedDowngrade(mProfD, mNativeApp, mProfileSvc,
|
||||
compatResult.lastVersion);
|
||||
rv = HandleDetectedDowngrade(mProfD, mNativeApp, mProfileSvc, mProfileLock,
|
||||
compatResult.lastVersion,
|
||||
compatResult.isDifferentInstall);
|
||||
if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS || rv == NS_ERROR_ABORT) {
|
||||
*aExitFlag = true;
|
||||
return 0;
|
||||
|
||||
@@ -93,6 +93,7 @@ struct CompatCheckResult {
|
||||
bool isCompatible = false;
|
||||
bool cachesOK = false;
|
||||
bool isDowngrade = false;
|
||||
bool isDifferentInstall = false;
|
||||
bool hasEncryptedDatabases = false;
|
||||
nsCString lastAppVersion{VoidCString()};
|
||||
nsCString lastAppBuildID{VoidCString()};
|
||||
@@ -108,7 +109,9 @@ CompatCheckResult CheckCompatibility(nsIFile* aProfileDir,
|
||||
#ifdef MOZ_BLOCK_PROFILE_DOWNGRADE
|
||||
mozilla::Maybe<mozilla::PathString> GenerateDowngradeTelemetry(
|
||||
const nsACString& aPingId, const nsCString& aLastVersion, bool aHasSync,
|
||||
int32_t aButton, const nsACString& aChannel);
|
||||
int32_t aButton, const nsACString& aChannel,
|
||||
const nsACString& aProfileSelectionReason,
|
||||
mozilla::Maybe<PRTime> aReplacedLockTime, bool aIsDifferentInstall);
|
||||
|
||||
bool BuildDowngradePingUrl(const nsACString& aPingId,
|
||||
const nsACString& aChannel, nsACString& aUrlOut);
|
||||
|
||||
@@ -137,6 +137,7 @@ TEST_F(CheckCompatibilityTest, VersionMatchesEverythingIdentical) {
|
||||
EXPECT_TRUE(result.isCompatible);
|
||||
EXPECT_TRUE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
@@ -153,6 +154,7 @@ TEST_F(CheckCompatibilityTest, VersionMatchesOSABIDiffers) {
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
@@ -170,6 +172,7 @@ TEST_F(CheckCompatibilityTest, VersionMatchesInvalidateCaches) {
|
||||
EXPECT_TRUE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
@@ -189,6 +192,78 @@ TEST_F(CheckCompatibilityTest, VersionMatchesPurgeCachesFileExists) {
|
||||
EXPECT_TRUE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
}
|
||||
|
||||
TEST_F(CheckCompatibilityTest, VersionMatchesDifferentPlatformDir) {
|
||||
nsCString version("130.0_20250101000000/20250101000000");
|
||||
|
||||
nsCOMPtr<nsIFile> tmpDir;
|
||||
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpDir));
|
||||
nsCOMPtr<nsIFile> otherDir;
|
||||
tmpDir->Clone(getter_AddRefs(otherDir));
|
||||
otherDir->Append(u"test_compat_other_platform"_ns);
|
||||
otherDir->Remove(true);
|
||||
(void)otherDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||
|
||||
nsAutoCString otherDesc;
|
||||
(void)otherDir->GetPersistentDescriptor(otherDesc);
|
||||
|
||||
nsAutoCString appDesc;
|
||||
(void)mAppDir->GetPersistentDescriptor(appDesc);
|
||||
|
||||
nsAutoCString content;
|
||||
content.AppendPrintf(
|
||||
"[Compatibility]\n"
|
||||
"LastVersion=%s\n"
|
||||
"LastOSABI=Darwin_aarch64-gcc3\n"
|
||||
"LastPlatformDir=%s\n"
|
||||
"LastAppDir=%s\n",
|
||||
version.get(), otherDesc.get(), appDesc.get());
|
||||
WriteCompatIni(content);
|
||||
|
||||
CompatCheckResult result =
|
||||
CheckCompatibility(mProfileDir, version, "Darwin_aarch64-gcc3"_ns,
|
||||
mPlatformDir, mAppDir, nullptr);
|
||||
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_TRUE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
|
||||
otherDir->Remove(true);
|
||||
}
|
||||
|
||||
TEST_F(CheckCompatibilityTest, VersionMatchesDifferentAppDir) {
|
||||
nsCString version("130.0_20250101000000/20250101000000");
|
||||
|
||||
nsAutoCString platformDesc;
|
||||
(void)mPlatformDir->GetPersistentDescriptor(platformDesc);
|
||||
|
||||
nsAutoCString content;
|
||||
content.AppendPrintf(
|
||||
"[Compatibility]\n"
|
||||
"LastVersion=%s\n"
|
||||
"LastOSABI=Darwin_aarch64-gcc3\n"
|
||||
"LastPlatformDir=%s\n"
|
||||
"LastAppDir=/nonexistent/path\n",
|
||||
version.get(), platformDesc.get());
|
||||
WriteCompatIni(content);
|
||||
|
||||
CompatCheckResult result =
|
||||
CheckCompatibility(mProfileDir, version, "Darwin_aarch64-gcc3"_ns,
|
||||
mPlatformDir, mAppDir, nullptr);
|
||||
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_TRUE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
@@ -206,6 +281,7 @@ TEST_F(CheckCompatibilityTest, VersionUpgrade) {
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "129.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20240901000000");
|
||||
@@ -223,6 +299,7 @@ TEST_F(CheckCompatibilityTest, VersionDowngrade) {
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_TRUE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "131.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250201000000");
|
||||
@@ -244,6 +321,7 @@ TEST_F(CheckCompatibilityTest, MinorVersionChangeNotDowngrade) {
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.1");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250201000000");
|
||||
@@ -261,6 +339,7 @@ TEST_F(CheckCompatibilityTest, PatchVersionChangeNotDowngrade) {
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0.1");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250201000000");
|
||||
@@ -278,6 +357,7 @@ TEST_F(CheckCompatibilityTest, EncryptedDatabases) {
|
||||
EXPECT_TRUE(result.isCompatible);
|
||||
EXPECT_TRUE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_TRUE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
@@ -294,6 +374,7 @@ TEST_F(CheckCompatibilityTest, EncryptedDatabasesAbsent) {
|
||||
EXPECT_TRUE(result.isCompatible);
|
||||
EXPECT_TRUE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
@@ -309,6 +390,7 @@ TEST_F(CheckCompatibilityTest, NoCompatIni) {
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_TRUE(result.lastAppVersion.IsVoid());
|
||||
EXPECT_TRUE(result.lastAppBuildID.IsVoid());
|
||||
@@ -325,6 +407,7 @@ TEST_F(CheckCompatibilityTest, SafeMode) {
|
||||
EXPECT_FALSE(result.isCompatible);
|
||||
EXPECT_FALSE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "Safe Mode");
|
||||
EXPECT_TRUE(result.lastAppBuildID.IsEmpty());
|
||||
@@ -351,6 +434,7 @@ TEST_F(CheckCompatibilityTest, VersionMatchesNoAppDir) {
|
||||
EXPECT_TRUE(result.isCompatible);
|
||||
EXPECT_TRUE(result.cachesOK);
|
||||
EXPECT_FALSE(result.isDowngrade);
|
||||
EXPECT_FALSE(result.isDifferentInstall);
|
||||
EXPECT_FALSE(result.hasEncryptedDatabases);
|
||||
EXPECT_STREQ(result.lastAppVersion.get(), "130.0");
|
||||
EXPECT_STREQ(result.lastAppBuildID.get(), "20250101000000");
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "json/json.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Printf.h"
|
||||
#include "mozilla/XREAppData.h"
|
||||
#include "nsAppRunner.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
@@ -70,6 +73,10 @@ class DowngradePingTest : public ::testing::Test {
|
||||
Preferences::ClearUser("toolkit.telemetry.server");
|
||||
Preferences::ClearUser("toolkit.telemetry.cachedClientID");
|
||||
Preferences::ClearUser("toolkit.telemetry.cachedProfileGroupID");
|
||||
RemoveUpdateTelemetryJson();
|
||||
#ifdef XP_WIN
|
||||
RemoveInstallTelemetryJson();
|
||||
#endif
|
||||
if (mTmpAppDataDir) {
|
||||
nsCOMPtr<nsIProperties> dirSvc =
|
||||
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
|
||||
@@ -99,6 +106,65 @@ class DowngradePingTest : public ::testing::Test {
|
||||
fclose(f);
|
||||
return bytesRead == static_cast<size_t>(fileSize);
|
||||
}
|
||||
|
||||
bool WriteUpdateTelemetryJson(uint64_t aTimestampMs) {
|
||||
nsCOMPtr<nsIFile> greDir;
|
||||
if (NS_FAILED(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir))))
|
||||
return false;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
if (NS_FAILED(greDir->Clone(getter_AddRefs(file)))) return false;
|
||||
if (NS_FAILED(file->Append(u"update_telemetry.json"_ns))) return false;
|
||||
|
||||
FILE* f = nullptr;
|
||||
if (NS_FAILED(file->OpenANSIFileDesc("w", &f)) || !f) return false;
|
||||
fprintf(f, "{\"install_timestamp\":\"%" PRIu64 "\"}", aTimestampMs);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveUpdateTelemetryJson() {
|
||||
nsCOMPtr<nsIFile> greDir;
|
||||
if (NS_FAILED(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir))))
|
||||
return;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
if (NS_FAILED(greDir->Clone(getter_AddRefs(file)))) return;
|
||||
if (NS_FAILED(file->Append(u"update_telemetry.json"_ns))) return;
|
||||
file->Remove(false);
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
bool WriteInstallTelemetryJson(uint64_t aFileTime) {
|
||||
nsCOMPtr<nsIFile> greDir;
|
||||
if (NS_FAILED(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir))))
|
||||
return false;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
if (NS_FAILED(greDir->Clone(getter_AddRefs(file)))) return false;
|
||||
if (NS_FAILED(file->Append(u"installation_telemetry.json"_ns)))
|
||||
return false;
|
||||
|
||||
char json[128];
|
||||
SprintfLiteral(json, "{\"install_timestamp\":\"%" PRIu64 "\"}", aFileTime);
|
||||
|
||||
nsAutoString utf16;
|
||||
utf16.AppendASCII(json);
|
||||
|
||||
FILE* f = nullptr;
|
||||
if (NS_FAILED(file->OpenANSIFileDesc("wb", &f)) || !f) return false;
|
||||
fwrite(utf16.get(), sizeof(char16_t), utf16.Length(), f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoveInstallTelemetryJson() {
|
||||
nsCOMPtr<nsIFile> greDir;
|
||||
if (NS_FAILED(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(greDir))))
|
||||
return;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
if (NS_FAILED(greDir->Clone(getter_AddRefs(file)))) return;
|
||||
if (NS_FAILED(file->Append(u"installation_telemetry.json"_ns))) return;
|
||||
file->Remove(false);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
TEST_F(DowngradePingTest, MissingClientId) {
|
||||
@@ -106,19 +172,19 @@ TEST_F(DowngradePingTest, MissingClientId) {
|
||||
|
||||
EXPECT_FALSE(GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns));
|
||||
"test-channel"_ns, "default"_ns, mozilla::Nothing(), false));
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, InvalidVersionFormat) {
|
||||
EXPECT_FALSE(GenerateDowngradeTelemetry("test-ping-id"_ns,
|
||||
"131.0-no-underscore"_ns, false, 0,
|
||||
"test-channel"_ns));
|
||||
EXPECT_FALSE(GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0-no-underscore"_ns, false, 0, "test-channel"_ns,
|
||||
"default"_ns, mozilla::Nothing(), false));
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, FullPingStructure) {
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, true, 1,
|
||||
"test-channel"_ns);
|
||||
"test-channel"_ns, "default"_ns, mozilla::Nothing(), true);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
@@ -183,12 +249,14 @@ TEST_F(DowngradePingTest, FullPingStructure) {
|
||||
EXPECT_STREQ(payload["lastBuildId"].asCString(), "20250201000000");
|
||||
EXPECT_TRUE(payload["hasSync"].asBool());
|
||||
EXPECT_EQ(payload["button"].asInt(), 1);
|
||||
EXPECT_TRUE(payload["isDifferentInstall"].asBool());
|
||||
EXPECT_STREQ(payload["profileSelectionReason"].asCString(), "default");
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, PayloadBooleansFalse) {
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns);
|
||||
"test-channel"_ns, "restart"_ns, mozilla::Nothing(), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
@@ -203,4 +271,213 @@ TEST_F(DowngradePingTest, PayloadBooleansFalse) {
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_FALSE(payload["hasSync"].asBool());
|
||||
EXPECT_EQ(payload["button"].asInt(), 0);
|
||||
EXPECT_FALSE(payload["isDifferentInstall"].asBool());
|
||||
EXPECT_STREQ(payload["profileSelectionReason"].asCString(), "restart");
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, IsNewUpdateTrue) {
|
||||
// Update happened after the lock time.
|
||||
PRTime lockTime = PRTime(1700000000) * PR_MSEC_PER_SEC;
|
||||
uint64_t updateTimestampMs = 1701388800000ULL;
|
||||
|
||||
if (!WriteUpdateTelemetryJson(updateTimestampMs)) return;
|
||||
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns, "default"_ns, mozilla::Some(lockTime), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
nsCString contents;
|
||||
ASSERT_TRUE(ReadPingFile(*result, contents));
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
ASSERT_TRUE(
|
||||
reader.parse(contents.BeginReading(), contents.EndReading(), root));
|
||||
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_TRUE(payload.isMember("isNewUpdate"));
|
||||
EXPECT_TRUE(payload["isNewUpdate"].asBool());
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, IsNewUpdateFalse) {
|
||||
// Update happened before the lock time.
|
||||
uint64_t updateTimestampMs = 1700000000000ULL;
|
||||
PRTime lockTime = PRTime(1701388800) * PR_MSEC_PER_SEC;
|
||||
|
||||
if (!WriteUpdateTelemetryJson(updateTimestampMs)) return;
|
||||
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns, "default"_ns, mozilla::Some(lockTime), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
nsCString contents;
|
||||
ASSERT_TRUE(ReadPingFile(*result, contents));
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
ASSERT_TRUE(
|
||||
reader.parse(contents.BeginReading(), contents.EndReading(), root));
|
||||
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_TRUE(payload.isMember("isNewUpdate"));
|
||||
EXPECT_FALSE(payload["isNewUpdate"].asBool());
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, IsNewUpdateAbsentWithoutLockTime) {
|
||||
uint64_t updateTimestampMs = 1701388800000ULL;
|
||||
|
||||
if (!WriteUpdateTelemetryJson(updateTimestampMs)) return;
|
||||
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns, "default"_ns, mozilla::Nothing(), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
nsCString contents;
|
||||
ASSERT_TRUE(ReadPingFile(*result, contents));
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
ASSERT_TRUE(
|
||||
reader.parse(contents.BeginReading(), contents.EndReading(), root));
|
||||
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_FALSE(payload.isMember("isNewUpdate"));
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, DaysSinceLock) {
|
||||
PRTime lockTime = PRTime(1700000000) * PR_MSEC_PER_SEC;
|
||||
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns, "default"_ns, mozilla::Some(lockTime), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
nsCString contents;
|
||||
ASSERT_TRUE(ReadPingFile(*result, contents));
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
ASSERT_TRUE(
|
||||
reader.parse(contents.BeginReading(), contents.EndReading(), root));
|
||||
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_TRUE(payload.isMember("daysSinceLock"));
|
||||
int64_t expectedDays = (int64_t(time(nullptr)) - 1700000000) / 86400;
|
||||
int64_t actual = payload["daysSinceLock"].asInt64();
|
||||
EXPECT_GE(actual, expectedDays - 1);
|
||||
EXPECT_LE(actual, expectedDays + 1);
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
TEST_F(DowngradePingTest, IsNewInstallTrue) {
|
||||
// Install happened after the lock time.
|
||||
PRTime lockTime = PRTime(1700000000) * PR_MSEC_PER_SEC;
|
||||
constexpr uint64_t kEpochOffset = 116444736000000000ULL;
|
||||
int64_t installUnixSec = 1701388800;
|
||||
uint64_t installFileTime =
|
||||
uint64_t(installUnixSec) * PR_USEC_PER_SEC * 10 + kEpochOffset;
|
||||
|
||||
if (!WriteInstallTelemetryJson(installFileTime)) return;
|
||||
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns, "default"_ns, mozilla::Some(lockTime), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
nsCString contents;
|
||||
ASSERT_TRUE(ReadPingFile(*result, contents));
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
ASSERT_TRUE(
|
||||
reader.parse(contents.BeginReading(), contents.EndReading(), root));
|
||||
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_TRUE(payload.isMember("isNewInstall"));
|
||||
EXPECT_TRUE(payload["isNewInstall"].asBool());
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, IsNewInstallFalse) {
|
||||
// Install happened before the lock time.
|
||||
constexpr uint64_t kEpochOffset = 116444736000000000ULL;
|
||||
int64_t installUnixSec = 1700000000;
|
||||
uint64_t installFileTime =
|
||||
uint64_t(installUnixSec) * PR_USEC_PER_SEC * 10 + kEpochOffset;
|
||||
PRTime lockTime = PRTime(1701388800) * PR_MSEC_PER_SEC;
|
||||
|
||||
if (!WriteInstallTelemetryJson(installFileTime)) return;
|
||||
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns, "default"_ns, mozilla::Some(lockTime), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
nsCString contents;
|
||||
ASSERT_TRUE(ReadPingFile(*result, contents));
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
ASSERT_TRUE(
|
||||
reader.parse(contents.BeginReading(), contents.EndReading(), root));
|
||||
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_TRUE(payload.isMember("isNewInstall"));
|
||||
EXPECT_FALSE(payload["isNewInstall"].asBool());
|
||||
}
|
||||
|
||||
TEST_F(DowngradePingTest, IsNewInstallAbsentWithoutLockTime) {
|
||||
constexpr uint64_t kEpochOffset = 116444736000000000ULL;
|
||||
int64_t installUnixSec = 1701388800;
|
||||
uint64_t installFileTime =
|
||||
uint64_t(installUnixSec) * PR_USEC_PER_SEC * 10 + kEpochOffset;
|
||||
|
||||
if (!WriteInstallTelemetryJson(installFileTime)) return;
|
||||
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns, "default"_ns, mozilla::Nothing(), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
nsCString contents;
|
||||
ASSERT_TRUE(ReadPingFile(*result, contents));
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
ASSERT_TRUE(
|
||||
reader.parse(contents.BeginReading(), contents.EndReading(), root));
|
||||
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_FALSE(payload.isMember("isNewInstall"));
|
||||
}
|
||||
#else
|
||||
TEST_F(DowngradePingTest, IsNewInstallNotPresentOnNonWindows) {
|
||||
PRTime lockTime = PRTime(1700000000) * PR_MSEC_PER_SEC;
|
||||
|
||||
auto result = GenerateDowngradeTelemetry(
|
||||
"test-ping-id"_ns, "131.0_20250201000000/20250201000000"_ns, false, 0,
|
||||
"test-channel"_ns, "default"_ns, mozilla::Some(lockTime), false);
|
||||
ASSERT_TRUE(result);
|
||||
mCreatedPingPath = *result;
|
||||
|
||||
nsCString contents;
|
||||
ASSERT_TRUE(ReadPingFile(*result, contents));
|
||||
|
||||
Json::Value root;
|
||||
Json::Reader reader;
|
||||
ASSERT_TRUE(
|
||||
reader.parse(contents.BeginReading(), contents.EndReading(), root));
|
||||
|
||||
Json::Value payload = root["payload"];
|
||||
EXPECT_FALSE(payload.isMember("isNewInstall"));
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user