#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "base/command_line.h"
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "base/debug/debugging_buildflags.h"
#include "base/files/file_path.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include <shellapi.h>
#include "base/win/scoped_localalloc.h"
#endif
#if BUILDFLAG(ENABLE_COMMANDLINE_SEQUENCE_CHECKS)
#include "base/run_loop.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#endif
namespace base {
#if BUILDFLAG(IS_WIN)
static const CommandLine::StringType kTrickyQuoted =
FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\"");
#endif
static const CommandLine::StringType kTricky = …);
TEST(CommandLineTest, CommandLineConstructor) { … }
TEST(CommandLineTest, CommandLineFromArgvWithoutProgram) { … }
TEST(CommandLineTest, CommandLineFromString) { … }
TEST(CommandLineTest, EmptyString) { … }
TEST(CommandLineTest, GetArgumentsString) { … }
TEST(CommandLineTest, AppendSwitches) { … }
TEST(CommandLineTest, AppendSwitchesDashDash) { … }
#if BUILDFLAG(IS_WIN)
struct CommandLineQuoteTestCase {
const wchar_t* const input_arg;
const wchar_t* const expected_output_arg;
};
class CommandLineQuoteTest
: public ::testing::TestWithParam<CommandLineQuoteTestCase> {};
INSTANTIATE_TEST_SUITE_P(
CommandLineQuoteTestCases,
CommandLineQuoteTest,
::testing::ValuesIn(std::vector<CommandLineQuoteTestCase>{
{L"", L""},
{L"abc = xyz", LR"("abc = xyz")"},
{LR"(C:\AppData\Local\setup.exe)", LR"("C:\AppData\Local\setup.exe")"},
{LR"(C:\Program Files\setup.exe)", LR"("C:\Program Files\setup.exe")"},
{LR"("C:\Program Files\setup.exe")",
LR"("\"C:\Program Files\setup.exe\"")"},
}));
TEST_P(CommandLineQuoteTest, TestCases) {
EXPECT_EQ(CommandLine::QuoteForCommandLineToArgvW(GetParam().input_arg),
GetParam().expected_output_arg);
}
struct CommandLineQuoteAfterTestCase {
const std::vector<std::wstring> input_args;
const wchar_t* const expected_output;
};
class CommandLineQuoteAfterTest
: public ::testing::TestWithParam<CommandLineQuoteAfterTestCase> {};
INSTANTIATE_TEST_SUITE_P(
CommandLineQuoteAfterTestCases,
CommandLineQuoteAfterTest,
::testing::ValuesIn(std::vector<CommandLineQuoteAfterTestCase>{
{{L"abc=1"}, L"abc=1"},
{{L"abc=1", L"xyz=2"}, L"abc=1 xyz=2"},
{{L"abc=1", L"xyz=2", L"q"}, L"abc=1 xyz=2 q"},
{{L" abc=1 ", L" xyz=2", L"q "}, L"abc=1 xyz=2 q"},
{{LR"("abc = 1")"}, LR"("abc = 1")"},
{{LR"(abc" = "1)", L"xyz=2"}, LR"("abc = 1" xyz=2)"},
{{LR"(abc" = "1)"}, LR"("abc = 1")"},
{{LR"(\\)", LR"(\\\")"}, LR"("\\\\" "\\\"")"},
}));
TEST_P(CommandLineQuoteAfterTest, TestCases) {
std::wstring input_command_line =
base::StrCat({LR"(c:\test\process.exe )",
base::JoinString(GetParam().input_args, L" ")});
int num_args = 0;
base::win::ScopedLocalAllocTyped<wchar_t*> argv(
::CommandLineToArgvW(&input_command_line[0], &num_args));
ASSERT_EQ(num_args - 1U, GetParam().input_args.size());
std::wstring recreated_command_line;
for (int i = 1; i < num_args; ++i) {
recreated_command_line.append(
CommandLine::QuoteForCommandLineToArgvW(argv.get()[i]));
if (i + 1 < num_args) {
recreated_command_line.push_back(L' ');
}
}
EXPECT_EQ(recreated_command_line, GetParam().expected_output);
}
TEST(CommandLineTest, GetCommandLineStringForShell) {
CommandLine cl = CommandLine::FromString(
FILE_PATH_LITERAL("program --switch /switch2 --"));
EXPECT_EQ(
cl.GetCommandLineStringForShell(),
FILE_PATH_LITERAL("program --switch /switch2 -- --single-argument %1"));
}
TEST(CommandLineTest, GetCommandLineStringWithUnsafeInsertSequences) {
CommandLine cl(FilePath(FILE_PATH_LITERAL("program")));
cl.AppendSwitchASCII("switch", "%1");
cl.AppendSwitch("%2");
cl.AppendArg("%3");
EXPECT_EQ(FILE_PATH_LITERAL("program --switch=%1 --%2 %3"),
cl.GetCommandLineStringWithUnsafeInsertSequences());
}
TEST(CommandLineTest, HasSingleArgument) {
CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
cl.AppendSwitchASCII("switch2", "foo");
EXPECT_FALSE(cl.HasSingleArgumentSwitch());
CommandLine cl_for_shell(
CommandLine::FromString(cl.GetCommandLineStringForShell()));
EXPECT_TRUE(cl_for_shell.HasSingleArgumentSwitch());
}
TEST(CommandLineTest, MaintainSingleArgument) {
static const CommandLine::StringType kCommandLine =
FILE_PATH_LITERAL("program --switch --single-argument foo bar.html");
CommandLine cl = CommandLine::FromString(kCommandLine);
CommandLine cl_for_shell = CommandLine::FromString(cl.GetCommandLineString());
EXPECT_TRUE(cl_for_shell.HasSingleArgumentSwitch());
EXPECT_EQ(kCommandLine, cl_for_shell.GetCommandLineString());
}
#endif
TEST(CommandLineTest, AppendArguments) { … }
#if BUILDFLAG(IS_WIN)
TEST(CommandLineTest, ProgramQuotes) {
const FilePath kProgram(L"Program");
CommandLine cl_program(kProgram);
EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value());
EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString());
const FilePath kProgramPath(L"Program Path");
CommandLine cl_program_path(kProgramPath);
EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value());
CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString());
EXPECT_EQ(L"\"Program Path\"", cmd_string);
}
#endif
TEST(CommandLineTest, Init) { … }
TEST(CommandLineTest, Copy) { … }
TEST(CommandLineTest, CopySwitches) { … }
TEST(CommandLineTest, Move) { … }
TEST(CommandLineTest, PrependSimpleWrapper) { … }
TEST(CommandLineTest, PrependComplexWrapper) { … }
TEST(CommandLineTest, RemoveSwitch) { … }
TEST(CommandLineTest, RemoveSwitchWithValue) { … }
TEST(CommandLineTest, RemoveSwitchDropsMultipleSameSwitches) { … }
TEST(CommandLineTest, AppendAndRemoveSwitchWithDefaultPrefix) { … }
TEST(CommandLineTest, AppendAndRemoveSwitchWithAlternativePrefix) { … }
TEST(CommandLineTest, AppendAndRemoveSwitchPreservesOtherSwitchesAndArgs) { … }
TEST(CommandLineTest, MultipleSameSwitch) { … }
class MergeDuplicateFoosSemicolon : public DuplicateSwitchHandler { … };
MergeDuplicateFoosSemicolon::~MergeDuplicateFoosSemicolon() = default;
void MergeDuplicateFoosSemicolon::ResolveDuplicate(
std::string_view key,
CommandLine::StringViewType new_value,
CommandLine::StringType& out_value) { … }
TEST(CommandLineTest, MultipleFilterFileSwitch) { … }
#if BUILDFLAG(IS_WIN)
TEST(CommandLineTest, ParseAsSingleArgument) {
CommandLine cl = CommandLine::FromString(
FILE_PATH_LITERAL("program --switch_before arg_before "
"--single-argument arg with spaces \"and quotes\" \""));
EXPECT_FALSE(cl.GetCommandLineString().empty());
EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")), cl.GetProgram());
EXPECT_TRUE(cl.HasSwitch("switch_before"));
EXPECT_EQ(cl.GetArgs(), CommandLine::StringVector({FILE_PATH_LITERAL(
"arg with spaces \"and quotes\" \"")}));
CommandLine cl_without_arg =
CommandLine::FromString(FILE_PATH_LITERAL("program --single-argument "));
EXPECT_FALSE(cl_without_arg.GetCommandLineString().empty());
EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")),
cl_without_arg.GetProgram());
EXPECT_TRUE(cl_without_arg.GetArgs().empty());
}
#endif
#if BUILDFLAG(ENABLE_COMMANDLINE_SEQUENCE_CHECKS)
TEST(CommandLineDeathTest, ThreadChecks) { … }
#endif
}