| //===- CommandLineTest.cpp ------------------------------------------------===// |
| // |
| // 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 "orc-rt-utils/CommandLine.h" |
| #include "orc-rt/Error.h" |
| #include "llvm/Testing/Support/Error.h" |
| #include "gtest/gtest.h" |
| |
| using namespace orc_rt; |
| |
| class CommandLineParserTest : public ::testing::Test { |
| protected: |
| std::string Host; |
| int Port = 0; |
| bool Verbose = false; |
| bool Help = false; |
| CommandLineParser Parser; |
| |
| void SetUp() override { |
| Parser.addValue("host", "Hostname", std::string("localhost"), Host, |
| CommandLineParser::OptionKind::Value, 'h'); |
| Parser.addFlag("help", "Display this help message", false, Help, '?'); |
| Parser.addValue("port", "Port number", 8080, Port, |
| CommandLineParser::OptionKind::Value, 'p'); |
| Parser.addFlag("verbose", "Enable verbose logging", false, Verbose, 'v'); |
| } |
| }; |
| |
| TEST_F(CommandLineParserTest, NoopTest) { |
| CommandLineParser Parser; |
| const char *Argv[] = {"appname"}; |
| auto Err = Parser.parse(1, const_cast<char **>(Argv)); |
| EXPECT_FALSE(!!Err); |
| } |
| |
| TEST_F(CommandLineParserTest, ValueRequired) { |
| const char *Argv[] = {"appname", "--host"}; |
| auto Err = Parser.parse(std::begin(Argv), std::end(Argv)); |
| if (!Err) { |
| ADD_FAILURE() << "--host requires a value, shouldn't succeed."; |
| } else { |
| orc_rt::consumeError(std::move(Err)); |
| } |
| } |
| |
| TEST_F(CommandLineParserTest, UnknownOption) { |
| const char *Argv[] = {"appname", "--unknown=foo"}; |
| auto Err = Parser.parse(std::begin(Argv), std::end(Argv)); |
| if (!Err) { |
| ADD_FAILURE() << "unknown option, shouldn't succeed."; |
| } else { |
| orc_rt::consumeError(std::move(Err)); |
| } |
| } |
| |
| TEST_F(CommandLineParserTest, InvalidInteger) { |
| const char *Argv[] = {"appname", "--port=not_a_number"}; |
| auto Err = Parser.parse(std::begin(Argv), std::end(Argv)); |
| if (!Err) { |
| ADD_FAILURE() << "Invalid integer, shouldn't succeed."; |
| } else { |
| orc_rt::consumeError(std::move(Err)); |
| } |
| } |
| |
| TEST_F(CommandLineParserTest, ParseFullConfiguration) { |
| const char *Argv[] = {"appname", "--host=example.com", "--port=8080", |
| "--verbose=true"}; |
| cantFail(Parser.parse(std::begin(Argv), std::end(Argv))); |
| EXPECT_EQ(Host, "example.com"); |
| EXPECT_EQ(Port, 8080); |
| EXPECT_EQ(Verbose, true); |
| } |
| |
| TEST_F(CommandLineParserTest, ShortFlagClustering) { |
| const char *Argv[] = {"appname", "-v?"}; |
| cantFail(Parser.parse(std::begin(Argv), std::end(Argv))); |
| EXPECT_TRUE(Verbose); |
| EXPECT_TRUE(Help); |
| } |
| |
| TEST_F(CommandLineParserTest, ShortFlagWithValue) { |
| const char *Argv[] = {"appname", "-p", "1234", "-hlocalhost"}; |
| cantFail(Parser.parse(std::begin(Argv), std::end(Argv))); |
| EXPECT_EQ(Port, 1234); |
| EXPECT_EQ(Host, "localhost"); |
| } |
| |
| TEST_F(CommandLineParserTest, ClusterWithValueAtEnd) { |
| const char *Argv[] = {"appname", "-vp9999"}; |
| cantFail(Parser.parse(std::begin(Argv), std::end(Argv))); |
| EXPECT_TRUE(Verbose); |
| EXPECT_EQ(Port, 9999); |
| } |
| |
| TEST_F(CommandLineParserTest, DoubleDashTerminatesOptionParsing) { |
| const char *Argv[] = {"appname", "-v", "--", "-p", "1234"}; |
| cantFail(Parser.parse(static_cast<int>(std::size(Argv)), |
| const_cast<char **>(Argv))); |
| |
| EXPECT_TRUE(Verbose); |
| EXPECT_EQ(Port, 8080); // Should remain default |
| ASSERT_EQ(Parser.positionals().size(), 2u); |
| EXPECT_EQ(Parser.positionals()[0], "-p"); |
| EXPECT_EQ(Parser.positionals()[1], "1234"); |
| } |
| |
| TEST_F(CommandLineParserTest, PrintHelpAlignmentWithShortFlags) { |
| std::string LogFile; |
| Parser.addValue("log-file", "Path to log", std::string("out.log"), LogFile); |
| |
| std::stringstream SS; |
| Parser.printHelp(SS, "appname"); |
| std::string Result = SS.str(); |
| |
| auto GetColumn = [&](std::string_view SearchTerm) -> size_t { |
| size_t Pos = Result.find(SearchTerm); |
| if (Pos == std::string::npos) |
| return 0; |
| size_t LineStart = Result.rfind('\n', Pos); |
| return (LineStart == std::string::npos) ? Pos : (Pos - LineStart - 1); |
| }; |
| |
| size_t PortDescCol = GetColumn("Port number"); |
| size_t LogDescCol = GetColumn("Path to log"); |
| size_t VerbDescCol = GetColumn("Enable verbose"); |
| |
| ASSERT_NE(PortDescCol, 0u); |
| EXPECT_EQ(PortDescCol, LogDescCol) |
| << "Descriptions should align even if short flag is missing"; |
| EXPECT_EQ(LogDescCol, VerbDescCol); |
| |
| size_t LogFlagPos = Result.find("--log-file"); |
| size_t PortFlagPos = Result.find("--port"); |
| |
| size_t LogFlagCol = (LogFlagPos - Result.rfind('\n', LogFlagPos) - 1); |
| size_t PortFlagCol = (PortFlagPos - Result.rfind('\n', PortFlagPos) - 1); |
| |
| EXPECT_EQ(LogFlagCol, PortFlagCol); |
| } |