diff options
Diffstat (limited to 'llvm/unittests/Support/CommandLineTest.cpp')
-rw-r--r-- | llvm/unittests/Support/CommandLineTest.cpp | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp index 416e0ee..5c95c9d 100644 --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -13,6 +13,7 @@ #include "llvm/Config/config.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #include "llvm/Support/StringSaver.h" @@ -839,4 +840,218 @@ TEST(CommandLineTest, GetCommandLineArguments) { } #endif +class OutputRedirector { +public: + OutputRedirector(int RedirectFD) + : RedirectFD(RedirectFD), OldFD(dup(RedirectFD)) { + if (OldFD == -1 || + sys::fs::createTemporaryFile("unittest-redirect", "", NewFD, + FilePath) || + dup2(NewFD, RedirectFD) == -1) + Valid = false; + } + + ~OutputRedirector() { + dup2(OldFD, RedirectFD); + close(OldFD); + close(NewFD); + } + + SmallVector<char, 128> FilePath; + bool Valid = true; + +private: + int RedirectFD; + int OldFD; + int NewFD; +}; + +struct AutoDeleteFile { + SmallVector<char, 128> FilePath; + ~AutoDeleteFile() { + if (!FilePath.empty()) + sys::fs::remove(std::string(FilePath.data(), FilePath.size())); + } +}; + +class PrintOptionInfoTest : public ::testing::Test { +public: + // Return std::string because the output of a failing EXPECT check is + // unreadable for StringRef. It also avoids any lifetime issues. + template <typename... Ts> std::string runTest(Ts... OptionAttributes) { + AutoDeleteFile File; + { + OutputRedirector Stdout(fileno(stdout)); + if (!Stdout.Valid) + return ""; + File.FilePath = Stdout.FilePath; + + StackOption<OptionValue> TestOption(Opt, cl::desc(HelpText), + OptionAttributes...); + printOptionInfo(TestOption, 25); + outs().flush(); + } + auto Buffer = MemoryBuffer::getFile(File.FilePath); + if (!Buffer) + return ""; + return Buffer->get()->getBuffer().str(); + } + + enum class OptionValue { Val }; + const StringRef Opt = "some-option"; + const StringRef HelpText = "some help"; + +private: + // This is a workaround for cl::Option sub-classes having their + // printOptionInfo functions private. + void printOptionInfo(const cl::Option &O, size_t Width) { + O.printOptionInfo(Width); + } +}; + +TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithoutSentinel) { + std::string Output = + runTest(cl::ValueOptional, + cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"))); + + // clang-format off + EXPECT_EQ(Output, (" -" + Opt + "=<value> - " + HelpText + "\n" + " =v1 - desc1\n") + .str()); + // clang-format on +} + +TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithSentinel) { + std::string Output = runTest( + cl::ValueOptional, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"), + clEnumValN(OptionValue::Val, "", ""))); + + // clang-format off + EXPECT_EQ(Output, + (" -" + Opt + " - " + HelpText + "\n" + " -" + Opt + "=<value> - " + HelpText + "\n" + " =v1 - desc1\n") + .str()); + // clang-format on +} + +TEST_F(PrintOptionInfoTest, PrintOptionInfoValueOptionalWithSentinelWithHelp) { + std::string Output = runTest( + cl::ValueOptional, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"), + clEnumValN(OptionValue::Val, "", "desc2"))); + + // clang-format off + EXPECT_EQ(Output, (" -" + Opt + " - " + HelpText + "\n" + " -" + Opt + "=<value> - " + HelpText + "\n" + " =v1 - desc1\n" + " =<empty> - desc2\n") + .str()); + // clang-format on +} + +TEST_F(PrintOptionInfoTest, PrintOptionInfoValueRequiredWithEmptyValueName) { + std::string Output = runTest( + cl::ValueRequired, cl::values(clEnumValN(OptionValue::Val, "v1", "desc1"), + clEnumValN(OptionValue::Val, "", ""))); + + // clang-format off + EXPECT_EQ(Output, (" -" + Opt + "=<value> - " + HelpText + "\n" + " =v1 - desc1\n" + " =<empty>\n") + .str()); + // clang-format on +} + +TEST_F(PrintOptionInfoTest, PrintOptionInfoEmptyValueDescription) { + std::string Output = runTest( + cl::ValueRequired, cl::values(clEnumValN(OptionValue::Val, "v1", ""))); + + // clang-format off + EXPECT_EQ(Output, + (" -" + Opt + "=<value> - " + HelpText + "\n" + " =v1\n").str()); + // clang-format on +} + +class GetOptionWidthTest : public ::testing::Test { +public: + enum class OptionValue { Val }; + + template <typename... Ts> + size_t runTest(StringRef ArgName, Ts... OptionAttributes) { + StackOption<OptionValue> TestOption(ArgName, cl::desc("some help"), + OptionAttributes...); + return getOptionWidth(TestOption); + } + +private: + // This is a workaround for cl::Option sub-classes having their + // printOptionInfo + // functions private. + size_t getOptionWidth(const cl::Option &O) { return O.getOptionWidth(); } +}; + +TEST_F(GetOptionWidthTest, GetOptionWidthArgNameLonger) { + StringRef ArgName("a-long-argument-name"); + Twine ExpectedStr = " -" + ArgName + "=<value> - "; + EXPECT_EQ( + runTest(ArgName, cl::values(clEnumValN(OptionValue::Val, "v", "help"))), + ExpectedStr.str().size()); +} + +TEST_F(GetOptionWidthTest, GetOptionWidthFirstOptionNameLonger) { + StringRef OptName("a-long-option-name"); + Twine ExpectedStr = " =" + OptName + " - "; + EXPECT_EQ( + runTest("a", cl::values(clEnumValN(OptionValue::Val, OptName, "help"), + clEnumValN(OptionValue::Val, "b", "help"))), + ExpectedStr.str().size()); +} + +TEST_F(GetOptionWidthTest, GetOptionWidthSecondOptionNameLonger) { + StringRef OptName("a-long-option-name"); + Twine ExpectedStr = " =" + OptName + " - "; + EXPECT_EQ( + runTest("a", cl::values(clEnumValN(OptionValue::Val, "b", "help"), + clEnumValN(OptionValue::Val, OptName, "help"))), + ExpectedStr.str().size()); +} + +TEST_F(GetOptionWidthTest, GetOptionWidthEmptyOptionNameLonger) { + Twine ExpectedStr = " =<empty> - "; + // The length of a=<value> (including indentation) is actually the same as the + // =<empty> string, so it is impossible to distinguish via testing the case + // where the empty string is picked from where the option name is picked. + EXPECT_EQ(runTest("a", cl::values(clEnumValN(OptionValue::Val, "b", "help"), + clEnumValN(OptionValue::Val, "", "help"))), + ExpectedStr.str().size()); +} + +TEST_F(GetOptionWidthTest, + GetOptionWidthValueOptionalEmptyOptionWithNoDescription) { + StringRef ArgName("a"); + // The length of a=<value> (including indentation) is actually the same as the + // =<empty> string, so it is impossible to distinguish via testing the case + // where the empty string is ignored from where it is not ignored. + // The dash will not actually be printed, but the space it would take up is + // included to ensure a consistent column width. + Twine ExpectedStr = " -" + ArgName + "=<value> - "; + EXPECT_EQ(runTest(ArgName, cl::ValueOptional, + cl::values(clEnumValN(OptionValue::Val, "value", "help"), + clEnumValN(OptionValue::Val, "", ""))), + ExpectedStr.str().size()); +} + +TEST_F(GetOptionWidthTest, + GetOptionWidthValueRequiredEmptyOptionWithNoDescription) { + // The length of a=<value> (including indentation) is actually the same as the + // =<empty> string, so it is impossible to distinguish via testing the case + // where the empty string is picked from where the option name is picked + Twine ExpectedStr = " =<empty> - "; + EXPECT_EQ(runTest("a", cl::ValueRequired, + cl::values(clEnumValN(OptionValue::Val, "value", "help"), + clEnumValN(OptionValue::Val, "", ""))), + ExpectedStr.str().size()); +} + } // anonymous namespace |