diff options
Diffstat (limited to 'toolkit/xre/test/gtest')
-rw-r--r-- | toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp | 227 | ||||
-rw-r--r-- | toolkit/xre/test/gtest/TestCmdLineAndEnvUtils.cpp | 372 | ||||
-rw-r--r-- | toolkit/xre/test/gtest/TestCompatVersionCompare.cpp | 53 | ||||
-rw-r--r-- | toolkit/xre/test/gtest/TestGeckoArgs.cpp | 179 | ||||
-rw-r--r-- | toolkit/xre/test/gtest/moz.build | 24 |
5 files changed, 855 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..e36c62e0db --- /dev/null +++ b/toolkit/xre/test/gtest/TestAssembleCommandLineWin.cpp @@ -0,0 +1,227 @@ +/* -*- 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/gtest/MozAssertions.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 + {{"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"\""}, + + // Empty string cases + {{"", nullptr}, L"\"\""}, + {{"foo", "", nullptr}, L"foo \"\""}, + {{"", "bar", nullptr}, L"\"\" bar"}, + {{"foo", "", "bar", nullptr}, L"foo \"\" bar"}, +}; + +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); + + 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_NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)); + EXPECT_EQ(static_cast<size_t>(len), ArrayLength(kExpectedArgsW)); + for (size_t 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_NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)); + EXPECT_EQ(static_cast<size_t>(len), ArrayLength(kExpectedArgsW)); + for (size_t 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_NS_SUCCEEDED(workingDir->GetPath(arg)); + EXPECT_STREQ(arg.get(), workingDirW); + + receiver.Parse(v2.CopyData()); + EXPECT_NS_SUCCEEDED(receiver.CommandLineRunner()->GetLength(&len)); + EXPECT_EQ(static_cast<size_t>(len), ArrayLength(kExpectedArgsW)); + for (size_t 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_NS_SUCCEEDED(workingDir->GetPath(arg)); + EXPECT_STREQ(arg.get(), workingDirW); +} + +TEST(WinRemoteMessage, NonNullTerminatedBuffer) +{ + // Reserve two pages and commit the first one + const uint32_t kPageSize = 4096; + UniquePtr<void, VirtualFreeDeleter> pages( + ::VirtualAlloc(nullptr, kPageSize * 2, MEM_RESERVE, PAGE_NOACCESS)); + EXPECT_TRUE(pages); + EXPECT_TRUE( + ::VirtualAlloc(pages.get(), kPageSize, MEM_COMMIT, PAGE_READWRITE)); + + // Test strings with lengths between 0 and |kMaxBufferSize| bytes + const int kMaxBufferSize = 10; + + // Set a string just before the boundary between the two pages. + uint8_t* bufferEnd = reinterpret_cast<uint8_t*>(pages.get()) + kPageSize; + memset(bufferEnd - kMaxBufferSize, '$', kMaxBufferSize); + + nsCOMPtr<nsIFile> workingDir; + COPYDATASTRUCT copyData = {}; + for (int i = 0; i < kMaxBufferSize; ++i) { + WinRemoteMessageReceiver receiver; + + copyData.cbData = i; + copyData.lpData = bufferEnd - i; + + copyData.dwData = + static_cast<ULONG_PTR>(WinRemoteMessageVersion::CommandLineOnly); + EXPECT_NS_SUCCEEDED(receiver.Parse(©Data)); + EXPECT_EQ(receiver.CommandLineRunner()->GetWorkingDirectory( + getter_AddRefs(workingDir)), + NS_ERROR_NOT_INITIALIZED); + + copyData.dwData = static_cast<ULONG_PTR>( + WinRemoteMessageVersion::CommandLineAndWorkingDir); + EXPECT_NS_SUCCEEDED(receiver.Parse(©Data)); + EXPECT_EQ(receiver.CommandLineRunner()->GetWorkingDirectory( + getter_AddRefs(workingDir)), + NS_ERROR_NOT_INITIALIZED); + + copyData.dwData = static_cast<ULONG_PTR>( + WinRemoteMessageVersion::CommandLineAndWorkingDirInUtf16); + EXPECT_NS_SUCCEEDED(receiver.Parse(©Data)); + EXPECT_EQ(receiver.CommandLineRunner()->GetWorkingDirectory( + getter_AddRefs(workingDir)), + NS_ERROR_NOT_INITIALIZED); + } +} diff --git a/toolkit/xre/test/gtest/TestCmdLineAndEnvUtils.cpp b/toolkit/xre/test/gtest/TestCmdLineAndEnvUtils.cpp new file mode 100644 index 0000000000..a5f8a19a9c --- /dev/null +++ b/toolkit/xre/test/gtest/TestCmdLineAndEnvUtils.cpp @@ -0,0 +1,372 @@ +/* -*- 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 <memory> +#include <ostream> +#include <string> +#include <string_view> +#include <type_traits> +#include <vector> + +#include "gtest/gtest.h" + +#include "mozilla/CmdLineAndEnvUtils.h" + +// Insulate these test-classes from the outside world. +// +// (In particular, distinguish this `CommandLine` from the one in ipc/chromium. +// The compiler has no issues here, but the MSVC debugger gets somewhat +// confused.) +namespace testzilla { + +// Auxiliary class to simplify the declaration of test-cases for +// EnsureCommandlineSafe. +class CommandLine { + const size_t size; + std::vector<std::string> _data_8; + std::vector<std::wstring> _data_16; + // _should_ be const all the way through, but... + char const** argv_8; + wchar_t const** argv_16; + + public: + inline int argc() const { return (int)(unsigned int)(size); } + + template <typename CharT> + CharT const** argv() const; + + CommandLine(CommandLine const&) = delete; + CommandLine(CommandLine&& that) = delete; + + template <typename Container> + explicit CommandLine(Container const& container) + : size{std::size(container) + 1}, // plus one for argv[0] + argv_8(new const char*[size + 1]), // plus one again for argv[argc] + argv_16(new const wchar_t*[size + 1]) { + _data_8.reserve(size + 1); + _data_16.reserve(size + 1); + size_t pos = 0; + + const auto append = [&](const char* val) { + size_t const len = ::strlen(val); + _data_8.emplace_back(val, val + len); + _data_16.emplace_back(val, val + len); + argv_8[pos] = _data_8.back().data(); + argv_16[pos] = _data_16.back().data(); + ++pos; + }; + + append("GECKOAPP.COM"); // argv[0] may be anything + for (const char* item : container) { + append(item); + } + assert(pos == size); + + // C99 §5.1.2.2.1 states "argv[argc] shall be a null pointer", and + // `CheckArg` implicitly relies on this. + argv_8[pos] = nullptr; + argv_16[pos] = nullptr; + } + + // debug output + friend std::ostream& operator<<(std::ostream& o, CommandLine const& cl) { + for (const auto& item : cl._data_8) { + o << '"'; + for (const char c : item) { + if (c == '"') { + o << "\\\""; + } else { + o << c; + } + } + o << "\", "; + } + return o; + } +}; + +template <> +char const** CommandLine::argv<char>() const { + return argv_8; +} +template <> +wchar_t const** CommandLine::argv<wchar_t>() const { + return argv_16; +} + +enum TestCaseState : bool { + FAIL = false, + PASS = true, +}; +constexpr TestCaseState operator!(TestCaseState s) { + return TestCaseState(!bool(s)); +} + +#ifdef XP_WIN +constexpr static const TestCaseState WIN_ONLY = PASS; +#else +constexpr static const TestCaseState WIN_ONLY = FAIL; +#endif + +using NarrowTestCase = std::pair<const char*, const char*>; +constexpr std::pair<TestCaseState, NarrowTestCase> kStrMatches8[] = { + {PASS, {"", ""}}, + + {PASS, {"i", "i"}}, + {PASS, {"i", "I"}}, + + {FAIL, {"", "i"}}, + {FAIL, {"i", ""}}, + {FAIL, {"i", "j"}}, + + {PASS, {"mozilla", "mozilla"}}, + {PASS, {"mozilla", "Mozilla"}}, + {PASS, {"mozilla", "MOZILLA"}}, + {PASS, {"mozilla", "mOZILLA"}}, + {PASS, {"mozilla", "mOZIlLa"}}, + {PASS, {"mozilla", "MoZiLlA"}}, + + {FAIL, {"mozilla", ""}}, + {FAIL, {"mozilla", "mozill"}}, + {FAIL, {"mozilla", "mozillo"}}, + {FAIL, {"mozilla", "mozillam"}}, + {FAIL, {"mozilla", "mozilla-"}}, + {FAIL, {"mozilla", "-mozilla"}}, + {FAIL, {"-mozilla", "mozilla"}}, + + // numbers are permissible + {PASS, {"m0zi11a", "m0zi11a"}}, + {PASS, {"m0zi11a", "M0ZI11A"}}, + {PASS, {"m0zi11a", "m0Zi11A"}}, + + {FAIL, {"mozilla", "m0zi11a"}}, + {FAIL, {"m0zi11a", "mozilla"}}, + + // capital letters are not accepted in the left comparand + {FAIL, {"I", "i"}}, + {FAIL, {"I", "i"}}, + {FAIL, {"Mozilla", "mozilla"}}, + {FAIL, {"Mozilla", "Mozilla"}}, + + // punctuation other than `-` is rejected + {FAIL, {"*", "*"}}, + {FAIL, {"*", "word"}}, + {FAIL, {".", "."}}, + {FAIL, {".", "a"}}, + {FAIL, {"_", "_"}}, + + // spaces are rejected + {FAIL, {" ", " "}}, + {FAIL, {"two words", "two words"}}, + + // non-ASCII characters are rejected + // + // (the contents of this test case may differ depending on the source and + // execution character sets, but the result should always be failure) + {FAIL, {"à", "a"}}, + {FAIL, {"a", "à"}}, + {FAIL, {"à", "à"}}, +}; + +#ifdef XP_WIN +using WideTestCase = std::pair<const char*, const wchar_t*>; +std::pair<TestCaseState, WideTestCase> kStrMatches16[] = { + // (Turkish 'İ' may lowercase to 'i' in some locales, but + // we explicitly prevent that from being relevant) + {FAIL, {"i", L"İ"}}, + {FAIL, {"mozilla", L"ṁozilla"}}, +}; +#endif + +constexpr const char* kRequiredArgs[] = {"aleph", "beth"}; + +std::pair<TestCaseState, std::vector<const char*>> const kCommandLines[] = { + // the basic admissible forms + {PASS, {"-osint", "-aleph", "http://www.example.com/"}}, + {PASS, {"-osint", "-beth", "http://www.example.com/"}}, + + // GNU-style double-dashes are also allowed + {PASS, {"--osint", "-aleph", "http://www.example.com/"}}, + {PASS, {"--osint", "-beth", "http://www.example.com/"}}, + {PASS, {"--osint", "--aleph", "http://www.example.com/"}}, + {PASS, {"--osint", "--beth", "http://www.example.com/"}}, + + // on Windows, slashes are also permitted + {WIN_ONLY, {"-osint", "/aleph", "http://www.example.com/"}}, + {WIN_ONLY, {"--osint", "/aleph", "http://www.example.com/"}}, + // - These should pass on Windows because they're well-formed. + // - These should pass elsewhere because the first parameter is + // just a local filesystem path, not an option. + {PASS, {"/osint", "/aleph", "http://www.example.com/"}}, + {PASS, {"/osint", "-aleph", "http://www.example.com/"}}, + {PASS, {"/osint", "--aleph", "http://www.example.com/"}}, + + // the required argument's argument may not itself be a flag + {FAIL, {"-osint", "-aleph", "-anything"}}, + {FAIL, {"-osint", "-aleph", "--anything"}}, + // - On Windows systems this is a switch and should be rejected. + // - On non-Windows systems this is potentially a valid local path. + {!WIN_ONLY, {"-osint", "-aleph", "/anything"}}, + + // wrong order + {FAIL, {"-osint", "http://www.example.com/", "-aleph"}}, + {FAIL, {"-aleph", "-osint", "http://www.example.com/"}}, + {FAIL, {"-aleph", "http://www.example.com/", "-osint"}}, + {FAIL, {"http://www.example.com/", "-osint", "-aleph"}}, + {FAIL, {"http://www.example.com/", "-aleph", "-osint"}}, + + // missing arguments + {FAIL, {"-osint", "http://www.example.com/"}}, + {FAIL, {"-osint", "-aleph"}}, + {FAIL, {"-osint"}}, + + // improper arguments + {FAIL, {"-osint", "-other-argument", "http://www.example.com/"}}, + {FAIL, {"-osint", "", "http://www.example.com/"}}, + + // additional arguments + {FAIL, {"-osint", "-aleph", "http://www.example.com/", "-other-argument"}}, + {FAIL, {"-osint", "-other-argument", "-aleph", "http://www.example.com/"}}, + {FAIL, {"-osint", "-aleph", "-other-argument", "http://www.example.com/"}}, + {FAIL, {"-osint", "-aleph", "http://www.example.com/", "-a", "-b"}}, + {FAIL, {"-osint", "-aleph", "-a", "http://www.example.com/", "-b"}}, + {FAIL, {"-osint", "-a", "-aleph", "http://www.example.com/", "-b"}}, + + // multiply-occurring otherwise-licit arguments + {FAIL, {"-osint", "-aleph", "-beth", "http://www.example.com/"}}, + {FAIL, {"-osint", "-aleph", "-aleph", "http://www.example.com/"}}, + + // if -osint is not supplied, anything goes + {PASS, {}}, + {PASS, {"http://www.example.com"}}, + {PASS, {"-aleph", "http://www.example.com/"}}, + {PASS, {"-beth", "http://www.example.com/"}}, + {PASS, {"-aleph", "http://www.example.com/", "-other-argument"}}, + {PASS, {"-aleph", "-aleph", "http://www.example.com/"}}, +}; + +constexpr static char const* const kOptionalArgs[] = {"mozilla", "allizom"}; + +// Additional test cases for optional parameters. +// +// Test cases marked PASS should pass iff the above optional parameters are +// permitted. (Test cases marked FAIL should fail regardless, and are only +// grouped here for convenience and semantic coherence.) +std::pair<TestCaseState, std::vector<const char*>> kCommandLinesOpt[] = { + // one permitted optional argument + {PASS, {"-osint", "-mozilla", "-aleph", "http://www.example.com/"}}, + {PASS, {"-osint", "-allizom", "-aleph", "http://www.example.com/"}}, + + // multiple permitted optional arguments + {PASS, + {"-osint", "-mozilla", "-allizom", "-aleph", "http://www.example.com/"}}, + {PASS, + {"-osint", "-allizom", "-mozilla", "-aleph", "http://www.example.com/"}}, + + // optional arguments in the wrong place + {FAIL, {"-mozilla", "-osint", "-aleph", "http://www.example.com/"}}, + {FAIL, {"-osint", "-aleph", "-mozilla", "http://www.example.com/"}}, + {FAIL, {"-osint", "-aleph", "http://www.example.com/", "-mozilla"}}, + + // optional arguments in both right and wrong places + {FAIL, + {"-mozilla", "-osint", "-allizom", "-aleph", "http://www.example.com/"}}, + {FAIL, + {"-osint", "-allizom", "-aleph", "-mozilla", "http://www.example.com/"}}, + {FAIL, + {"-osint", "-allizom", "-aleph", "http://www.example.com/", "-mozilla"}}, +}; + +enum WithOptionalState : bool { + NoOptionalArgs = false, + WithOptionalArgs = true, +}; + +template <typename CharT> +bool TestCommandLineImpl(CommandLine const& cl, + WithOptionalState withOptional) { + int argc = cl.argc(); + // EnsureCommandlineSafe's signature isn't const-correct here for annoying + // reasons, but it is indeed non-mutating. + CharT** argv = const_cast<CharT**>(cl.argv<CharT>()); + if (withOptional) { + return mozilla::internal::EnsureCommandlineSafeImpl( + argc, argv, kRequiredArgs, kOptionalArgs); + } + return mozilla::internal::EnsureCommandlineSafeImpl(argc, argv, + kRequiredArgs); +} + +// Test that `args` produces `expectation`. On Windows, test against both +// wide-character and narrow-character implementations. +void TestCommandLine(TestCaseState expectation, CommandLine const& cl, + WithOptionalState withOptional) { + EXPECT_EQ(TestCommandLineImpl<char>(cl, withOptional), expectation) + << "cl is: " << cl << std::endl + << "withOptional is: " << bool(withOptional); +#ifdef XP_WIN + EXPECT_EQ(TestCommandLineImpl<wchar_t>(cl, withOptional), expectation) + << "cl is: " << cl << std::endl + << "withOptional is: " << bool(withOptional); +#endif +} +} // namespace testzilla + +/*********************** + * Test cases + */ + +TEST(CmdLineAndEnvUtils, strimatch) +{ + using namespace testzilla; + using mozilla::strimatch; + for (auto const& [result, data] : kStrMatches8) { + auto const& [left, right] = data; + EXPECT_EQ(strimatch(left, right), result) + << '<' << left << "> !~ <" << right << '>'; + +#ifdef XP_WIN + wchar_t right_wide[200]; + ::mbstowcs(right_wide, right, 200); + EXPECT_EQ(strimatch(left, right_wide), result) + << '<' << left << "> !~ L<" << right << '>'; +#endif + } + +#ifdef XP_WIN + for (auto const& [result, data] : kStrMatches16) { + auto const& [left, right] = data; + EXPECT_EQ(strimatch(left, right), result) + << '<' << left << "> !~ L<" << right << '>'; + } +#endif +} + +TEST(CmdLineAndEnvUtils, ensureSafe) +{ + using namespace testzilla; + for (auto const& [result, data] : kCommandLines) { + CommandLine const cl(data); + TestCommandLine(result, cl, NoOptionalArgs); + } + for (auto const& [_unused, data] : kCommandLinesOpt) { + MOZ_UNUSED(_unused); // silence gcc + CommandLine const cl(data); + TestCommandLine(FAIL, cl, NoOptionalArgs); + } +} + +TEST(CmdLineAndEnvUtils, ensureSafeWithOptional) +{ + using namespace testzilla; + for (auto const& [result, data] : kCommandLines) { + CommandLine const cl(data); + TestCommandLine(result, cl, WithOptionalArgs); + } + for (auto const& [result, data] : kCommandLinesOpt) { + CommandLine const cl(data); + TestCommandLine(result, cl, WithOptionalArgs); + } +} 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/TestGeckoArgs.cpp b/toolkit/xre/test/gtest/TestGeckoArgs.cpp new file mode 100644 index 0000000000..42c7a41b2b --- /dev/null +++ b/toolkit/xre/test/gtest/TestGeckoArgs.cpp @@ -0,0 +1,179 @@ +/* -*- 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/ArrayUtils.h" +#include "mozilla/GeckoArgs.h" + +using namespace mozilla; +using namespace mozilla::geckoargs; + +static CommandLineArg<const char*> kCharParam{"-charParam", "charparam"}; +static CommandLineArg<uint64_t> kUint64Param{"-Uint64Param", "uint64param"}; +static CommandLineArg<bool> kFlag{"-Flag", "flag"}; + +template <size_t N> +bool CheckArgv(char** aArgv, const char* const (&aExpected)[N]) { + for (size_t i = 0; i < N; ++i) { + if (aArgv[i] == nullptr && aExpected[i] == nullptr) { + return true; + } + if (aArgv[i] == nullptr || aExpected[i] == nullptr) { + return false; + } + if (strcmp(aArgv[i], aExpected[i]) != 0) { + return false; + } + } + return true; +} + +char kFirefox[] = "$HOME/bin/firefox/firefox-bin"; + +TEST(GeckoArgs, const_char_ptr) +{ + char kCharParamStr[] = "-charParam"; + char kCharParamValue[] = "paramValue"; + + { + char* argv[] = {kFirefox, kCharParamStr, kCharParamValue, nullptr}; + int argc = ArrayLength(argv); + EXPECT_EQ(argc, 4); + + Maybe<const char*> charParam = kCharParam.Get(argc, argv); + EXPECT_TRUE(charParam.isSome()); + EXPECT_EQ(*charParam, kCharParamValue); + + const char* const expArgv[] = {kFirefox, nullptr}; + EXPECT_TRUE(CheckArgv(argv, expArgv)); + } + { + char kBlahBlah[] = "-blahblah"; + char* argv[] = {kFirefox, kCharParamStr, kBlahBlah, nullptr}; + int argc = ArrayLength(argv); + EXPECT_EQ(argc, 4); + + Maybe<const char*> charParam = kCharParam.Get(argc, argv); + EXPECT_TRUE(charParam.isNothing()); + + const char* const expArgv[] = {kFirefox, kBlahBlah, nullptr}; + EXPECT_TRUE(CheckArgv(argv, expArgv)); + } + { + std::vector<std::string> extraArgs; + EXPECT_EQ(extraArgs.size(), 0U); + kCharParam.Put("ParamValue", extraArgs); + EXPECT_EQ(extraArgs.size(), 2U); + EXPECT_EQ(extraArgs[0], "-charParam"); + EXPECT_EQ(extraArgs[1], "ParamValue"); + } + { EXPECT_EQ(kCharParam.Name(), "-charParam"); } +} + +TEST(GeckoArgs, uint64) +{ + char kUint64ParamStr[] = "-Uint64Param"; + + { + char* argv[] = {kFirefox, kUint64ParamStr, nullptr}; + int argc = ArrayLength(argv); + EXPECT_EQ(argc, 3); + + Maybe<uint64_t> uint64Param = kUint64Param.Get(argc, argv); + EXPECT_TRUE(uint64Param.isNothing()); + + const char* const expArgv[] = {kFirefox, nullptr}; + EXPECT_TRUE(CheckArgv(argv, expArgv)); + } + { + char* argv[] = {kFirefox, nullptr}; + int argc = ArrayLength(argv); + EXPECT_EQ(argc, 2); + + Maybe<uint64_t> uint64Param = kUint64Param.Get(argc, argv); + EXPECT_TRUE(uint64Param.isNothing()); + + const char* const expArgv[] = {kFirefox, nullptr}; + EXPECT_TRUE(CheckArgv(argv, expArgv)); + } + { + char kUint64ParamValue[] = "42"; + char* argv[] = {kFirefox, kUint64ParamStr, kUint64ParamValue, nullptr}; + int argc = ArrayLength(argv); + EXPECT_EQ(argc, 4); + + Maybe<uint64_t> uint64Param = kUint64Param.Get(argc, argv); + EXPECT_TRUE(uint64Param.isSome()); + EXPECT_EQ(*uint64Param, 42U); + + const char* const expArgv[] = {kFirefox, nullptr}; + EXPECT_TRUE(CheckArgv(argv, expArgv)); + } + { + char kUint64ParamValue[] = "aa"; + char* argv[] = {kFirefox, kUint64ParamStr, kUint64ParamValue, nullptr}; + int argc = ArrayLength(argv); + EXPECT_EQ(argc, 4); + + Maybe<uint64_t> uint64Param = kUint64Param.Get(argc, argv); + EXPECT_TRUE(uint64Param.isNothing()); + + const char* const expArgv[] = {kFirefox, nullptr}; + EXPECT_TRUE(CheckArgv(argv, expArgv)); + } + { + std::vector<std::string> extraArgs; + EXPECT_EQ(extraArgs.size(), 0U); + kUint64Param.Put(1234, extraArgs); + EXPECT_EQ(extraArgs.size(), 2U); + EXPECT_EQ(extraArgs[0], "-Uint64Param"); + EXPECT_EQ(extraArgs[1], "1234"); + } + { EXPECT_EQ(kUint64Param.Name(), "-Uint64Param"); } +} + +TEST(GeckoArgs, bool) +{ + char kFlagStr[] = "-Flag"; + + { + char* argv[] = {kFirefox, kFlagStr, nullptr}; + int argc = ArrayLength(argv); + EXPECT_EQ(argc, 3); + + Maybe<bool> Flag = kFlag.Get(argc, argv); + EXPECT_TRUE(Flag.isSome()); + EXPECT_TRUE(*Flag); + + const char* const expArgv[] = {kFirefox, nullptr}; + EXPECT_TRUE(CheckArgv(argv, expArgv)); + } + { + char* argv[] = {kFirefox, nullptr}; + int argc = ArrayLength(argv); + EXPECT_EQ(argc, 2); + + Maybe<bool> Flag = kFlag.Get(argc, argv); + EXPECT_TRUE(Flag.isNothing()); + + const char* const expArgv[] = {kFirefox, nullptr}; + EXPECT_TRUE(CheckArgv(argv, expArgv)); + } + { + std::vector<std::string> extraArgs; + EXPECT_EQ(extraArgs.size(), 0U); + kFlag.Put(true, extraArgs); + EXPECT_EQ(extraArgs.size(), 1U); + EXPECT_EQ(extraArgs[0], "-Flag"); + } + { + std::vector<std::string> extraArgs; + EXPECT_EQ(extraArgs.size(), 0U); + kFlag.Put(false, extraArgs); + EXPECT_EQ(extraArgs.size(), 0U); + } + { EXPECT_EQ(kFlag.Name(), "-Flag"); } +} diff --git a/toolkit/xre/test/gtest/moz.build b/toolkit/xre/test/gtest/moz.build new file mode 100644 index 0000000000..bec881fa72 --- /dev/null +++ b/toolkit/xre/test/gtest/moz.build @@ -0,0 +1,24 @@ +# -*- 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 = [ + "TestCmdLineAndEnvUtils.cpp", + "TestCompatVersionCompare.cpp", + "TestGeckoArgs.cpp", +] + +LOCAL_INCLUDES += [ + "/toolkit/components/remote", +] + +if CONFIG["OS_TARGET"] == "WINNT": + UNIFIED_SOURCES += [ + "TestAssembleCommandLineWin.cpp", + ] + +FINAL_LIBRARY = "xul-gtest" |