diff options
Diffstat (limited to 'toolkit/xre/test/win')
-rw-r--r-- | toolkit/xre/test/win/Makefile.in | 11 | ||||
-rw-r--r-- | toolkit/xre/test/win/TestLauncherRegistryInfo.cpp | 779 | ||||
-rw-r--r-- | toolkit/xre/test/win/TestXREMakeCommandLineWin.cpp | 266 | ||||
-rw-r--r-- | toolkit/xre/test/win/TestXREMakeCommandLineWin.ini | 94 | ||||
-rw-r--r-- | toolkit/xre/test/win/moz.build | 44 |
5 files changed, 1194 insertions, 0 deletions
diff --git a/toolkit/xre/test/win/Makefile.in b/toolkit/xre/test/win/Makefile.in new file mode 100644 index 0000000000..5a67b8eac8 --- /dev/null +++ b/toolkit/xre/test/win/Makefile.in @@ -0,0 +1,11 @@ +# 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/. + +MOZ_WINCONSOLE = 1 + +include $(topsrcdir)/config/rules.mk + +check:: + @echo 'Running TestXREMakeCommandLineWin tests' + @$(RUN_TEST_PROGRAM) $(FINAL_TARGET)/TestXREMakeCommandLineWin.exe diff --git a/toolkit/xre/test/win/TestLauncherRegistryInfo.cpp b/toolkit/xre/test/win/TestLauncherRegistryInfo.cpp new file mode 100644 index 0000000000..273c1cb72a --- /dev/null +++ b/toolkit/xre/test/win/TestLauncherRegistryInfo.cpp @@ -0,0 +1,779 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#define MOZ_USE_LAUNCHER_ERROR + +#include "mozilla/LauncherRegistryInfo.h" +#include "mozilla/NativeNt.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Unused.h" +#include "nsWindowsHelpers.h" + +#include "LauncherRegistryInfo.cpp" + +#include <string> + +static const char kMsgStart[] = "TEST-FAILED | LauncherRegistryInfo | "; + +static const wchar_t kRegKeyPath[] = L"SOFTWARE\\" EXPAND_STRING_MACRO( + MOZ_APP_VENDOR) L"\\" EXPAND_STRING_MACRO(MOZ_APP_BASENAME) L"\\Launcher"; +static const wchar_t kBrowserSuffix[] = L"|Browser"; +static const wchar_t kLauncherSuffix[] = L"|Launcher"; +static const wchar_t kImageSuffix[] = L"|Image"; +static const wchar_t kTelemetrySuffix[] = L"|Telemetry"; + +static std::wstring gBrowserValue; +static std::wstring gLauncherValue; +static std::wstring gImageValue; +static std::wstring gTelemetryValue; + +static DWORD gMyImageTimestamp; + +#define RUN_TEST(result, fn) \ + if ((result = fn()).isErr()) { \ + const mozilla::LauncherError& err = result.inspectErr(); \ + printf("%s%s | %08lx (%s:%d)\n", kMsgStart, #fn, err.mError.AsHResult(), \ + err.mFile, err.mLine); \ + return 1; \ + } + +#define EXPECT_COMMIT_IS_OK() \ + do { \ + mozilla::LauncherVoidResult vr2 = info.Commit(); \ + if (vr2.isErr()) { \ + return vr2; \ + } \ + } while (0) + +#define EXPECT_CHECK_RESULT_IS(desired, expected) \ + do { \ + mozilla::LauncherResult<mozilla::LauncherRegistryInfo::ProcessType> \ + result = info.Check(mozilla::LauncherRegistryInfo::desired); \ + if (result.isErr()) { \ + return result.propagateErr(); \ + } \ + if (result.unwrap() != mozilla::LauncherRegistryInfo::expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_FAIL); \ + } \ + } while (0) + +#define EXPECT_ENABLED_STATE_IS(expected) \ + do { \ + mozilla::LauncherResult<mozilla::LauncherRegistryInfo::EnabledState> \ + enabled = info.IsEnabled(); \ + if (enabled.isErr()) { \ + return enabled.propagateErr(); \ + } \ + if (enabled.unwrap() != mozilla::LauncherRegistryInfo::expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_TELEMETRY_IS_ENABLED(expected) \ + do { \ + mozilla::LauncherResult<bool> enabled = info.IsTelemetryEnabled(); \ + if (enabled.isErr()) { \ + return enabled.propagateErr(); \ + } \ + if (enabled.unwrap() != expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_DWORD_EXISTS_AND_EQ(name, expected) \ + do { \ + mozilla::LauncherResult<mozilla::Maybe<DWORD>> result = \ + ReadRegistryValueData<DWORD>(name, REG_DWORD); \ + if (result.isErr()) { \ + return result.propagateErr(); \ + } \ + if (result.inspect().isNothing() || \ + result.inspect().value() != expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_QWORD_EXISTS(name) \ + do { \ + mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \ + ReadRegistryValueData<uint64_t>(name, REG_QWORD); \ + if (result.isErr()) { \ + return result.propagateErr(); \ + } \ + if (result.inspect().isNothing()) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_QWORD_EXISTS_AND_EQ(name, expected) \ + do { \ + mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \ + ReadRegistryValueData<uint64_t>(name, REG_QWORD); \ + if (result.isErr()) { \ + return result.propagateErr(); \ + } \ + if (result.inspect().isNothing() || \ + result.inspect().value() != expected) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_DWORD_DOES_NOT_EXIST(name) \ + do { \ + mozilla::LauncherResult<mozilla::Maybe<DWORD>> result = \ + ReadRegistryValueData<DWORD>(name, REG_DWORD); \ + if (result.isErr()) { \ + return result.propagateErr(); \ + } \ + if (result.inspect().isSome()) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +#define EXPECT_REG_QWORD_DOES_NOT_EXIST(name) \ + do { \ + mozilla::LauncherResult<mozilla::Maybe<uint64_t>> result = \ + ReadRegistryValueData<uint64_t>(name, REG_QWORD); \ + if (result.isErr()) { \ + return result.propagateErr(); \ + } \ + if (result.inspect().isSome()) { \ + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); \ + } \ + } while (0) + +template <typename T> +static mozilla::LauncherResult<mozilla::Maybe<T>> ReadRegistryValueData( + const std::wstring& name, DWORD expectedType) { + T data; + DWORD dataLen = sizeof(data); + DWORD type; + LSTATUS status = ::RegGetValueW(HKEY_CURRENT_USER, kRegKeyPath, name.c_str(), + RRF_RT_ANY, &type, &data, &dataLen); + if (status == ERROR_FILE_NOT_FOUND) { + return mozilla::Maybe<T>(); + } + + if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); + } + + if (type != expectedType) { + return LAUNCHER_ERROR_FROM_WIN32(ERROR_DATATYPE_MISMATCH); + } + + return mozilla::Some(data); +} + +template <typename T> +static mozilla::LauncherVoidResult WriteRegistryValueData( + const std::wstring& name, DWORD type, T data) { + LSTATUS status = ::RegSetKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, + name.c_str(), type, &data, sizeof(T)); + if (status != ERROR_SUCCESS) { + return LAUNCHER_ERROR_FROM_WIN32(status); + } + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult DeleteRegistryValueData( + const std::wstring& name) { + LSTATUS status = + ::RegDeleteKeyValueW(HKEY_CURRENT_USER, kRegKeyPath, name.c_str()); + if (status == ERROR_SUCCESS || status == ERROR_FILE_NOT_FOUND) { + return mozilla::Ok(); + } + + return LAUNCHER_ERROR_FROM_WIN32(status); +} + +static mozilla::LauncherVoidResult DeleteAllRegstryValues() { + // Unblock commit via ReflectPrefToRegistry + // (We need to set false, and then true to bypass the early return) + mozilla::LauncherRegistryInfo info; + mozilla::LauncherVoidResult vr = info.ReflectPrefToRegistry(false); + vr = info.ReflectPrefToRegistry(true); + if (vr.isErr()) { + return vr; + } + + vr = DeleteRegistryValueData(gImageValue); + if (vr.isErr()) { + return vr; + } + + vr = DeleteRegistryValueData(gLauncherValue); + if (vr.isErr()) { + return vr; + } + + vr = DeleteRegistryValueData(gBrowserValue); + if (vr.isErr()) { + return vr; + } + + return DeleteRegistryValueData(gTelemetryValue); +} + +static mozilla::LauncherVoidResult SetupEnabledScenario() { + // Reset the registry state to an enabled state. First, we delete all existing + // registry values (if any). + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; + } + + // Now we run Check(Launcher)... + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); + // ...and Check(Browser) + EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + + // By this point we are considered to be fully enabled. + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestEmptyRegistry() { + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); + + // LauncherRegistryInfo should have created Launcher and Image values + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_EXISTS(gLauncherValue); + EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue); + + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestNormal() { + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, QPCNowRaw()); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + + // Make sure the browser timestamp is newer than the launcher's + mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherTs = + ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD); + if (launcherTs.isErr()) { + return launcherTs.propagateErr(); + } + mozilla::LauncherResult<mozilla::Maybe<uint64_t>> browserTs = + ReadRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD); + if (browserTs.isErr()) { + return browserTs.propagateErr(); + } + if (launcherTs.inspect().isNothing() || browserTs.inspect().isNothing() || + browserTs.inspect().value() <= launcherTs.inspect().value()) { + return LAUNCHER_ERROR_FROM_HRESULT(E_FAIL); + } + + EXPECT_ENABLED_STATE_IS(EnabledState::Enabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestBrowserNoLauncher() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + vr = DeleteRegistryValueData(gLauncherValue); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + + // Verify that we still don't have a launcher timestamp + EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue); + // Verify that the browser timestamp is now zero + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL); + + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestLauncherNoBrowser() { + constexpr uint64_t launcherTs = 0x77777777; + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, launcherTs); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + + // Launcher's timestamps is kept intact while browser's is set to 0. + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL); + + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestBrowserLessThanLauncher() { + constexpr uint64_t launcherTs = 0x77777777, browserTs = 0x66666666; + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, gMyImageTimestamp); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, launcherTs); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, browserTs); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + + // Launcher's timestamps is kept intact while browser's is set to 0. + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL); + + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestImageTimestampChange() { + // This should reset the timestamps and then essentially run like + // TestEmptyRegistry + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD, 1ULL); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, 2ULL); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); + + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_EXISTS(gLauncherValue); + EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestImageTimestampChangeWhenDisabled() { + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678); + if (vr.isErr()) { + return vr; + } + vr = WriteRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD, 0ULL); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0); + + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestDisableDueToFailure() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + // Check that we are indeed enabled. + mozilla::LauncherRegistryInfo info; + EXPECT_ENABLED_STATE_IS(EnabledState::Enabled); + + // Now call DisableDueToFailure + mozilla::LauncherVoidResult lvr = info.DisableDueToFailure(); + if (lvr.isErr()) { + return lvr.propagateErr(); + } + + // We should now be FailDisabled + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + // If we delete the launcher timestamp, IsEnabled should then return + // ForceDisabled. + vr = DeleteRegistryValueData(gLauncherValue); + if (vr.isErr()) { + return vr; + } + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestPrefReflection() { + // Reset the registry to a known good state. + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + // Let's see what happens when we flip the pref to OFF. + mozilla::LauncherRegistryInfo info; + mozilla::LauncherVoidResult reflectOk = info.ReflectPrefToRegistry(false); + if (reflectOk.isErr()) { + return reflectOk.propagateErr(); + } + + // Launcher timestamp should be non-existent. + EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue); + // Browser timestamp should be zero + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, 0ULL); + // IsEnabled should give us ForceDisabled + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + // Now test to see what happens when the pref is set to ON. + reflectOk = info.ReflectPrefToRegistry(true); + if (reflectOk.isErr()) { + return reflectOk.propagateErr(); + } + + // Launcher and browser timestamps should be non-existent. + EXPECT_REG_QWORD_DOES_NOT_EXIST(gLauncherValue); + EXPECT_REG_QWORD_DOES_NOT_EXIST(gBrowserValue); + + // IsEnabled should give us Enabled. + EXPECT_ENABLED_STATE_IS(EnabledState::Enabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestTelemetryConfig() { + mozilla::LauncherVoidResult vr = DeleteAllRegstryValues(); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_TELEMETRY_IS_ENABLED(false); + + mozilla::LauncherVoidResult reflectOk = + info.ReflectTelemetryPrefToRegistry(false); + if (reflectOk.isErr()) { + return reflectOk.propagateErr(); + } + EXPECT_TELEMETRY_IS_ENABLED(false); + + reflectOk = info.ReflectTelemetryPrefToRegistry(true); + if (reflectOk.isErr()) { + return reflectOk.propagateErr(); + } + EXPECT_TELEMETRY_IS_ENABLED(true); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestCommitAbort() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + // Retrieve the current timestamps to compare later + mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherValue = + ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD); + if (launcherValue.isErr() || launcherValue.inspect().isNothing()) { + return launcherValue.propagateErr(); + } + mozilla::LauncherResult<mozilla::Maybe<uint64_t>> browserValue = + ReadRegistryValueData<uint64_t>(gBrowserValue, REG_QWORD); + if (browserValue.isErr() || browserValue.inspect().isNothing()) { + return browserValue.propagateErr(); + } + uint64_t launcherTs = launcherValue.inspect().value(); + uint64_t browserTs = browserValue.inspect().value(); + + vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + // No commit + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Exiting the scope discards the change. + mozilla::LauncherRegistryInfo info; + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, browserTs); + + // Commit -> Check -> Abort -> Commit + EXPECT_COMMIT_IS_OK(); + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + info.Abort(); + EXPECT_COMMIT_IS_OK(); + + // Nothing is changed. + EXPECT_REG_DWORD_EXISTS_AND_EQ(gImageValue, gMyImageTimestamp); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gBrowserValue, browserTs); + EXPECT_ENABLED_STATE_IS(EnabledState::Enabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestDisableDuringLauncherLaunch() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherResult<mozilla::Maybe<uint64_t>> launcherTs = + ReadRegistryValueData<uint64_t>(gLauncherValue, REG_QWORD); + if (launcherTs.isErr()) { + return launcherTs.propagateErr(); + } + if (launcherTs.inspect().isNothing()) { + return LAUNCHER_ERROR_FROM_HRESULT(E_UNEXPECTED); + } + + vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + + // Call DisableDueToFailure with a different instance + mozilla::LauncherVoidResult vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + mozilla::LauncherVoidResult vr = info.DisableDueToFailure(); + if (vr.isErr()) { + return vr.propagateErr(); + } + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Commit after disable. + EXPECT_COMMIT_IS_OK(); + + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Make sure we're still FailDisabled and the launcher's timestamp is not + // updated + mozilla::LauncherRegistryInfo info; + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + EXPECT_REG_QWORD_EXISTS_AND_EQ(gLauncherValue, launcherTs.inspect().value()); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestDisableDuringBrowserLaunch() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); + + vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + EXPECT_CHECK_RESULT_IS(ProcessType::Browser, ProcessType::Browser); + + // Call DisableDueToFailure with a different instance + mozilla::LauncherVoidResult vr = []() -> mozilla::LauncherVoidResult { + mozilla::LauncherRegistryInfo info; + mozilla::LauncherVoidResult vr = info.DisableDueToFailure(); + if (vr.isErr()) { + return vr.propagateErr(); + } + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Commit after disable. + EXPECT_COMMIT_IS_OK(); + + return mozilla::Ok(); + }(); + if (vr.isErr()) { + return vr; + } + + // Make sure we're still FailDisabled + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + return mozilla::Ok(); +} + +static mozilla::LauncherVoidResult TestReEnable() { + mozilla::LauncherVoidResult vr = SetupEnabledScenario(); + if (vr.isErr()) { + return vr; + } + + // Make FailDisabled + mozilla::LauncherRegistryInfo info; + vr = info.DisableDueToFailure(); + if (vr.isErr()) { + return vr.propagateErr(); + } + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + // Attempt to launch when FailDisabled: Still be FailDisabled + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + EXPECT_ENABLED_STATE_IS(EnabledState::FailDisabled); + + // Change the timestamp + vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678); + if (vr.isErr()) { + return vr; + } + + // Attempt to launch again: Launcher comes back + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Launcher); + EXPECT_COMMIT_IS_OK(); + + // Make ForceDisabled + vr = info.ReflectPrefToRegistry(false); + if (vr.isErr()) { + return vr.propagateErr(); + } + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + // Attempt to launch when ForceDisabled: Still be ForceDisabled + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + // Change the timestamp + vr = WriteRegistryValueData<DWORD>(gImageValue, REG_DWORD, 0x12345678); + if (vr.isErr()) { + return vr; + } + + // Attempt to launch again: Still be ForceDisabled + EXPECT_CHECK_RESULT_IS(ProcessType::Launcher, ProcessType::Browser); + EXPECT_COMMIT_IS_OK(); + EXPECT_ENABLED_STATE_IS(EnabledState::ForceDisabled); + + return mozilla::Ok(); +} + +int main(int argc, char* argv[]) { + auto fullPath = mozilla::GetFullBinaryPath(); + if (!fullPath) { + return 1; + } + + // Global setup for all tests + gBrowserValue = fullPath.get(); + gBrowserValue += kBrowserSuffix; + + gLauncherValue = fullPath.get(); + gLauncherValue += kLauncherSuffix; + + gImageValue = fullPath.get(); + gImageValue += kImageSuffix; + + gTelemetryValue = fullPath.get(); + gTelemetryValue += kTelemetrySuffix; + + mozilla::LauncherResult<DWORD> timestamp = 0; + RUN_TEST(timestamp, GetCurrentImageTimestamp); + gMyImageTimestamp = timestamp.unwrap(); + + auto onExit = mozilla::MakeScopeExit( + []() { mozilla::Unused << DeleteAllRegstryValues(); }); + + mozilla::LauncherVoidResult vr = mozilla::Ok(); + + // All testcases should call SetupEnabledScenario() or + // DeleteAllRegstryValues() to be order-independent + RUN_TEST(vr, TestEmptyRegistry); + RUN_TEST(vr, TestNormal); + RUN_TEST(vr, TestBrowserNoLauncher); + RUN_TEST(vr, TestLauncherNoBrowser); + RUN_TEST(vr, TestBrowserLessThanLauncher); + RUN_TEST(vr, TestImageTimestampChange); + RUN_TEST(vr, TestImageTimestampChangeWhenDisabled); + RUN_TEST(vr, TestDisableDueToFailure); + RUN_TEST(vr, TestPrefReflection); + RUN_TEST(vr, TestTelemetryConfig); + RUN_TEST(vr, TestCommitAbort); + RUN_TEST(vr, TestDisableDuringLauncherLaunch); + RUN_TEST(vr, TestDisableDuringBrowserLaunch); + RUN_TEST(vr, TestReEnable); + + return 0; +} diff --git a/toolkit/xre/test/win/TestXREMakeCommandLineWin.cpp b/toolkit/xre/test/win/TestXREMakeCommandLineWin.cpp new file mode 100644 index 0000000000..c12550c3d6 --- /dev/null +++ b/toolkit/xre/test/win/TestXREMakeCommandLineWin.cpp @@ -0,0 +1,266 @@ +/* -*- 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 <stdio.h> +#include <stdlib.h> +#include <windows.h> +// Support for _setmode +#include <fcntl.h> +#include <io.h> + +#include "nsWindowsRestart.cpp" + +// CommandLineToArgvW may return different values for argv[0] since it contains +// the path to the binary that was executed so we prepend an argument that is +// quoted with a space to prevent argv[1] being appended to argv[0]. +#define DUMMY_ARG1 L"\"arg 1\" " + +#ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# elif defined(MAX_PATH) +# define MAXPATHLEN MAX_PATH +# elif defined(_MAX_PATH) +# define MAXPATHLEN _MAX_PATH +# elif defined(CCHMAXPATH) +# define MAXPATHLEN CCHMAXPATH +# else +# define MAXPATHLEN 1024 +# endif +#endif + +#define TEST_NAME L"XRE MakeCommandLine" +#define MAX_TESTS 100 + +// Verbose output can be enabled by defining VERBOSE 1 +#define VERBOSE 0 + +// Compares compareCmdLine with the output of MakeCommandLine. This is +// accomplished by converting inCmdLine to an argument list with +// CommandLineToArgvW and converting it back to a command line with +// MakeCommandLine. +static int verifyCmdLineCreation(wchar_t* inCmdLine, wchar_t* compareCmdLine, + bool passes, int testNum) { + int rv = 0; + int i; + int inArgc; + int outArgc; + bool isEqual; + + // When debugging with command lines containing Unicode characters greater + // than 255 you can set the mode for stdout to Unicode so the console will + // receive the correct characters though it won't display them properly unless + // the console's font has been set to one that can display the characters. You + // can also redirect the console output to a file that has been saved as + // Unicode to view the characters. + // _setmode(_fileno(stdout), _O_WTEXT); + + // Prepend an additional argument to the command line. CommandLineToArgvW + // handles argv[0] differently than other arguments since argv[0] is the path + // to the binary being executed and MakeCommandLine only handles argv[1] and + // larger. + wchar_t* inCmdLineNew = (wchar_t*)malloc( + (wcslen(DUMMY_ARG1) + wcslen(inCmdLine) + 1) * sizeof(wchar_t)); + wcscpy(inCmdLineNew, DUMMY_ARG1); + wcscat(inCmdLineNew, inCmdLine); + LPWSTR* inArgv = CommandLineToArgvW(inCmdLineNew, &inArgc); + + auto outCmdLine = mozilla::MakeCommandLine(inArgc - 1, inArgv + 1); + wchar_t* outCmdLineNew = (wchar_t*)malloc( + (wcslen(DUMMY_ARG1) + wcslen(outCmdLine.get()) + 1) * sizeof(wchar_t)); + wcscpy(outCmdLineNew, DUMMY_ARG1); + wcscat(outCmdLineNew, outCmdLine.get()); + LPWSTR* outArgv = CommandLineToArgvW(outCmdLineNew, &outArgc); + + if (VERBOSE) { + wprintf(L"\n"); + wprintf(L"Verbose Output\n"); + wprintf(L"--------------\n"); + wprintf(L"Input command line : >%s<\n", inCmdLine); + wprintf(L"MakeComandLine output: >%s<\n", outCmdLine.get()); + wprintf(L"Expected command line: >%s<\n", compareCmdLine); + + wprintf(L"input argc : %d\n", inArgc - 1); + wprintf(L"output argc: %d\n", outArgc - 1); + + for (i = 1; i < inArgc; ++i) { + wprintf(L"input argv[%d] : >%s<\n", i - 1, inArgv[i]); + } + + for (i = 1; i < outArgc; ++i) { + wprintf(L"output argv[%d]: >%s<\n", i - 1, outArgv[i]); + } + wprintf(L"\n"); + } + + isEqual = (inArgc == outArgc); + if (!isEqual) { + wprintf(L"TEST-%s-FAIL | %s | ARGC Comparison (check %2d)\n", + passes ? L"UNEXPECTED" : L"KNOWN", TEST_NAME, testNum); + if (passes) { + rv = 1; + } + LocalFree(inArgv); + LocalFree(outArgv); + free(inCmdLineNew); + free(outCmdLineNew); + return rv; + } + + for (i = 1; i < inArgc; ++i) { + isEqual = (wcscmp(inArgv[i], outArgv[i]) == 0); + if (!isEqual) { + wprintf(L"TEST-%s-FAIL | %s | ARGV Comparison (check %2d)\n", + passes ? L"UNEXPECTED" : L"KNOWN", TEST_NAME, testNum); + if (passes) { + rv = 1; + } + LocalFree(inArgv); + LocalFree(outArgv); + free(inCmdLineNew); + free(outCmdLineNew); + return rv; + } + } + + isEqual = (wcscmp(outCmdLine.get(), compareCmdLine) == 0); + if (!isEqual) { + wprintf(L"TEST-%s-FAIL | %s | Command Line Comparison (check %2d)\n", + passes ? L"UNEXPECTED" : L"KNOWN", TEST_NAME, testNum); + if (passes) { + rv = 1; + } + LocalFree(inArgv); + LocalFree(outArgv); + free(inCmdLineNew); + free(outCmdLineNew); + return rv; + } + + if (rv == 0) { + if (passes) { + wprintf(L"TEST-PASS | %s | check %2d\n", TEST_NAME, testNum); + } else { + wprintf(L"TEST-UNEXPECTED-PASS | %s | check %2d\n", TEST_NAME, testNum); + rv = 1; + } + } + + LocalFree(inArgv); + LocalFree(outArgv); + free(inCmdLineNew); + free(outCmdLineNew); + return rv; +} + +int wmain(int argc, wchar_t* argv[]) { + int i; + int rv = 0; + + if (argc > 1 && (_wcsicmp(argv[1], L"-check-one") != 0 || argc != 3)) { + fwprintf(stderr, + L"Displays and validates output from MakeCommandLine.\n\n"); + fwprintf(stderr, L"Usage: %s -check-one <test number>\n\n", argv[0]); + fwprintf(stderr, + L" <test number>\tSpecifies the test number to run from the\n"); + fwprintf(stderr, L"\t\tTestXREMakeCommandLineWin.ini file.\n"); + return 255; + } + + wchar_t inifile[MAXPATHLEN]; + if (!::GetModuleFileNameW(0, inifile, MAXPATHLEN)) { + wprintf(L"TEST-UNEXPECTED-FAIL | %s | GetModuleFileNameW\n", TEST_NAME); + return 2; + } + + WCHAR* slash = wcsrchr(inifile, '\\'); + if (!slash) { + wprintf(L"TEST-UNEXPECTED-FAIL | %s | wcsrchr\n", TEST_NAME); + return 3; + } + + wcscpy(slash + 1, L"TestXREMakeCommandLineWin.ini\0"); + + for (i = 0; i < MAX_TESTS; ++i) { + wchar_t sInputVal[MAXPATHLEN]; + wchar_t sOutputVal[MAXPATHLEN]; + wchar_t sPassesVal[MAXPATHLEN]; + wchar_t sInputKey[MAXPATHLEN]; + wchar_t sOutputKey[MAXPATHLEN]; + wchar_t sPassesKey[MAXPATHLEN]; + + if (argc > 2 && _wcsicmp(argv[1], L"-check-one") == 0 && argc == 3) { + i = _wtoi(argv[2]); + } + + _snwprintf(sInputKey, MAXPATHLEN, L"input_%d", i); + _snwprintf(sOutputKey, MAXPATHLEN, L"output_%d", i); + _snwprintf(sPassesKey, MAXPATHLEN, L"passes_%d", i); + + if (!GetPrivateProfileStringW(L"MakeCommandLineTests", sInputKey, nullptr, + sInputVal, MAXPATHLEN, inifile)) { + if (i == 0 || (argc > 2 && _wcsicmp(argv[1], L"-check-one") == 0)) { + wprintf(L"TEST-UNEXPECTED-FAIL | %s | see following explanation:\n", + TEST_NAME); + wprintf( + L"ERROR: Either the TestXREMakeCommandLineWin.ini file doesn't " + L"exist\n"); + if (argc > 1 && _wcsicmp(argv[1], L"-check-one") == 0 && argc == 3) { + wprintf( + L"ERROR: or the test is not defined in the MakeCommandLineTests " + L"section.\n"); + } else { + wprintf( + L"ERROR: or it has no tests defined in the MakeCommandLineTests " + L"section.\n"); + } + wprintf(L"ERROR: File: %s\n", inifile); + return 4; + } + break; + } + + GetPrivateProfileStringW(L"MakeCommandLineTests", sOutputKey, nullptr, + sOutputVal, MAXPATHLEN, inifile); + GetPrivateProfileStringW(L"MakeCommandLineTests", sPassesKey, nullptr, + sPassesVal, MAXPATHLEN, inifile); + + rv |= verifyCmdLineCreation( + sInputVal, sOutputVal, + (_wcsicmp(sPassesVal, L"false") == 0) ? FALSE : TRUE, i); + + if (argc > 2 && _wcsicmp(argv[1], L"-check-one") == 0) { + break; + } + } + + if (rv == 0) { + wprintf(L"TEST-PASS | %s | all checks passed\n", TEST_NAME); + } else { + wprintf(L"TEST-UNEXPECTED-FAIL | %s | some checks failed\n", TEST_NAME); + } + + return rv; +} + +#ifdef __MINGW32__ + +/* MingW currently does not implement a wide version of the + startup routines. Workaround is to implement something like + it ourselves. See bug 411826 */ + +# include <shellapi.h> + +int main(int argc, char** argv) { + LPWSTR commandLine = GetCommandLineW(); + int argcw = 0; + LPWSTR* argvw = CommandLineToArgvW(commandLine, &argcw); + if (!argvw) return 127; + + int result = wmain(argcw, argvw); + LocalFree(argvw); + return result; +} +#endif /* __MINGW32__ */ diff --git a/toolkit/xre/test/win/TestXREMakeCommandLineWin.ini b/toolkit/xre/test/win/TestXREMakeCommandLineWin.ini new file mode 100644 index 0000000000..dbb529d1b1 --- /dev/null +++ b/toolkit/xre/test/win/TestXREMakeCommandLineWin.ini @@ -0,0 +1,94 @@ +; A typical MakeCommandLine test will contain an input and an output name value +; pair. The value for input_xx is the input command line and the value for +; output_xx is the expected output command line. +; +; A test that is known to fail can be added as follows. If the passes_xx name +; value pair doesn't exist it defaults to true. +; input_99=yabadaba +; output_99=doo +; passes_99=false +; +; If a value starts and ends with single or double quotation marks then it must +; be enclosed in single or double quotation marks due to GetPrivateProfileString +; discarding the outmost quotation marks. See GetPrivateProfileString on MSDN +; for more information. +; http://msdn.microsoft.com/en-us/library/ms724353.aspx + +[MakeCommandLineTests] +input_0=a:\ +output_0=a:\ + +input_1=""a:\"" +output_1=a:\" + +input_2=""a:\b c"" +output_2=""a:\b c"" + +input_3=""a:\b c\"" +output_3=""a:\b c\""" + +input_4=""a:\b c\d e"" +output_4=""a:\b c\d e"" + +input_5=""a:\b c\d e\"" +output_5=""a:\b c\d e\""" + +input_6=""a:\\"" +output_6=a:\ + +input_7="a:\" "b:\c d" +output_7=a:\" "b:\c d" + +input_8="a "b:\" "c:\d e"" +output_8="a "b:\" c:\d" e" + +input_9="abc" d e +output_9=abc d e + +input_10="a b c" d e +output_10="a b c" d e + +input_11=a\\\b d"e f"g h +output_11=a\\\b "de fg" h + +input_12=a b +output_12=a b + +input_13=""a b"" +output_13=""a b"" + +input_14=a\\\"b c d +output_14=a\\\"b c d + +input_15=a\\\"b c" +output_15=a\\\"b c + +input_16=""a\\\b c" +output_16=""a\\\b c"" + +input_17=\"a +output_17=\"a + +input_18=\\"a +output_18=\a + +input_19=\\"\\\\"a +output_19=\\\a + +input_20=\\"\\\\\"a +output_20=\\\\\\\"a + +input_21="a\\\"b c\" d e +output_21=""a\\\"b c\" d e"" + +input_22=a\\\\\"b c" d e" +output_22=a\\\\\"b "c d e" + +input_23=a:\b c\アルファ オメガ\d +output_23=a:\b c\アルファ オメガ\d + +input_24=a:\b "c\アルファ オメガ\d" +output_24=a:\b "c\アルファ オメガ\d" + +input_25=アルファ オメガ +output_25=アルファ オメガ diff --git a/toolkit/xre/test/win/moz.build b/toolkit/xre/test/win/moz.build new file mode 100644 index 0000000000..8dbdbe5389 --- /dev/null +++ b/toolkit/xre/test/win/moz.build @@ -0,0 +1,44 @@ +# -*- Mode: python; 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/. + +GeckoCppUnitTests( + [ + "TestXREMakeCommandLineWin", + ], + linkage=None, +) + +# This needs to be installed alongside the above unit test. +FINAL_TARGET_FILES += [ + "TestXREMakeCommandLineWin.ini", +] + +DEFINES["NS_NO_XPCOM"] = True + +LOCAL_INCLUDES += [ + "/config", + "/toolkit/xre", +] + +DisableStlWrapping() +USE_STATIC_LIBS = True + +OS_LIBS += [ + "comctl32", + "shell32", + "ws2_32", +] + +if CONFIG["MOZ_LAUNCHER_PROCESS"]: + GeckoCppUnitTests( + [ + "TestLauncherRegistryInfo", + ], + linkage=None, + ) + # Needed for TestLauncherRegistryInfo + for var in ("MOZ_APP_BASENAME", "MOZ_APP_VENDOR"): + DEFINES[var] = '"%s"' % CONFIG[var] |