1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00
llvm-mirror/unittests/Support/CommandLineTest.cpp
Chandler Carruth ae65e281f3 Update the file headers across all of the LLVM projects in the monorepo
to reflect the new license.

We understand that people may be surprised that we're moving the header
entirely to discuss the new license. We checked this carefully with the
Foundation's lawyer and we believe this is the correct approach.

Essentially, all code in the project is now made available by the LLVM
project under our new license, so you will see that the license headers
include that license only. Some of our contributors have contributed
code under our old license, and accordingly, we have retained a copy of
our old license notice in the top-level files in each project and
repository.

llvm-svn: 351636
2019-01-19 08:50:56 +00:00

917 lines
30 KiB
C++

//===- llvm/unittest/Support/CommandLineTest.cpp - CommandLine tests ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/CommandLine.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Config/config.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/StringSaver.h"
#include "gtest/gtest.h"
#include <fstream>
#include <stdlib.h>
#include <string>
using namespace llvm;
namespace {
class TempEnvVar {
public:
TempEnvVar(const char *name, const char *value)
: name(name) {
const char *old_value = getenv(name);
EXPECT_EQ(nullptr, old_value) << old_value;
#if HAVE_SETENV
setenv(name, value, true);
#else
# define SKIP_ENVIRONMENT_TESTS
#endif
}
~TempEnvVar() {
#if HAVE_SETENV
// Assume setenv and unsetenv come together.
unsetenv(name);
#else
(void)name; // Suppress -Wunused-private-field.
#endif
}
private:
const char *const name;
};
template <typename T, typename Base = cl::opt<T>>
class StackOption : public Base {
public:
template <class... Ts>
explicit StackOption(Ts &&... Ms) : Base(std::forward<Ts>(Ms)...) {}
~StackOption() override { this->removeArgument(); }
template <class DT> StackOption<T> &operator=(const DT &V) {
this->setValue(V);
return *this;
}
};
class StackSubCommand : public cl::SubCommand {
public:
StackSubCommand(StringRef Name,
StringRef Description = StringRef())
: SubCommand(Name, Description) {}
StackSubCommand() : SubCommand() {}
~StackSubCommand() { unregisterSubCommand(); }
};
cl::OptionCategory TestCategory("Test Options", "Description");
TEST(CommandLineTest, ModifyExisitingOption) {
StackOption<int> TestOption("test-option", cl::desc("old description"));
static const char Description[] = "New description";
static const char ArgString[] = "new-test-option";
static const char ValueString[] = "Integer";
StringMap<cl::Option *> &Map =
cl::getRegisteredOptions(*cl::TopLevelSubCommand);
ASSERT_TRUE(Map.count("test-option") == 1) <<
"Could not find option in map.";
cl::Option *Retrieved = Map["test-option"];
ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";
ASSERT_EQ(&cl::GeneralCategory,Retrieved->Category) <<
"Incorrect default option category.";
Retrieved->setCategory(TestCategory);
ASSERT_EQ(&TestCategory,Retrieved->Category) <<
"Failed to modify option's option category.";
Retrieved->setDescription(Description);
ASSERT_STREQ(Retrieved->HelpStr.data(), Description)
<< "Changing option description failed.";
Retrieved->setArgStr(ArgString);
ASSERT_STREQ(ArgString, Retrieved->ArgStr.data())
<< "Failed to modify option's Argument string.";
Retrieved->setValueStr(ValueString);
ASSERT_STREQ(Retrieved->ValueStr.data(), ValueString)
<< "Failed to modify option's Value string.";
Retrieved->setHiddenFlag(cl::Hidden);
ASSERT_EQ(cl::Hidden, TestOption.getOptionHiddenFlag()) <<
"Failed to modify option's hidden flag.";
}
#ifndef SKIP_ENVIRONMENT_TESTS
const char test_env_var[] = "LLVM_TEST_COMMAND_LINE_FLAGS";
cl::opt<std::string> EnvironmentTestOption("env-test-opt");
TEST(CommandLineTest, ParseEnvironment) {
TempEnvVar TEV(test_env_var, "-env-test-opt=hello");
EXPECT_EQ("", EnvironmentTestOption);
cl::ParseEnvironmentOptions("CommandLineTest", test_env_var);
EXPECT_EQ("hello", EnvironmentTestOption);
}
// This test used to make valgrind complain
// ("Conditional jump or move depends on uninitialised value(s)")
//
// Warning: Do not run any tests after this one that try to gain access to
// registered command line options because this will likely result in a
// SEGFAULT. This can occur because the cl::opt in the test below is declared
// on the stack which will be destroyed after the test completes but the
// command line system will still hold a pointer to a deallocated cl::Option.
TEST(CommandLineTest, ParseEnvironmentToLocalVar) {
// Put cl::opt on stack to check for proper initialization of fields.
StackOption<std::string> EnvironmentTestOptionLocal("env-test-opt-local");
TempEnvVar TEV(test_env_var, "-env-test-opt-local=hello-local");
EXPECT_EQ("", EnvironmentTestOptionLocal);
cl::ParseEnvironmentOptions("CommandLineTest", test_env_var);
EXPECT_EQ("hello-local", EnvironmentTestOptionLocal);
}
#endif // SKIP_ENVIRONMENT_TESTS
TEST(CommandLineTest, UseOptionCategory) {
StackOption<int> TestOption2("test-option", cl::cat(TestCategory));
ASSERT_EQ(&TestCategory,TestOption2.Category) << "Failed to assign Option "
"Category.";
}
typedef void ParserFunction(StringRef Source, StringSaver &Saver,
SmallVectorImpl<const char *> &NewArgv,
bool MarkEOLs);
void testCommandLineTokenizer(ParserFunction *parse, StringRef Input,
const char *const Output[], size_t OutputSize) {
SmallVector<const char *, 0> Actual;
BumpPtrAllocator A;
StringSaver Saver(A);
parse(Input, Saver, Actual, /*MarkEOLs=*/false);
EXPECT_EQ(OutputSize, Actual.size());
for (unsigned I = 0, E = Actual.size(); I != E; ++I) {
if (I < OutputSize) {
EXPECT_STREQ(Output[I], Actual[I]);
}
}
}
TEST(CommandLineTest, TokenizeGNUCommandLine) {
const char Input[] =
"foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' -DFOO=bar\\(\\) "
"foo\"bar\"baz C:\\\\src\\\\foo.cpp \"C:\\src\\foo.cpp\"";
const char *const Output[] = {
"foo bar", "foo bar", "foo bar", "foo\\bar",
"-DFOO=bar()", "foobarbaz", "C:\\src\\foo.cpp", "C:srcfoo.cpp"};
testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeWindowsCommandLine) {
const char Input[] = "a\\b c\\\\d e\\\\\"f g\" h\\\"i j\\\\\\\"k \"lmn\" o pqr "
"\"st \\\"u\" \\v";
const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k",
"lmn", "o", "pqr", "st \"u", "\\v" };
testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile1) {
const char *Input = "\\";
const char *const Output[] = { "\\" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile2) {
const char *Input = "\\abc";
const char *const Output[] = { "abc" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile3) {
const char *Input = "abc\\";
const char *const Output[] = { "abc\\" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile4) {
const char *Input = "abc\\\n123";
const char *const Output[] = { "abc123" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile5) {
const char *Input = "abc\\\r\n123";
const char *const Output[] = { "abc123" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile6) {
const char *Input = "abc\\\n";
const char *const Output[] = { "abc" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile7) {
const char *Input = "abc\\\r\n";
const char *const Output[] = { "abc" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile8) {
SmallVector<const char *, 0> Actual;
BumpPtrAllocator A;
StringSaver Saver(A);
cl::tokenizeConfigFile("\\\n", Saver, Actual, /*MarkEOLs=*/false);
EXPECT_TRUE(Actual.empty());
}
TEST(CommandLineTest, TokenizeConfigFile9) {
SmallVector<const char *, 0> Actual;
BumpPtrAllocator A;
StringSaver Saver(A);
cl::tokenizeConfigFile("\\\r\n", Saver, Actual, /*MarkEOLs=*/false);
EXPECT_TRUE(Actual.empty());
}
TEST(CommandLineTest, TokenizeConfigFile10) {
const char *Input = "\\\nabc";
const char *const Output[] = { "abc" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, TokenizeConfigFile11) {
const char *Input = "\\\r\nabc";
const char *const Output[] = { "abc" };
testCommandLineTokenizer(cl::tokenizeConfigFile, Input, Output,
array_lengthof(Output));
}
TEST(CommandLineTest, AliasesWithArguments) {
static const size_t ARGC = 3;
const char *const Inputs[][ARGC] = {
{ "-tool", "-actual=x", "-extra" },
{ "-tool", "-actual", "x" },
{ "-tool", "-alias=x", "-extra" },
{ "-tool", "-alias", "x" }
};
for (size_t i = 0, e = array_lengthof(Inputs); i < e; ++i) {
StackOption<std::string> Actual("actual");
StackOption<bool> Extra("extra");
StackOption<std::string> Input(cl::Positional);
cl::alias Alias("alias", llvm::cl::aliasopt(Actual));
cl::ParseCommandLineOptions(ARGC, Inputs[i]);
EXPECT_EQ("x", Actual);
EXPECT_EQ(0, Input.getNumOccurrences());
Alias.removeArgument();
}
}
void testAliasRequired(int argc, const char *const *argv) {
StackOption<std::string> Option("option", cl::Required);
cl::alias Alias("o", llvm::cl::aliasopt(Option));
cl::ParseCommandLineOptions(argc, argv);
EXPECT_EQ("x", Option);
EXPECT_EQ(1, Option.getNumOccurrences());
Alias.removeArgument();
}
TEST(CommandLineTest, AliasRequired) {
const char *opts1[] = { "-tool", "-option=x" };
const char *opts2[] = { "-tool", "-o", "x" };
testAliasRequired(array_lengthof(opts1), opts1);
testAliasRequired(array_lengthof(opts2), opts2);
}
TEST(CommandLineTest, HideUnrelatedOptions) {
StackOption<int> TestOption1("hide-option-1");
StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory));
cl::HideUnrelatedOptions(TestCategory);
ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())
<< "Failed to hide extra option.";
ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
<< "Hid extra option that should be visable.";
StringMap<cl::Option *> &Map =
cl::getRegisteredOptions(*cl::TopLevelSubCommand);
ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
<< "Hid default option that should be visable.";
}
cl::OptionCategory TestCategory2("Test Options set 2", "Description");
TEST(CommandLineTest, HideUnrelatedOptionsMulti) {
StackOption<int> TestOption1("multi-hide-option-1");
StackOption<int> TestOption2("multi-hide-option-2", cl::cat(TestCategory));
StackOption<int> TestOption3("multi-hide-option-3", cl::cat(TestCategory2));
const cl::OptionCategory *VisibleCategories[] = {&TestCategory,
&TestCategory2};
cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())
<< "Failed to hide extra option.";
ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
<< "Hid extra option that should be visable.";
ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())
<< "Hid extra option that should be visable.";
StringMap<cl::Option *> &Map =
cl::getRegisteredOptions(*cl::TopLevelSubCommand);
ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
<< "Hid default option that should be visable.";
}
TEST(CommandLineTest, SetValueInSubcategories) {
cl::ResetCommandLineParser();
StackSubCommand SC1("sc1", "First subcommand");
StackSubCommand SC2("sc2", "Second subcommand");
StackOption<bool> TopLevelOpt("top-level", cl::init(false));
StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
EXPECT_FALSE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_FALSE(SC2Opt);
const char *args[] = {"prog", "-top-level"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
EXPECT_TRUE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_FALSE(SC2Opt);
TopLevelOpt = false;
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_FALSE(SC2Opt);
const char *args2[] = {"prog", "sc1", "-sc1"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));
EXPECT_FALSE(TopLevelOpt);
EXPECT_TRUE(SC1Opt);
EXPECT_FALSE(SC2Opt);
SC1Opt = false;
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_FALSE(SC2Opt);
const char *args3[] = {"prog", "sc2", "-sc2"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(3, args3, StringRef(), &llvm::nulls()));
EXPECT_FALSE(TopLevelOpt);
EXPECT_FALSE(SC1Opt);
EXPECT_TRUE(SC2Opt);
}
TEST(CommandLineTest, LookupFailsInWrongSubCommand) {
cl::ResetCommandLineParser();
StackSubCommand SC1("sc1", "First subcommand");
StackSubCommand SC2("sc2", "Second subcommand");
StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
std::string Errs;
raw_string_ostream OS(Errs);
const char *args[] = {"prog", "sc1", "-sc2"};
EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));
OS.flush();
EXPECT_FALSE(Errs.empty());
}
TEST(CommandLineTest, AddToAllSubCommands) {
cl::ResetCommandLineParser();
StackSubCommand SC1("sc1", "First subcommand");
StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands),
cl::init(false));
StackSubCommand SC2("sc2", "Second subcommand");
const char *args[] = {"prog", "-everywhere"};
const char *args2[] = {"prog", "sc1", "-everywhere"};
const char *args3[] = {"prog", "sc2", "-everywhere"};
std::string Errs;
raw_string_ostream OS(Errs);
EXPECT_FALSE(AllOpt);
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS));
EXPECT_TRUE(AllOpt);
AllOpt = false;
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(AllOpt);
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS));
EXPECT_TRUE(AllOpt);
AllOpt = false;
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(AllOpt);
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS));
EXPECT_TRUE(AllOpt);
// Since all parsing succeeded, the error message should be empty.
OS.flush();
EXPECT_TRUE(Errs.empty());
}
TEST(CommandLineTest, ReparseCommandLineOptions) {
cl::ResetCommandLineParser();
StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::TopLevelSubCommand),
cl::init(false));
const char *args[] = {"prog", "-top-level"};
EXPECT_FALSE(TopLevelOpt);
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
EXPECT_TRUE(TopLevelOpt);
TopLevelOpt = false;
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(TopLevelOpt);
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
EXPECT_TRUE(TopLevelOpt);
}
TEST(CommandLineTest, RemoveFromRegularSubCommand) {
cl::ResetCommandLineParser();
StackSubCommand SC("sc", "Subcommand");
StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false));
StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false));
const char *args[] = {"prog", "sc", "-remove-option"};
std::string Errs;
raw_string_ostream OS(Errs);
EXPECT_FALSE(RemoveOption);
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));
EXPECT_TRUE(RemoveOption);
OS.flush();
EXPECT_TRUE(Errs.empty());
RemoveOption.removeArgument();
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, StringRef(), &OS));
OS.flush();
EXPECT_FALSE(Errs.empty());
}
TEST(CommandLineTest, RemoveFromTopLevelSubCommand) {
cl::ResetCommandLineParser();
StackOption<bool> TopLevelRemove(
"top-level-remove", cl::sub(*cl::TopLevelSubCommand), cl::init(false));
StackOption<bool> TopLevelKeep(
"top-level-keep", cl::sub(*cl::TopLevelSubCommand), cl::init(false));
const char *args[] = {"prog", "-top-level-remove"};
EXPECT_FALSE(TopLevelRemove);
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
EXPECT_TRUE(TopLevelRemove);
TopLevelRemove.removeArgument();
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(
cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
}
TEST(CommandLineTest, RemoveFromAllSubCommands) {
cl::ResetCommandLineParser();
StackSubCommand SC1("sc1", "First Subcommand");
StackSubCommand SC2("sc2", "Second Subcommand");
StackOption<bool> RemoveOption("remove-option", cl::sub(*cl::AllSubCommands),
cl::init(false));
StackOption<bool> KeepOption("keep-option", cl::sub(*cl::AllSubCommands),
cl::init(false));
const char *args0[] = {"prog", "-remove-option"};
const char *args1[] = {"prog", "sc1", "-remove-option"};
const char *args2[] = {"prog", "sc2", "-remove-option"};
// It should work for all subcommands including the top-level.
EXPECT_FALSE(RemoveOption);
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));
EXPECT_TRUE(RemoveOption);
RemoveOption = false;
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(RemoveOption);
EXPECT_TRUE(
cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls()));
EXPECT_TRUE(RemoveOption);
RemoveOption = false;
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(RemoveOption);
EXPECT_TRUE(
cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));
EXPECT_TRUE(RemoveOption);
RemoveOption.removeArgument();
// It should not work for any subcommands including the top-level.
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(
cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(
cl::ParseCommandLineOptions(3, args1, StringRef(), &llvm::nulls()));
cl::ResetAllOptionOccurrences();
EXPECT_FALSE(
cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));
}
TEST(CommandLineTest, GetRegisteredSubcommands) {
cl::ResetCommandLineParser();
StackSubCommand SC1("sc1", "First Subcommand");
StackOption<bool> Opt1("opt1", cl::sub(SC1), cl::init(false));
StackSubCommand SC2("sc2", "Second subcommand");
StackOption<bool> Opt2("opt2", cl::sub(SC2), cl::init(false));
const char *args0[] = {"prog", "sc1"};
const char *args1[] = {"prog", "sc2"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args0, StringRef(), &llvm::nulls()));
EXPECT_FALSE(Opt1);
EXPECT_FALSE(Opt2);
for (auto *S : cl::getRegisteredSubcommands()) {
if (*S) {
EXPECT_EQ("sc1", S->getName());
}
}
cl::ResetAllOptionOccurrences();
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args1, StringRef(), &llvm::nulls()));
EXPECT_FALSE(Opt1);
EXPECT_FALSE(Opt2);
for (auto *S : cl::getRegisteredSubcommands()) {
if (*S) {
EXPECT_EQ("sc2", S->getName());
}
}
}
TEST(CommandLineTest, ArgumentLimit) {
std::string args(32 * 4096, 'a');
EXPECT_FALSE(llvm::sys::commandLineFitsWithinSystemLimits("cl", args.data()));
}
TEST(CommandLineTest, ResponseFileWindows) {
if (!Triple(sys::getProcessTriple()).isOSWindows())
return;
StackOption<std::string, cl::list<std::string>> InputFilenames(
cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore);
StackOption<bool> TopLevelOpt("top-level", cl::init(false));
// Create response file.
int FileDescriptor;
SmallString<64> TempPath;
std::error_code EC =
llvm::sys::fs::createTemporaryFile("resp-", ".txt", FileDescriptor, TempPath);
EXPECT_TRUE(!EC);
std::ofstream RspFile(TempPath.c_str());
EXPECT_TRUE(RspFile.is_open());
RspFile << "-top-level\npath\\dir\\file1\npath/dir/file2";
RspFile.close();
llvm::SmallString<128> RspOpt;
RspOpt.append(1, '@');
RspOpt.append(TempPath.c_str());
const char *args[] = {"prog", RspOpt.c_str()};
EXPECT_FALSE(TopLevelOpt);
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
EXPECT_TRUE(TopLevelOpt);
EXPECT_TRUE(InputFilenames[0] == "path\\dir\\file1");
EXPECT_TRUE(InputFilenames[1] == "path/dir/file2");
llvm::sys::fs::remove(TempPath.c_str());
}
TEST(CommandLineTest, ResponseFiles) {
llvm::SmallString<128> TestDir;
std::error_code EC =
llvm::sys::fs::createUniqueDirectory("unittest", TestDir);
EXPECT_TRUE(!EC);
// Create included response file of first level.
llvm::SmallString<128> IncludedFileName;
llvm::sys::path::append(IncludedFileName, TestDir, "resp1");
std::ofstream IncludedFile(IncludedFileName.c_str());
EXPECT_TRUE(IncludedFile.is_open());
IncludedFile << "-option_1 -option_2\n"
"@incdir/resp2\n"
"-option_3=abcd\n";
IncludedFile.close();
// Directory for included file.
llvm::SmallString<128> IncDir;
llvm::sys::path::append(IncDir, TestDir, "incdir");
EC = llvm::sys::fs::create_directory(IncDir);
EXPECT_TRUE(!EC);
// Create included response file of second level.
llvm::SmallString<128> IncludedFileName2;
llvm::sys::path::append(IncludedFileName2, IncDir, "resp2");
std::ofstream IncludedFile2(IncludedFileName2.c_str());
EXPECT_TRUE(IncludedFile2.is_open());
IncludedFile2 << "-option_21 -option_22\n";
IncludedFile2 << "-option_23=abcd\n";
IncludedFile2.close();
// Prepare 'file' with reference to response file.
SmallString<128> IncRef;
IncRef.append(1, '@');
IncRef.append(IncludedFileName.c_str());
llvm::SmallVector<const char *, 4> Argv =
{ "test/test", "-flag_1", IncRef.c_str(), "-flag_2" };
// Expand response files.
llvm::BumpPtrAllocator A;
llvm::StringSaver Saver(A);
bool Res = llvm::cl::ExpandResponseFiles(
Saver, llvm::cl::TokenizeGNUCommandLine, Argv, false, true);
EXPECT_TRUE(Res);
EXPECT_EQ(Argv.size(), 9U);
EXPECT_STREQ(Argv[0], "test/test");
EXPECT_STREQ(Argv[1], "-flag_1");
EXPECT_STREQ(Argv[2], "-option_1");
EXPECT_STREQ(Argv[3], "-option_2");
EXPECT_STREQ(Argv[4], "-option_21");
EXPECT_STREQ(Argv[5], "-option_22");
EXPECT_STREQ(Argv[6], "-option_23=abcd");
EXPECT_STREQ(Argv[7], "-option_3=abcd");
EXPECT_STREQ(Argv[8], "-flag_2");
llvm::sys::fs::remove(IncludedFileName2);
llvm::sys::fs::remove(IncDir);
llvm::sys::fs::remove(IncludedFileName);
llvm::sys::fs::remove(TestDir);
}
TEST(CommandLineTest, SetDefautValue) {
cl::ResetCommandLineParser();
StackOption<std::string> Opt1("opt1", cl::init("true"));
StackOption<bool> Opt2("opt2", cl::init(true));
cl::alias Alias("alias", llvm::cl::aliasopt(Opt2));
StackOption<int> Opt3("opt3", cl::init(3));
const char *args[] = {"prog", "-opt1=false", "-opt2", "-opt3"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
EXPECT_TRUE(Opt1 == "false");
EXPECT_TRUE(Opt2);
EXPECT_TRUE(Opt3 == 3);
Opt2 = false;
Opt3 = 1;
cl::ResetAllOptionOccurrences();
for (auto &OM : cl::getRegisteredOptions(*cl::TopLevelSubCommand)) {
cl::Option *O = OM.second;
if (O->ArgStr == "opt2") {
continue;
}
O->setDefault();
}
EXPECT_TRUE(Opt1 == "true");
EXPECT_TRUE(Opt2);
EXPECT_TRUE(Opt3 == 3);
Alias.removeArgument();
}
TEST(CommandLineTest, ReadConfigFile) {
llvm::SmallVector<const char *, 1> Argv;
llvm::SmallString<128> TestDir;
std::error_code EC =
llvm::sys::fs::createUniqueDirectory("unittest", TestDir);
EXPECT_TRUE(!EC);
llvm::SmallString<128> TestCfg;
llvm::sys::path::append(TestCfg, TestDir, "foo");
std::ofstream ConfigFile(TestCfg.c_str());
EXPECT_TRUE(ConfigFile.is_open());
ConfigFile << "# Comment\n"
"-option_1\n"
"@subconfig\n"
"-option_3=abcd\n"
"-option_4=\\\n"
"cdef\n";
ConfigFile.close();
llvm::SmallString<128> TestCfg2;
llvm::sys::path::append(TestCfg2, TestDir, "subconfig");
std::ofstream ConfigFile2(TestCfg2.c_str());
EXPECT_TRUE(ConfigFile2.is_open());
ConfigFile2 << "-option_2\n"
"\n"
" # comment\n";
ConfigFile2.close();
// Make sure the current directory is not the directory where config files
// resides. In this case the code that expands response files will not find
// 'subconfig' unless it resolves nested inclusions relative to the including
// file.
llvm::SmallString<128> CurrDir;
EC = llvm::sys::fs::current_path(CurrDir);
EXPECT_TRUE(!EC);
EXPECT_TRUE(StringRef(CurrDir) != StringRef(TestDir));
llvm::BumpPtrAllocator A;
llvm::StringSaver Saver(A);
bool Result = llvm::cl::readConfigFile(TestCfg, Saver, Argv);
EXPECT_TRUE(Result);
EXPECT_EQ(Argv.size(), 4U);
EXPECT_STREQ(Argv[0], "-option_1");
EXPECT_STREQ(Argv[1], "-option_2");
EXPECT_STREQ(Argv[2], "-option_3=abcd");
EXPECT_STREQ(Argv[3], "-option_4=cdef");
llvm::sys::fs::remove(TestCfg2);
llvm::sys::fs::remove(TestCfg);
llvm::sys::fs::remove(TestDir);
}
TEST(CommandLineTest, PositionalEatArgsError) {
StackOption<std::string, cl::list<std::string>> PosEatArgs(
"positional-eat-args", cl::Positional, cl::desc("<arguments>..."),
cl::ZeroOrMore, cl::PositionalEatsArgs);
const char *args[] = {"prog", "-positional-eat-args=XXXX"};
const char *args2[] = {"prog", "-positional-eat-args=XXXX", "-foo"};
const char *args3[] = {"prog", "-positional-eat-args", "-foo"};
std::string Errs;
raw_string_ostream OS(Errs);
EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, StringRef(), &OS)); OS.flush();
EXPECT_FALSE(Errs.empty()); Errs.clear();
EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, StringRef(), &OS)); OS.flush();
EXPECT_FALSE(Errs.empty()); Errs.clear();
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, StringRef(), &OS)); OS.flush();
EXPECT_TRUE(Errs.empty());
}
#ifdef _WIN32
TEST(CommandLineTest, GetCommandLineArguments) {
int argc = __argc;
char **argv = __argv;
// GetCommandLineArguments is called in InitLLVM.
llvm::InitLLVM X(argc, argv);
EXPECT_EQ(llvm::sys::path::is_absolute(argv[0]),
llvm::sys::path::is_absolute(__argv[0]));
EXPECT_TRUE(llvm::sys::path::filename(argv[0])
.equals_lower("supporttests.exe"))
<< "Filename of test executable is "
<< llvm::sys::path::filename(argv[0]);
}
#endif
TEST(CommandLineTest, PrefixOptions) {
cl::ResetCommandLineParser();
StackOption<std::string, cl::list<std::string>> IncludeDirs(
"I", cl::Prefix, cl::desc("Declare an include directory"));
// Test non-prefixed variant works with cl::Prefix options.
EXPECT_TRUE(IncludeDirs.empty());
const char *args[] = {"prog", "-I=/usr/include"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args, StringRef(), &llvm::nulls()));
EXPECT_TRUE(IncludeDirs.size() == 1);
EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0);
IncludeDirs.erase(IncludeDirs.begin());
cl::ResetAllOptionOccurrences();
// Test non-prefixed variant works with cl::Prefix options when value is
// passed in following argument.
EXPECT_TRUE(IncludeDirs.empty());
const char *args2[] = {"prog", "-I", "/usr/include"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(3, args2, StringRef(), &llvm::nulls()));
EXPECT_TRUE(IncludeDirs.size() == 1);
EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0);
IncludeDirs.erase(IncludeDirs.begin());
cl::ResetAllOptionOccurrences();
// Test prefixed variant works with cl::Prefix options.
EXPECT_TRUE(IncludeDirs.empty());
const char *args3[] = {"prog", "-I/usr/include"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args3, StringRef(), &llvm::nulls()));
EXPECT_TRUE(IncludeDirs.size() == 1);
EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0);
StackOption<std::string, cl::list<std::string>> MacroDefs(
"D", cl::AlwaysPrefix, cl::desc("Define a macro"),
cl::value_desc("MACRO[=VALUE]"));
cl::ResetAllOptionOccurrences();
// Test non-prefixed variant does not work with cl::AlwaysPrefix options:
// equal sign is part of the value.
EXPECT_TRUE(MacroDefs.empty());
const char *args4[] = {"prog", "-D=HAVE_FOO"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args4, StringRef(), &llvm::nulls()));
EXPECT_TRUE(MacroDefs.size() == 1);
EXPECT_TRUE(MacroDefs.front().compare("=HAVE_FOO") == 0);
MacroDefs.erase(MacroDefs.begin());
cl::ResetAllOptionOccurrences();
// Test non-prefixed variant does not allow value to be passed in following
// argument with cl::AlwaysPrefix options.
EXPECT_TRUE(MacroDefs.empty());
const char *args5[] = {"prog", "-D", "HAVE_FOO"};
EXPECT_FALSE(
cl::ParseCommandLineOptions(3, args5, StringRef(), &llvm::nulls()));
EXPECT_TRUE(MacroDefs.empty());
cl::ResetAllOptionOccurrences();
// Test prefixed variant works with cl::AlwaysPrefix options.
EXPECT_TRUE(MacroDefs.empty());
const char *args6[] = {"prog", "-DHAVE_FOO"};
EXPECT_TRUE(
cl::ParseCommandLineOptions(2, args6, StringRef(), &llvm::nulls()));
EXPECT_TRUE(MacroDefs.size() == 1);
EXPECT_TRUE(MacroDefs.front().compare("HAVE_FOO") == 0);
}
} // anonymous namespace