зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1684971 - Add tests for the new WDBA cache r=nalexander
Differential Revision: https://phabricator.services.mozilla.com/D102224
This commit is contained in:
Родитель
c75575c2c1
Коммит
1ebc9745be
|
@ -46,7 +46,6 @@
|
|||
#endif // !defined(RRF_SUBKEY_WOW6464KEY)
|
||||
|
||||
using TelemetryFieldResult = mozilla::WindowsErrorResult<std::string>;
|
||||
using FilePathResult = mozilla::WindowsErrorResult<std::wstring>;
|
||||
using BoolResult = mozilla::WindowsErrorResult<bool>;
|
||||
|
||||
// This function was copied from the implementation of
|
||||
|
@ -114,27 +113,6 @@ static TelemetryFieldResult GetOSLocale() {
|
|||
return std::string(narrowLocaleName.get());
|
||||
}
|
||||
|
||||
static FilePathResult GenerateUUIDStr() {
|
||||
UUID uuid;
|
||||
RPC_STATUS status = UuidCreate(&uuid);
|
||||
if (status != RPC_S_OK) {
|
||||
HRESULT hr = MAKE_HRESULT(1, FACILITY_RPC, status);
|
||||
LOG_ERROR(hr);
|
||||
return FilePathResult(mozilla::WindowsError::FromHResult(hr));
|
||||
}
|
||||
|
||||
// 39 == length of a UUID string including braces and NUL.
|
||||
wchar_t guidBuf[39] = {};
|
||||
if (StringFromGUID2(uuid, guidBuf, 39) != 39) {
|
||||
LOG_ERROR(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
|
||||
return FilePathResult(
|
||||
mozilla::WindowsError::FromWin32Error(ERROR_INSUFFICIENT_BUFFER));
|
||||
}
|
||||
|
||||
// Remove the curly braces.
|
||||
return std::wstring(guidBuf + 1, guidBuf + 37);
|
||||
}
|
||||
|
||||
static FilePathResult GetPingFilePath(std::wstring& uuid) {
|
||||
wchar_t* rawAppDataPath;
|
||||
HRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr,
|
||||
|
|
|
@ -35,3 +35,24 @@ ULONGLONG SecondsPassedSince(ULONGLONG initialTime,
|
|||
/ 1000 // To milliseconds
|
||||
/ 1000; // To seconds
|
||||
}
|
||||
|
||||
FilePathResult GenerateUUIDStr() {
|
||||
UUID uuid;
|
||||
RPC_STATUS status = UuidCreate(&uuid);
|
||||
if (status != RPC_S_OK) {
|
||||
HRESULT hr = MAKE_HRESULT(1, FACILITY_RPC, status);
|
||||
LOG_ERROR(hr);
|
||||
return FilePathResult(mozilla::WindowsError::FromHResult(hr));
|
||||
}
|
||||
|
||||
// 39 == length of a UUID string including braces and NUL.
|
||||
wchar_t guidBuf[39] = {};
|
||||
if (StringFromGUID2(uuid, guidBuf, 39) != 39) {
|
||||
LOG_ERROR(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
|
||||
return FilePathResult(
|
||||
mozilla::WindowsError::FromWin32Error(ERROR_INSUFFICIENT_BUFFER));
|
||||
}
|
||||
|
||||
// Remove the curly braces.
|
||||
return std::wstring(guidBuf + 1, guidBuf + 37);
|
||||
}
|
||||
|
|
|
@ -15,4 +15,7 @@ ULONGLONG GetCurrentTimestamp();
|
|||
// to get the current time rather than using a passed value.
|
||||
ULONGLONG SecondsPassedSince(ULONGLONG initialTime, ULONGLONG currentTime = 0);
|
||||
|
||||
using FilePathResult = mozilla::WindowsErrorResult<std::wstring>;
|
||||
FilePathResult GenerateUUIDStr();
|
||||
|
||||
#endif // __DEFAULT_BROWSER_AGENT_COMMON_H__
|
||||
|
|
|
@ -69,6 +69,9 @@ DEFINES["IMPL_MFBT"] = True
|
|||
DEFINES["UNICODE"] = True
|
||||
DEFINES["_UNICODE"] = True
|
||||
|
||||
# If defines are added to this list that are required by the Cache or its
|
||||
# dependencies (Registry, EventLog, common), tests/gtest/moz.build will need to
|
||||
# be updated as well.
|
||||
for var in ("MOZ_APP_BASENAME", "MOZ_APP_DISPLAYNAME", "MOZ_APP_VENDOR"):
|
||||
DEFINES[var] = '"%s"' % CONFIG[var]
|
||||
|
||||
|
@ -92,5 +95,8 @@ defaultagentini.inputs = [
|
|||
]
|
||||
FINAL_TARGET_FILES += ["!defaultagent.ini"]
|
||||
|
||||
if CONFIG["ENABLE_TESTS"]:
|
||||
DIRS += ["tests/gtest"]
|
||||
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Firefox", "Installer")
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Cache.h"
|
||||
#include "common.h"
|
||||
#include "Registry.h"
|
||||
|
||||
#include "mozilla/Result.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WinHeaderOnlyUtils.h"
|
||||
|
||||
FilePathResult ToWideString(const char* narrowString) {
|
||||
int wideLen = MultiByteToWideChar(CP_UTF8, 0, narrowString, -1, nullptr, 0);
|
||||
if (wideLen == 0) {
|
||||
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
LOG_ERROR(hr);
|
||||
return mozilla::Err(mozilla::WindowsError::FromHResult(hr));
|
||||
}
|
||||
mozilla::UniquePtr<wchar_t[]> wideValue =
|
||||
mozilla::MakeUnique<wchar_t[]>(wideLen);
|
||||
int charsWritten = MultiByteToWideChar(CP_UTF8, 0, narrowString, -1,
|
||||
wideValue.get(), wideLen);
|
||||
if (charsWritten == 0) {
|
||||
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
LOG_ERROR(hr);
|
||||
return mozilla::Err(mozilla::WindowsError::FromHResult(hr));
|
||||
}
|
||||
return std::wstring(wideValue.get());
|
||||
}
|
||||
|
||||
class WDBACacheTest : public ::testing::Test {
|
||||
protected:
|
||||
std::wstring mCacheRegKey;
|
||||
|
||||
void SetUp() override {
|
||||
// Create a unique registry key to put the cache in for each test.
|
||||
const ::testing::TestInfo* const testInfo =
|
||||
::testing::UnitTest::GetInstance()->current_test_info();
|
||||
FilePathResult testCaseResult = ToWideString(testInfo->test_case_name());
|
||||
ASSERT_TRUE(testCaseResult.isOk());
|
||||
mCacheRegKey = testCaseResult.unwrap();
|
||||
|
||||
FilePathResult testNameResult = ToWideString(testInfo->name());
|
||||
ASSERT_TRUE(testNameResult.isOk());
|
||||
mCacheRegKey += L'.';
|
||||
mCacheRegKey += testNameResult.unwrap();
|
||||
|
||||
FilePathResult uuidResult = GenerateUUIDStr();
|
||||
ASSERT_TRUE(uuidResult.isOk());
|
||||
mCacheRegKey += L'.';
|
||||
mCacheRegKey += uuidResult.unwrap();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// It seems like the TearDown probably doesn't run if SetUp doesn't
|
||||
// succeed, but I can't find any documentation saying that. And we don't
|
||||
// want to accidentally clobber the entirety of AGENT_REGKEY_NAME.
|
||||
if (!mCacheRegKey.empty()) {
|
||||
std::wstring regKey = AGENT_REGKEY_NAME;
|
||||
regKey += L'\\';
|
||||
regKey += mCacheRegKey;
|
||||
RegDeleteTreeW(HKEY_CURRENT_USER, regKey.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(WDBACacheTest, BasicFunctionality) {
|
||||
Cache cache(mCacheRegKey.c_str());
|
||||
VoidResult result = cache.Init();
|
||||
ASSERT_TRUE(result.isOk());
|
||||
|
||||
// Test that the cache starts empty
|
||||
Cache::MaybeEntryResult entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
Cache::MaybeEntry entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isNothing());
|
||||
|
||||
// Test that the cache stops accepting items when it is full.
|
||||
ASSERT_EQ(Cache::kDefaultCapacity, 2);
|
||||
Cache::Entry toWrite = Cache::Entry{
|
||||
.notificationType = "string1",
|
||||
.notificationShown = "string2",
|
||||
.notificationAction = "string3",
|
||||
.prevNotificationAction = "string4",
|
||||
};
|
||||
result = cache.Enqueue(toWrite);
|
||||
ASSERT_TRUE(result.isOk());
|
||||
toWrite = Cache::Entry{
|
||||
.notificationType = "string5",
|
||||
.notificationShown = "string6",
|
||||
.notificationAction = "string7",
|
||||
.prevNotificationAction = "string8",
|
||||
};
|
||||
result = cache.Enqueue(toWrite);
|
||||
ASSERT_TRUE(result.isOk());
|
||||
toWrite = Cache::Entry{
|
||||
.notificationType = "string9",
|
||||
.notificationShown = "string10",
|
||||
.notificationAction = "string11",
|
||||
.prevNotificationAction = "string12",
|
||||
};
|
||||
result = cache.Enqueue(toWrite);
|
||||
ASSERT_TRUE(result.isErr());
|
||||
|
||||
// Read the two cache entries back out and test that they match the expected
|
||||
// values.
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isSome());
|
||||
ASSERT_EQ(entry.value().entryVersion, 2);
|
||||
ASSERT_EQ(entry.value().notificationType, "string1");
|
||||
ASSERT_EQ(entry.value().notificationShown, "string2");
|
||||
ASSERT_EQ(entry.value().notificationAction, "string3");
|
||||
ASSERT_TRUE(entry.value().prevNotificationAction.isSome());
|
||||
ASSERT_EQ(entry.value().prevNotificationAction.value(), "string4");
|
||||
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isSome());
|
||||
ASSERT_EQ(entry.value().entryVersion, 2);
|
||||
ASSERT_EQ(entry.value().notificationType, "string5");
|
||||
ASSERT_EQ(entry.value().notificationShown, "string6");
|
||||
ASSERT_EQ(entry.value().notificationAction, "string7");
|
||||
ASSERT_TRUE(entry.value().prevNotificationAction.isSome());
|
||||
ASSERT_EQ(entry.value().prevNotificationAction.value(), "string8");
|
||||
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isNothing());
|
||||
}
|
||||
|
||||
TEST_F(WDBACacheTest, Version1Migration) {
|
||||
// Set up 2 version 1 cache entries
|
||||
VoidResult result = RegistrySetValueString(
|
||||
IsPrefixed::Unprefixed, L"PingCacheNotificationType0", "string1");
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
L"PingCacheNotificationShown0", "string2");
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
L"PingCacheNotificationAction0", "string3");
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
L"PingCacheNotificationType1", "string4");
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
L"PingCacheNotificationShown1", "string5");
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
L"PingCacheNotificationAction1", "string6");
|
||||
ASSERT_TRUE(result.isOk());
|
||||
|
||||
Cache cache(mCacheRegKey.c_str());
|
||||
result = cache.Init();
|
||||
ASSERT_TRUE(result.isOk());
|
||||
|
||||
Cache::MaybeEntryResult entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
Cache::MaybeEntry entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isSome());
|
||||
ASSERT_EQ(entry.value().entryVersion, 1);
|
||||
ASSERT_EQ(entry.value().notificationType, "string1");
|
||||
ASSERT_EQ(entry.value().notificationShown, "string2");
|
||||
ASSERT_EQ(entry.value().notificationAction, "string3");
|
||||
ASSERT_TRUE(entry.value().prevNotificationAction.isNothing());
|
||||
|
||||
// Insert a new item to test coexistence of different versions
|
||||
Cache::Entry toWrite = Cache::Entry{
|
||||
.notificationType = "string7",
|
||||
.notificationShown = "string8",
|
||||
.notificationAction = "string9",
|
||||
.prevNotificationAction = "string10",
|
||||
};
|
||||
result = cache.Enqueue(toWrite);
|
||||
ASSERT_TRUE(result.isOk());
|
||||
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isSome());
|
||||
ASSERT_EQ(entry.value().entryVersion, 1);
|
||||
ASSERT_EQ(entry.value().notificationType, "string4");
|
||||
ASSERT_EQ(entry.value().notificationShown, "string5");
|
||||
ASSERT_EQ(entry.value().notificationAction, "string6");
|
||||
ASSERT_TRUE(entry.value().prevNotificationAction.isNothing());
|
||||
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isSome());
|
||||
ASSERT_EQ(entry.value().entryVersion, 2);
|
||||
ASSERT_EQ(entry.value().notificationType, "string7");
|
||||
ASSERT_EQ(entry.value().notificationShown, "string8");
|
||||
ASSERT_EQ(entry.value().notificationAction, "string9");
|
||||
ASSERT_TRUE(entry.value().prevNotificationAction.isSome());
|
||||
ASSERT_EQ(entry.value().prevNotificationAction.value(), "string10");
|
||||
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isNothing());
|
||||
}
|
||||
|
||||
TEST_F(WDBACacheTest, ForwardsCompatibility) {
|
||||
// Set up a cache that might have been made by a future version with a larger
|
||||
// capacity set and more keys per entry.
|
||||
std::wstring settingsKey = mCacheRegKey + L"\\version2";
|
||||
VoidResult result = RegistrySetValueDword(
|
||||
IsPrefixed::Unprefixed, Cache::kCapacityRegName, 8, settingsKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
// We're going to insert the future version's entry at index 6 so there's
|
||||
// space for 1 more before we loop back to index 0. Then we are going to
|
||||
// enqueue 2 new values to test that this works properly.
|
||||
result = RegistrySetValueDword(IsPrefixed::Unprefixed, Cache::kFrontRegName,
|
||||
6, settingsKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueDword(IsPrefixed::Unprefixed, Cache::kSizeRegName, 1,
|
||||
settingsKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
|
||||
// Insert an entry as if it was inserted by a future version
|
||||
std::wstring entryRegKey = settingsKey + L"\\6";
|
||||
result =
|
||||
RegistrySetValueDword(IsPrefixed::Unprefixed, Cache::kEntryVersionKey,
|
||||
9999, entryRegKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
Cache::kNotificationTypeKey, "string1",
|
||||
entryRegKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
Cache::kNotificationShownKey, "string2",
|
||||
entryRegKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
Cache::kNotificationActionKey, "string3",
|
||||
entryRegKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed,
|
||||
Cache::kPrevNotificationActionKey, "string4",
|
||||
entryRegKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
result = RegistrySetValueString(IsPrefixed::Unprefixed, L"UnknownFutureKey",
|
||||
"string5", entryRegKey.c_str());
|
||||
ASSERT_TRUE(result.isOk());
|
||||
|
||||
Cache cache(mCacheRegKey.c_str());
|
||||
result = cache.Init();
|
||||
ASSERT_TRUE(result.isOk());
|
||||
|
||||
// Insert 2 new items to test that these features work with a different
|
||||
// capacity.
|
||||
Cache::Entry toWrite = Cache::Entry{
|
||||
.notificationType = "string6",
|
||||
.notificationShown = "string7",
|
||||
.notificationAction = "string8",
|
||||
.prevNotificationAction = "string9",
|
||||
};
|
||||
result = cache.Enqueue(toWrite);
|
||||
ASSERT_TRUE(result.isOk());
|
||||
toWrite = Cache::Entry{
|
||||
.notificationType = "string10",
|
||||
.notificationShown = "string11",
|
||||
.notificationAction = "string12",
|
||||
.prevNotificationAction = "string13",
|
||||
};
|
||||
result = cache.Enqueue(toWrite);
|
||||
ASSERT_TRUE(result.isOk());
|
||||
|
||||
// Read cache and verify the output
|
||||
Cache::MaybeEntryResult entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
Cache::MaybeEntry entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isSome());
|
||||
ASSERT_EQ(entry.value().entryVersion, 9999);
|
||||
ASSERT_EQ(entry.value().notificationType, "string1");
|
||||
ASSERT_EQ(entry.value().notificationShown, "string2");
|
||||
ASSERT_EQ(entry.value().notificationAction, "string3");
|
||||
ASSERT_TRUE(entry.value().prevNotificationAction.isSome());
|
||||
ASSERT_EQ(entry.value().prevNotificationAction.value(), "string4");
|
||||
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isSome());
|
||||
ASSERT_EQ(entry.value().entryVersion, 2);
|
||||
ASSERT_EQ(entry.value().notificationType, "string6");
|
||||
ASSERT_EQ(entry.value().notificationShown, "string7");
|
||||
ASSERT_EQ(entry.value().notificationAction, "string8");
|
||||
ASSERT_TRUE(entry.value().prevNotificationAction.isSome());
|
||||
ASSERT_EQ(entry.value().prevNotificationAction.value(), "string9");
|
||||
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isSome());
|
||||
ASSERT_EQ(entry.value().entryVersion, 2);
|
||||
ASSERT_EQ(entry.value().notificationType, "string10");
|
||||
ASSERT_EQ(entry.value().notificationShown, "string11");
|
||||
ASSERT_EQ(entry.value().notificationAction, "string12");
|
||||
ASSERT_TRUE(entry.value().prevNotificationAction.isSome());
|
||||
ASSERT_EQ(entry.value().prevNotificationAction.value(), "string13");
|
||||
|
||||
entryResult = cache.Dequeue();
|
||||
ASSERT_TRUE(entryResult.isOk());
|
||||
entry = entryResult.unwrap();
|
||||
ASSERT_TRUE(entry.isNothing());
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
Library("DefaultAgentTest")
|
||||
|
||||
# Normally, we only include the test code in gtest sources because they get
|
||||
# linked against libxul. But the code we are testing is not part of libxul, so
|
||||
# if we want it to be available to us to test, we have to include it here.
|
||||
UNIFIED_SOURCES += [
|
||||
"../../Cache.cpp",
|
||||
"../../common.cpp",
|
||||
"../../EventLog.cpp",
|
||||
"../../Registry.cpp",
|
||||
"CacheTest.cpp",
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
"/toolkit/mozapps/defaultagent",
|
||||
]
|
||||
|
||||
OS_LIBS += [
|
||||
"advapi32",
|
||||
"kernel32",
|
||||
"rpcrt4",
|
||||
]
|
||||
|
||||
DEFINES["UNICODE"] = True
|
||||
DEFINES["_UNICODE"] = True
|
||||
|
||||
for var in ("MOZ_APP_BASENAME", "MOZ_APP_DISPLAYNAME", "MOZ_APP_VENDOR"):
|
||||
DEFINES[var] = '"%s"' % CONFIG[var]
|
||||
|
||||
# We need STL headers that aren't allowed when wrapping is on (at least
|
||||
# <filesystem>, and possibly others).
|
||||
DisableStlWrapping()
|
||||
|
||||
FINAL_LIBRARY = "xul-gtest"
|
Загрузка…
Ссылка в новой задаче