summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/defaultagent/tests/gtest/CacheTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/defaultagent/tests/gtest/CacheTest.cpp')
-rw-r--r--toolkit/mozapps/defaultagent/tests/gtest/CacheTest.cpp299
1 files changed, 299 insertions, 0 deletions
diff --git a/toolkit/mozapps/defaultagent/tests/gtest/CacheTest.cpp b/toolkit/mozapps/defaultagent/tests/gtest/CacheTest.cpp
new file mode 100644
index 0000000000..a5a618ff23
--- /dev/null
+++ b/toolkit/mozapps/defaultagent/tests/gtest/CacheTest.cpp
@@ -0,0 +1,299 @@
+/* -*- 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 "UtfConvert.h"
+
+#include "mozilla/Result.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+
+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();
+ Utf8ToUtf16Result testCaseResult = Utf8ToUtf16(testInfo->test_case_name());
+ ASSERT_TRUE(testCaseResult.isOk());
+ mCacheRegKey = testCaseResult.unwrap();
+
+ Utf8ToUtf16Result testNameResult = Utf8ToUtf16(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, 2U);
+ 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, 2U);
+ 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, 2U);
+ 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, 1U);
+ 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, 1U);
+ 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, 2U);
+ 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, 9999U);
+ 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, 2U);
+ 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, 2U);
+ 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());
+}