diff options
Diffstat (limited to 'toolkit/xre/test/gtest')
9 files changed, 723 insertions, 0 deletions
diff --git a/toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp b/toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp new file mode 100644 index 0000000000..cba65ae71a --- /dev/null +++ b/toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "mozilla/AssembleCmdLine.h" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/UniquePtrExtensions.h" +#include "WinRemoteMessage.h" + +using namespace mozilla; + +template <typename T> +struct TestCase { + const T* mArgs[4]; + const wchar_t* mExpected; +}; + +#define ALPHA_IN_UTF8 "\xe3\x82\xa2\xe3\x83\xab\xe3\x83\x95\xe3\x82\xa1" +#define OMEGA_IN_UTF8 "\xe3\x82\xaa\xe3\x83\xa1\xe3\x82\xac" +#define ALPHA_IN_UTF16 L"\u30A2\u30EB\u30D5\u30A1" +#define OMEGA_IN_UTF16 L"\u30AA\u30E1\u30AC" +#define UPPER_CYRILLIC_P_IN_UTF8 "\xd0\xa0" +#define LOWER_CYRILLIC_P_IN_UTF8 "\xd1\x80" +#define UPPER_CYRILLIC_P_IN_UTF16 L"\u0420" +#define LOWER_CYRILLIC_P_IN_UTF16 L"\u0440" + +TestCase<char> testCases[] = { + // Copied from TestXREMakeCommandLineWin.ini + {{"a:\\", nullptr}, L"a:\\"}, + {{"a:\"", nullptr}, L"a:\\\""}, + {{"a:\\b c", nullptr}, L"\"a:\\b c\""}, + {{"a:\\b c\"", nullptr}, L"\"a:\\b c\\\"\""}, + {{"a:\\b c\\d e", nullptr}, L"\"a:\\b c\\d e\""}, + {{"a:\\b c\\d e\"", nullptr}, L"\"a:\\b c\\d e\\\"\""}, + {{"a:\\", nullptr}, L"a:\\"}, + {{"a:\"", "b:\\c d", nullptr}, L"a:\\\" \"b:\\c d\""}, + {{"a", "b:\" c:\\d", "e", nullptr}, L"a \"b:\\\" c:\\d\" e"}, + {{"abc", "d", "e", nullptr}, L"abc d e"}, + {{"a b c", "d", "e", nullptr}, L"\"a b c\" d e"}, + {{"a\\\\\\b", "de fg", "h", nullptr}, L"a\\\\\\b \"de fg\" h"}, + {{"a", "b", nullptr}, L"a b"}, + {{"a\tb", nullptr}, L"\"a\tb\""}, + {{"a\\\"b", "c", "d", nullptr}, L"a\\\\\\\"b c d"}, + {{"a\\\"b", "c", nullptr}, L"a\\\\\\\"b c"}, + {{"a\\\\\\b c", nullptr}, L"\"a\\\\\\b c\""}, + {{"\"a", nullptr}, L"\\\"a"}, + {{"\\a", nullptr}, L"\\a"}, + {{"\\\\\\a", nullptr}, L"\\\\\\a"}, + {{"\\\\\\\"a", nullptr}, L"\\\\\\\\\\\\\\\"a"}, + {{"a\\\"b c\" d e", nullptr}, L"\"a\\\\\\\"b c\\\" d e\""}, + {{"a\\\\\"b", "c d e", nullptr}, L"a\\\\\\\\\\\"b \"c d e\""}, + {{"a:\\b", "c\\" ALPHA_IN_UTF8, OMEGA_IN_UTF8 "\\d", nullptr}, + L"a:\\b c\\" ALPHA_IN_UTF16 L" " OMEGA_IN_UTF16 L"\\d"}, + {{"a:\\b", "c\\" ALPHA_IN_UTF8 " " OMEGA_IN_UTF8 "\\d", nullptr}, + L"a:\\b \"c\\" ALPHA_IN_UTF16 L" " OMEGA_IN_UTF16 L"\\d\""}, + {{ALPHA_IN_UTF8, OMEGA_IN_UTF8, nullptr}, + ALPHA_IN_UTF16 L" " OMEGA_IN_UTF16}, + + // More single-argument cases + {{"", nullptr}, L""}, + {{"a\fb", nullptr}, L"\"a\fb\""}, + {{"a\nb", nullptr}, L"\"a\nb\""}, + {{"a\rb", nullptr}, L"\"a\rb\""}, + {{"a\vb", nullptr}, L"\"a\vb\""}, + {{"\"a\" \"b\"", nullptr}, L"\"\\\"a\\\" \\\"b\\\"\""}, + {{"\"a\\b\" \"c\\d\"", nullptr}, L"\"\\\"a\\b\\\" \\\"c\\d\\\"\""}, + {{"\\\\ \\\\", nullptr}, L"\"\\\\ \\\\\\\\\""}, + {{"\"\" \"\"", nullptr}, L"\"\\\"\\\" \\\"\\\"\""}, + {{ALPHA_IN_UTF8 "\\" OMEGA_IN_UTF8, nullptr}, + ALPHA_IN_UTF16 L"\\" OMEGA_IN_UTF16}, + {{ALPHA_IN_UTF8 " " OMEGA_IN_UTF8, nullptr}, + L"\"" ALPHA_IN_UTF16 L" " OMEGA_IN_UTF16 L"\""}, +}; + +TEST(AssembleCommandLineWin, assembleCmdLine) +{ + for (const auto& testCase : testCases) { + UniqueFreePtr<wchar_t> assembled; + wchar_t* assembledRaw = nullptr; + EXPECT_EQ(assembleCmdLine(testCase.mArgs, &assembledRaw, CP_UTF8), 0); + assembled.reset(assembledRaw); + + EXPECT_STREQ(assembled.get(), testCase.mExpected); + } +} + +TEST(CommandLineParserWin, HandleCommandLine) +{ + CommandLineParserWin<char> parser; + for (const auto& testCase : testCases) { + NS_ConvertUTF16toUTF8 utf8(testCase.mExpected); + parser.HandleCommandLine(utf8.get()); + + if (utf8.Length() == 0) { + EXPECT_EQ(parser.Argc(), 0); + continue; + } + + for (int i = 0; i < parser.Argc(); ++i) { + EXPECT_NE(testCase.mArgs[i], nullptr); + EXPECT_STREQ(parser.Argv()[i], testCase.mArgs[i]); + } + EXPECT_EQ(testCase.mArgs[parser.Argc()], nullptr); + } +} + +TEST(WinRemoteMessage, SendReceive) +{ + const char kCommandline[] = + "dummy.exe /arg1 --arg2 \"3rd arg\" " + "4th=\"" UPPER_CYRILLIC_P_IN_UTF8 " " LOWER_CYRILLIC_P_IN_UTF8 "\""; + const wchar_t kCommandlineW[] = + L"dummy.exe /arg1 --arg2 \"3rd arg\" " + L"4th=\"" UPPER_CYRILLIC_P_IN_UTF16 L" " LOWER_CYRILLIC_P_IN_UTF16 L"\""; + const wchar_t* kExpectedArgsW[] = { + L"-arg1", L"-arg2", L"3rd arg", + L"4th=" UPPER_CYRILLIC_P_IN_UTF16 L" " LOWER_CYRILLIC_P_IN_UTF16}; + + char workingDirA[MAX_PATH]; + wchar_t workingDirW[MAX_PATH]; + EXPECT_NE(getcwd(workingDirA, MAX_PATH), nullptr); + EXPECT_NE(_wgetcwd(workingDirW, MAX_PATH), nullptr); + + WinRemoteMessageSender v0(kCommandline); + WinRemoteMessageSender v1(kCommandline, workingDirA); + WinRemoteMessageSender v2(kCommandlineW, workingDirW); + + WinRemoteMessageReceiver receiver; + int32_t len; + nsAutoString arg; + nsCOMPtr<nsIFile> workingDir; + + receiver.Parse(v0.CopyData()); + EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len))); + EXPECT_EQ(len, ArrayLength(kExpectedArgsW)); + for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) { + EXPECT_TRUE( + NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg))); + EXPECT_STREQ(arg.get(), kExpectedArgsW[i]); + } + EXPECT_EQ(receiver.CommandLineRunner()->GetWorkingDirectory( + getter_AddRefs(workingDir)), + NS_ERROR_NOT_INITIALIZED); + + receiver.Parse(v1.CopyData()); + EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len))); + EXPECT_EQ(len, ArrayLength(kExpectedArgsW)); + for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) { + EXPECT_TRUE( + NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg))); + EXPECT_STREQ(arg.get(), kExpectedArgsW[i]); + } + EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetWorkingDirectory( + getter_AddRefs(workingDir)))); + EXPECT_TRUE(NS_SUCCEEDED(workingDir->GetPath(arg))); + EXPECT_STREQ(arg.get(), workingDirW); + + receiver.Parse(v2.CopyData()); + EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len))); + EXPECT_EQ(len, ArrayLength(kExpectedArgsW)); + for (int i = 0; i < ArrayLength(kExpectedArgsW); ++i) { + EXPECT_TRUE( + NS_SUCCEEDED(receiver.CommandLineRunner()->GetArgument(i, arg))); + EXPECT_STREQ(arg.get(), kExpectedArgsW[i]); + } + EXPECT_TRUE(NS_SUCCEEDED(receiver.CommandLineRunner()->GetWorkingDirectory( + getter_AddRefs(workingDir)))); + EXPECT_TRUE(NS_SUCCEEDED(workingDir->GetPath(arg))); + EXPECT_STREQ(arg.get(), workingDirW); +} diff --git a/toolkit/xre/test/gtest/TestCompatVersionCompare.cpp b/toolkit/xre/test/gtest/TestCompatVersionCompare.cpp new file mode 100644 index 0000000000..bce64aacd5 --- /dev/null +++ b/toolkit/xre/test/gtest/TestCompatVersionCompare.cpp @@ -0,0 +1,53 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include "gtest/gtest.h" +#include "nsAppRunner.h" +#include "nsString.h" + +void CheckCompatVersionCompare(const nsCString& aOldCompatVersion, + const nsCString& aNewCompatVersion, + bool aExpectedSame, bool aExpectedDowngrade) { + printf("Comparing '%s' to '%s'.\n", aOldCompatVersion.get(), + aNewCompatVersion.get()); + + int32_t result = CompareCompatVersions(aOldCompatVersion, aNewCompatVersion); + + ASSERT_EQ(aExpectedSame, result == 0) + << "Version sameness check should match."; + ASSERT_EQ(aExpectedDowngrade, result > 0) + << "Version downgrade check should match."; +} + +void CheckExpectedResult(const char* aOldAppVersion, const char* aNewAppVersion, + bool aExpectedSame, bool aExpectedDowngrade) { + nsCString oldCompatVersion; + BuildCompatVersion(aOldAppVersion, "", "", oldCompatVersion); + + nsCString newCompatVersion; + BuildCompatVersion(aNewAppVersion, "", "", newCompatVersion); + + CheckCompatVersionCompare(oldCompatVersion, newCompatVersion, aExpectedSame, + aExpectedDowngrade); +} + +TEST(CompatVersionCompare, CompareVersionChange) +{ + // Identical + CheckExpectedResult("67.0", "67.0", true, false); + + // Version changes + CheckExpectedResult("67.0", "68.0", false, false); + CheckExpectedResult("68.0", "67.0", false, true); + CheckExpectedResult("67.0", "67.0.1", true, false); + CheckExpectedResult("67.0.1", "67.0", true, false); + CheckExpectedResult("67.0.1", "67.0.1", true, false); + CheckExpectedResult("67.0.1", "67.0.2", true, false); + CheckExpectedResult("67.0.2", "67.0.1", true, false); + + // Check that if the last run was safe mode then we consider this an upgrade. + CheckCompatVersionCompare( + "Safe Mode"_ns, "67.0.1_20000000000000/20000000000000"_ns, false, false); +} diff --git a/toolkit/xre/test/gtest/TestUntrustedModules.cpp b/toolkit/xre/test/gtest/TestUntrustedModules.cpp new file mode 100644 index 0000000000..661b6919bf --- /dev/null +++ b/toolkit/xre/test/gtest/TestUntrustedModules.cpp @@ -0,0 +1,382 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include "gtest/gtest.h" + +#include "js/RegExp.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "mozilla/UntrustedModulesProcessor.h" +#include "mozilla/WinDllServices.h" +#include "nsContentUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "TelemetryFixture.h" +#include "UntrustedModulesBackupService.h" +#include "UntrustedModulesDataSerializer.h" + +class ModuleLoadCounter final { + nsDataHashtable<nsStringCaseInsensitiveHashKey, int> mCounters; + + public: + template <int N> + ModuleLoadCounter(const nsString (&aNames)[N], const int (&aCounts)[N]) + : mCounters(N) { + for (int i = 0; i < N; ++i) { + mCounters.Put(aNames[i], aCounts[i]); + } + } + + template <int N> + bool Remains(const nsString (&aNames)[N], const int (&aCounts)[N]) { + EXPECT_EQ(mCounters.Count(), N); + if (mCounters.Count() != N) { + return false; + } + + bool result = true; + for (int i = 0; i < N; ++i) { + int* entry = mCounters.GetValue(aNames[i]); + if (!entry) { + wprintf(L"%s is not registered.\n", aNames[i].get()); + result = false; + } else if (*entry != aCounts[i]) { + // We can return false, but let's print out all unmet modules + // which may be helpful to investigate test failures. + wprintf(L"%s:%4d\n", aNames[i].get(), *entry); + result = false; + } + } + return result; + } + + bool IsDone() const { + bool allZero = true; + for (auto iter = mCounters.ConstIter(); !iter.Done(); iter.Next()) { + if (iter.Data() < 0) { + // If any counter is negative, we know the test fails. + // No need to continue. + return true; + } + if (iter.Data() > 0) { + allZero = false; + } + } + // If all counters are zero, the test finished nicely. Otherwise, those + // counters are expected to be decremented later. Let's continue. + return allZero; + } + + void Decrement(const nsString& aName) { + if (int* entry = mCounters.GetValue(aName)) { + --(*entry); + } + } +}; + +class UntrustedModulesCollector { + static constexpr int kMaximumPendingQueries = 200; + Vector<UntrustedModulesData> mData; + + public: + Vector<UntrustedModulesData>& Data() { return mData; } + + nsresult Collect(ModuleLoadCounter& aChecker) { + nsresult rv = NS_OK; + + mData.clear(); + int pendingQueries = 0; + + EXPECT_TRUE(SpinEventLoopUntil([this, &pendingQueries, &aChecker, &rv]() { + // Some of expected loaded modules are still missing + // after kMaximumPendingQueries queries were submitted. + // Giving up here to avoid an infinite loop. + if (pendingQueries >= kMaximumPendingQueries) { + rv = NS_ERROR_ABORT; + return true; + } + + ++pendingQueries; + + RefPtr<DllServices> dllSvc(DllServices::Get()); + dllSvc->GetUntrustedModulesData()->Then( + GetMainThreadSerialEventTarget(), __func__, + [this, &pendingQueries, + &aChecker](Maybe<UntrustedModulesData>&& aResult) { + EXPECT_GT(pendingQueries, 0); + --pendingQueries; + + if (aResult.isSome()) { + wprintf(L"Received data. (pendingQueries=%d)\n", pendingQueries); + for (const auto& evt : aResult.ref().mEvents) { + aChecker.Decrement(evt.mRequestedDllName); + } + EXPECT_TRUE(mData.emplaceBack(std::move(aResult.ref()))); + } + }, + [&pendingQueries, &rv](nsresult aReason) { + EXPECT_GT(pendingQueries, 0); + --pendingQueries; + + wprintf(L"GetUntrustedModulesData() failed - %08x\n", aReason); + EXPECT_TRUE(false); + rv = aReason; + }); + + // Keep calling GetUntrustedModulesData() until we meet the condition. + return aChecker.IsDone(); + })); + + EXPECT_TRUE(SpinEventLoopUntil( + [&pendingQueries]() { return pendingQueries <= 0; })); + + return rv; + } +}; + +static void ValidateUntrustedModules(const UntrustedModulesData& aData) { + EXPECT_EQ(aData.mProcessType, GeckoProcessType_Default); + EXPECT_EQ(aData.mPid, ::GetCurrentProcessId()); + + nsTHashtable<nsPtrHashKey<void>> moduleSet; + for (auto iter = aData.mModules.ConstIter(); !iter.Done(); iter.Next()) { + const RefPtr<ModuleRecord>& module = iter.Data(); + moduleSet.PutEntry(module); + } + + for (const auto& evt : aData.mEvents) { + EXPECT_EQ(evt.mThreadId, ::GetCurrentThreadId()); + // Make sure mModule is pointing to an entry of mModules. + EXPECT_TRUE(moduleSet.Contains(evt.mModule)); + EXPECT_FALSE(evt.mIsDependent); + EXPECT_EQ(evt.mLoadStatus, 0); + } + + // No check for the mXULLoadDurationMS field because the field has a value + // in CCov build GTest, but it is empty in non-CCov build (bug 1681936). + EXPECT_GT(aData.mEvents.length(), 0); + EXPECT_GT(aData.mStacks.GetModuleCount(), 0); + EXPECT_EQ(aData.mSanitizationFailures, 0); + EXPECT_EQ(aData.mTrustTestFailures, 0); +} + +class UntrustedModulesFixture : public TelemetryTestFixture { + static constexpr int kLoadCountBeforeDllServices = 5; + static constexpr int kLoadCountAfterDllServices = 5; + static constexpr uint32_t kMaxModulesArrayLen = 10; + + // One of the important test scenarios is to load modules before DllServices + // is initialized and to make sure those loading events are forwarded when + // DllServices is initialized. + // However, GTest instantiates a Fixture class every testcase and there is + // no way to re-enable DllServices and UntrustedModulesProcessor once it's + // disabled, which means no matter how many testcases we have, only the + // first testcase exercises that scenario. That's why we implement that + // test scenario in InitialModuleLoadOnce as a static member and runs it + // in the first testcase to be executed. + static INIT_ONCE sInitLoadOnce; + static UntrustedModulesCollector sInitLoadDataCollector; + + static nsString PrependWorkingDir(const nsAString& aLeaf) { + nsCOMPtr<nsIFile> file; + EXPECT_TRUE(NS_SUCCEEDED(NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, + getter_AddRefs(file)))); + EXPECT_TRUE(NS_SUCCEEDED(file->Append(aLeaf))); + bool exists; + EXPECT_TRUE(NS_SUCCEEDED(file->Exists(&exists)) && exists); + nsString fullPath; + EXPECT_TRUE(NS_SUCCEEDED(file->GetPath(fullPath))); + return fullPath; + } + + static BOOL CALLBACK InitialModuleLoadOnce(PINIT_ONCE, void*, void**); + + protected: + static constexpr int kInitLoadCount = + kLoadCountBeforeDllServices + kLoadCountAfterDllServices; + static const nsString kTestModules[]; + + static void LoadAndFree(const nsAString& aLeaf) { + nsModuleHandle dll(::LoadLibraryW(PrependWorkingDir(aLeaf).get())); + EXPECT_TRUE(!!dll); + } + + virtual void SetUp() override { + TelemetryTestFixture::SetUp(); + ::InitOnceExecuteOnce(&sInitLoadOnce, InitialModuleLoadOnce, nullptr, + nullptr); + } + + static const Vector<UntrustedModulesData>& GetInitLoadData() { + return sInitLoadDataCollector.Data(); + } + + // This method is useful if we want a new instance of UntrustedModulesData + // which is not copyable. + static UntrustedModulesData CollectSingleData() { + // If we call LoadAndFree more than once, those loading events are + // likely to be merged into an instance of UntrustedModulesData, + // meaning the length of the collector's vector is at least one but + // the exact number is unknown. + LoadAndFree(kTestModules[0]); + + UntrustedModulesCollector collector; + ModuleLoadCounter waitForOne({kTestModules[0]}, {1}); + EXPECT_TRUE(NS_SUCCEEDED(collector.Collect(waitForOne))); + EXPECT_TRUE(waitForOne.Remains({kTestModules[0]}, {0})); + EXPECT_EQ(collector.Data().length(), 1); + + // Cannot "return collector.Data()[0]" as copy ctor is deleted. + return UntrustedModulesData(std::move(collector.Data()[0])); + } + + template <typename DataFetcherT> + void ValidateJSValue(const char16_t* aPattern, size_t aPatternLength, + DataFetcherT&& aDataFetcher) { + AutoJSContextWithGlobal cx(mCleanGlobal); + mozilla::Telemetry::UntrustedModulesDataSerializer serializer( + cx.GetJSContext(), kMaxModulesArrayLen); + EXPECT_TRUE(!!serializer); + aDataFetcher(serializer); + + JS::RootedValue jsval(cx.GetJSContext()); + serializer.GetObject(&jsval); + + nsAutoString json; + EXPECT_TRUE(nsContentUtils::StringifyJSON(cx.GetJSContext(), &jsval, json)); + + JS::RootedObject re( + cx.GetJSContext(), + JS::NewUCRegExpObject(cx.GetJSContext(), aPattern, aPatternLength, + JS::RegExpFlag::Global)); + EXPECT_TRUE(!!re); + + JS::RootedValue matchResult(cx.GetJSContext(), JS::NullValue()); + size_t idx = 0; + EXPECT_TRUE(JS::ExecuteRegExpNoStatics(cx.GetJSContext(), re, json.get(), + json.Length(), &idx, true, + &matchResult)); + // On match, with aOnlyMatch = true, ExecuteRegExpNoStatics returns boolean + // true. If no match, ExecuteRegExpNoStatics returns Null. + EXPECT_TRUE(matchResult.isBoolean() && matchResult.toBoolean()); + if (!matchResult.toBoolean()) { + // If match failed, print out the actual JSON kindly. + wprintf(L"JSON: %s\n", json.get()); + wprintf(L"RE: %s\n", aPattern); + } + } +}; + +const nsString UntrustedModulesFixture::kTestModules[] = { + u"TestUntrustedModules_Dll1.dll"_ns, u"TestUntrustedModules_Dll2.dll"_ns}; +INIT_ONCE UntrustedModulesFixture::sInitLoadOnce = INIT_ONCE_STATIC_INIT; +UntrustedModulesCollector UntrustedModulesFixture::sInitLoadDataCollector; + +BOOL CALLBACK UntrustedModulesFixture::InitialModuleLoadOnce(PINIT_ONCE, void*, + void**) { + for (int i = 0; i < kLoadCountBeforeDllServices; ++i) { + for (const auto& mod : kTestModules) { + LoadAndFree(mod); + } + } + + RefPtr<DllServices> dllSvc(DllServices::Get()); + dllSvc->StartUntrustedModulesProcessor(); + + for (int i = 0; i < kLoadCountAfterDllServices; ++i) { + for (const auto& mod : kTestModules) { + LoadAndFree(mod); + } + } + + ModuleLoadCounter waitForTwo(kTestModules, {kInitLoadCount, kInitLoadCount}); + EXPECT_EQ(sInitLoadDataCollector.Collect(waitForTwo), NS_OK); + EXPECT_TRUE(waitForTwo.Remains(kTestModules, {0, 0})); + + for (const auto& event : GetInitLoadData()) { + ValidateUntrustedModules(event); + } + + // Data was removed when retrieved. No data is retrieved again. + UntrustedModulesCollector collector; + ModuleLoadCounter waitOnceForEach(kTestModules, {1, 1}); + EXPECT_EQ(collector.Collect(waitOnceForEach), NS_ERROR_ABORT); + EXPECT_TRUE(waitOnceForEach.Remains(kTestModules, {1, 1})); + + return TRUE; +} + +#define PROCESS_OBJ(TYPE, PID) \ + u"\"" TYPE u"\\." PID u"\":{" \ + u"\"processType\":\"" TYPE u"\",\"elapsed\":\\d+\\.\\d+," \ + u"\"sanitizationFailures\":0,\"trustTestFailures\":0," \ + u"\"events\":\\[{" \ + u"\"processUptimeMS\":\\d+,\"loadDurationMS\":\\d+\\.\\d+," \ + u"\"threadID\":\\d+,\"threadName\":\"Main Thread\"," \ + u"\"baseAddress\":\"0x[0-9a-f]+\",\"moduleIndex\":0," \ + u"\"isDependent\":false,\"loadStatus\":0}\\]," \ + u"\"combinedStacks\":{" \ + u"\"memoryMap\":\\[\\[\"\\w+\\.\\w+\",\"[0-9A-Z]+\"\\]" \ + u"(,\\[\"\\w+\\.\\w+\",\"[0-9A-Z]+\\\"\\])*\\]," \ + u"\"stacks\":\\[\\[\\[\\d+,\\d+\\]" \ + u"(,\\[\\d+,\\d+\\])*\\]\\]}}" + +TEST_F(UntrustedModulesFixture, Serialize) { + // clang-format off + const char16_t kPattern[] = u"{\"structVersion\":1," + u"\"modules\":\\[{" + u"\"resolvedDllName\":\"TestUntrustedModules_Dll1\\.dll\"," + u"\"fileVersion\":\"1\\.2\\.3\\.4\"," + u"\"companyName\":\"Mozilla Corporation\",\"trustFlags\":0}\\]," + u"\"processes\":{" + PROCESS_OBJ(u"browser", u"0xabc") u"," + PROCESS_OBJ(u"browser", u"0x4") u"," + PROCESS_OBJ(u"rdd", u"0x4") + u"}}"; + // clang-format on + + UntrustedModulesBackupData backup1, backup2; + { + UntrustedModulesData data1 = CollectSingleData(); + UntrustedModulesData data2 = CollectSingleData(); + UntrustedModulesData data3 = CollectSingleData(); + + data1.mPid = 0xabc; + data2.mPid = 0x4; + data2.mProcessType = GeckoProcessType_RDD; + data3.mPid = 0x4; + + backup1.Add(std::move(data1)); + backup2.Add(std::move(data2)); + backup1.Add(std::move(data3)); + } + + ValidateJSValue(kPattern, ArrayLength(kPattern) - 1, + [&backup1, &backup2]( + Telemetry::UntrustedModulesDataSerializer& aSerializer) { + EXPECT_TRUE(NS_SUCCEEDED(aSerializer.Add(backup1))); + EXPECT_TRUE(NS_SUCCEEDED(aSerializer.Add(backup2))); + }); +} + +TEST_F(UntrustedModulesFixture, Backup) { + using BackupType = UntrustedModulesBackupService::BackupType; + + RefPtr<UntrustedModulesBackupService> backupSvc( + UntrustedModulesBackupService::Get()); + for (int i = 0; i < 5; ++i) { + backupSvc->Backup(BackupType::Staging, CollectSingleData()); + } + + backupSvc->SettleAllStagingData(); + EXPECT_TRUE(backupSvc->Ref(BackupType::Staging).IsEmpty()); + + for (auto iter = backupSvc->Ref(BackupType::Settled).ConstIter(); + !iter.Done(); iter.Next()) { + const RefPtr<UntrustedModulesDataContainer>& container = iter.Data(); + EXPECT_TRUE(!!container); + const UntrustedModulesData& data = container->mData; + EXPECT_EQ(iter.Key(), ProcessHashKey(data.mProcessType, data.mPid)); + ValidateUntrustedModules(data); + } +} diff --git a/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.cpp b/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.cpp new file mode 100644 index 0000000000..4f6ce877eb --- /dev/null +++ b/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.cpp @@ -0,0 +1,7 @@ +/* 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 <windows.h> + +BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; } diff --git a/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.rc b/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.rc new file mode 100644 index 0000000000..2358b88b93 --- /dev/null +++ b/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.rc @@ -0,0 +1,38 @@ +/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,2,3,4 // This field will be collected
+ PRODUCTVERSION 5,6,7,8
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "Mozilla Corporation"
+ VALUE "OriginalFilename", "TestUntrustedModules_Dll1.dll"
+ VALUE "ProductName", "Test DLL"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/moz.build b/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/moz.build new file mode 100644 index 0000000000..57fc59ca8a --- /dev/null +++ b/toolkit/xre/test/gtest/TestUntrustedModules_Dll1/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# 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/. + +DIST_INSTALL = False + +SharedLibrary("TestUntrustedModules_Dll1") + +UNIFIED_SOURCES = [ + "TestUntrustedModules_Dll1.cpp", +] + +RCFILE = "TestUntrustedModules_Dll1.rc" + +if CONFIG["COMPILE_ENVIRONMENT"]: + TEST_HARNESS_FILES.gtest += ["!TestUntrustedModules_Dll1.dll"] diff --git a/toolkit/xre/test/gtest/TestUntrustedModules_Dll2/TestUntrustedModules_Dll2.cpp b/toolkit/xre/test/gtest/TestUntrustedModules_Dll2/TestUntrustedModules_Dll2.cpp new file mode 100644 index 0000000000..4f6ce877eb --- /dev/null +++ b/toolkit/xre/test/gtest/TestUntrustedModules_Dll2/TestUntrustedModules_Dll2.cpp @@ -0,0 +1,7 @@ +/* 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 <windows.h> + +BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; } diff --git a/toolkit/xre/test/gtest/TestUntrustedModules_Dll2/moz.build b/toolkit/xre/test/gtest/TestUntrustedModules_Dll2/moz.build new file mode 100644 index 0000000000..fcefe41329 --- /dev/null +++ b/toolkit/xre/test/gtest/TestUntrustedModules_Dll2/moz.build @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# 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/. + +DIST_INSTALL = False + +SharedLibrary("TestUntrustedModules_Dll2") + +UNIFIED_SOURCES = [ + "TestUntrustedModules_Dll2.cpp", +] + +if CONFIG["COMPILE_ENVIRONMENT"]: + TEST_HARNESS_FILES.gtest += ["!TestUntrustedModules_Dll2.dll"] diff --git a/toolkit/xre/test/gtest/moz.build b/toolkit/xre/test/gtest/moz.build new file mode 100644 index 0000000000..e98d2deea8 --- /dev/null +++ b/toolkit/xre/test/gtest/moz.build @@ -0,0 +1,31 @@ +# -*- 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 http://mozilla.org/MPL/2.0/. + +Library("xretest") + +UNIFIED_SOURCES = [ + "TestCompatVersionCompare.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +LOCAL_INCLUDES += [ + "/toolkit/components/remote", + "/toolkit/components/telemetry/other", + "/toolkit/components/telemetry/tests/gtest", +] + +if CONFIG["OS_TARGET"] == "WINNT": + UNIFIED_SOURCES += [ + "TestAssembleCommandLineWin.cpp", + "TestUntrustedModules.cpp", + ] + TEST_DIRS += [ + "TestUntrustedModules_Dll1", + "TestUntrustedModules_Dll2", + ] + +FINAL_LIBRARY = "xul-gtest" |