1+ // ===- unittest/Support/OptionParsingTest.cpp - OptTable tests ------------===//
2+ //
3+ // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+ // See https://llvm.org/LICENSE.txt for license information.
5+ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+ //
7+ // ===----------------------------------------------------------------------===//
8+
9+ #include " llvm/ADT/STLExtras.h"
10+ #include " llvm/Option/Arg.h"
11+ #include " llvm/Option/ArgList.h"
12+ #include " llvm/Option/OptTable.h"
13+ #include " llvm/Option/Option.h"
14+ #include " llvm/Support/raw_ostream.h"
15+ #include " gtest/gtest.h"
16+
17+ using namespace llvm ;
18+ using namespace llvm ::opt;
19+
20+ #if defined(__clang__)
21+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
22+ #endif
23+
24+ namespace {
25+ enum ID {
26+ OPT_INVALID = 0 ,
27+ #define OPTION (PREFIXES, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
28+ VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
29+ VALUES, SUBCOMMANDIDS_OFFSET) \
30+ OPT_##ID,
31+ #include " SubCommandOpts.inc"
32+ #undef OPTION
33+ };
34+ #define OPTTABLE_STR_TABLE_CODE
35+ #include " SubCommandOpts.inc"
36+ #undef OPTTABLE_STR_TABLE_CODE
37+
38+ #define OPTTABLE_PREFIXES_TABLE_CODE
39+ #include " SubCommandOpts.inc"
40+ #undef OPTTABLE_PREFIXES_TABLE_CODE
41+
42+ #define OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE
43+ #include " SubCommandOpts.inc"
44+ #undef OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE
45+
46+ #define OPTTABLE_SUBCOMMANDS_CODE
47+ #include " SubCommandOpts.inc"
48+ #undef OPTTABLE_SUBCOMMANDS_CODE
49+
50+ static constexpr OptTable::Info InfoTable[] = {
51+ #define OPTION (...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
52+ #include " SubCommandOpts.inc"
53+ #undef OPTION
54+ };
55+
56+ class TestOptSubCommandTable : public GenericOptTable {
57+ public:
58+ TestOptSubCommandTable (bool IgnoreCase = false )
59+ : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
60+ /* IgnoreCase=*/ false , OptionSubCommands,
61+ OptionSubCommandIDsTable) {}
62+ };
63+
64+ // Test fixture
65+ template <typename T> class OptSubCommandTableTest : public ::testing::Test {};
66+
67+ // Test both precomputed and computed OptTables with the same suite of tests.
68+ using OptSubCommandTableTestTypes = ::testing::Types<TestOptSubCommandTable>;
69+
70+ TYPED_TEST_SUITE (OptSubCommandTableTest, OptSubCommandTableTestTypes, );
71+
72+ TYPED_TEST (OptSubCommandTableTest, SubCommandParsing) {
73+ TypeParam T;
74+ unsigned MAI, MAC;
75+
76+ std::string ErrMsg;
77+ raw_string_ostream RSO1 (ErrMsg);
78+
79+ auto HandleMultipleSubcommands = [&](ArrayRef<StringRef> SubCommands) {
80+ ErrMsg.clear ();
81+ RSO1 << " Multiple subcommands passed\n " ;
82+ for (auto SC : SubCommands) {
83+ RSO1 << " \n " << SC;
84+ }
85+ };
86+
87+ auto HandleOtherPositionals = [&](ArrayRef<StringRef> Positionals) {
88+ ErrMsg.clear ();
89+ RSO1 << " Unregistered positionals passed\n " ;
90+ for (auto SC : Positionals) {
91+ RSO1 << " \n " << SC;
92+ }
93+ };
94+
95+ {
96+ // Test case 1: Toplevel option, no subcommand
97+ const char *Args[] = {" -version" };
98+ InputArgList AL = T.ParseArgs (Args, MAI, MAC);
99+ EXPECT_TRUE (AL.hasArg (OPT_version));
100+ StringRef SC = AL.getSubcommand (
101+ T.getSubCommands (), HandleMultipleSubcommands, HandleOtherPositionals);
102+ EXPECT_TRUE (SC.empty ());
103+ EXPECT_FALSE (AL.hasArg (OPT_uppercase));
104+ EXPECT_FALSE (AL.hasArg (OPT_lowercase));
105+ }
106+
107+ {
108+ // Test case 2: Subcommand 'foo' with its valid options
109+ const char *Args[] = {" foo" , " -uppercase" };
110+ InputArgList AL = T.ParseArgs (Args, MAI, MAC);
111+ StringRef SC = AL.getSubcommand (
112+ T.getSubCommands (), HandleMultipleSubcommands, HandleOtherPositionals);
113+ EXPECT_EQ (SC, " foo" );
114+ EXPECT_TRUE (AL.hasArg (OPT_uppercase));
115+ EXPECT_FALSE (AL.hasArg (OPT_lowercase));
116+ EXPECT_FALSE (AL.hasArg (OPT_version));
117+ // Do not expect any error messages as this is a valid use case.
118+ EXPECT_EQ (std::string::npos, ErrMsg.find (" Multiple subcommands passed" ));
119+ EXPECT_EQ (std::string::npos,
120+ ErrMsg.find (" Unregistered positionals passed" ));
121+ }
122+
123+ {
124+ // Test case 3: Check valid use of subcommand which follows a valid
125+ // subcommand option.
126+ const char *Args[] = {" -uppercase" , " foo" };
127+ InputArgList AL = T.ParseArgs (Args, MAI, MAC);
128+ StringRef SC = AL.getSubcommand (
129+ T.getSubCommands (), HandleMultipleSubcommands, HandleOtherPositionals);
130+ EXPECT_EQ (SC, " foo" );
131+ EXPECT_TRUE (AL.hasArg (OPT_uppercase));
132+ EXPECT_FALSE (AL.hasArg (OPT_lowercase));
133+ EXPECT_FALSE (AL.hasArg (OPT_version));
134+ // Do not expect any error messages as this is a valid use case.
135+ EXPECT_EQ (std::string::npos, ErrMsg.find (" Multiple subcommands passed" ));
136+ EXPECT_EQ (std::string::npos,
137+ ErrMsg.find (" Unregistered positionals passed" ));
138+ }
139+
140+ {
141+ // Test case 4: Check invalid use of passing multiple subcommands.
142+ const char *Args[] = {" -uppercase" , " foo" , " bar" };
143+ InputArgList AL = T.ParseArgs (Args, MAI, MAC);
144+ StringRef SC = AL.getSubcommand (
145+ T.getSubCommands (), HandleMultipleSubcommands, HandleOtherPositionals);
146+ // No valid subcommand should be returned as this is an invalid invocation.
147+ EXPECT_TRUE (SC.empty ());
148+ // Expect the multiple subcommands error message.
149+ EXPECT_NE (std::string::npos, ErrMsg.find (" Multiple subcommands passed" ));
150+ // Do not expect the rnregistered subcommands error message.
151+ EXPECT_EQ (std::string::npos,
152+ ErrMsg.find (" Unregistered positionals passed" ));
153+ }
154+ }
155+
156+ TYPED_TEST (OptSubCommandTableTest, SubCommandHelp) {
157+ TypeParam T;
158+ std::string Help;
159+ raw_string_ostream RSO (Help);
160+
161+ // Toplevel help
162+ T.printHelp (RSO, " Test Usage String" , " OverviewString" );
163+ EXPECT_NE (std::string::npos, Help.find (" OVERVIEW:" ));
164+ EXPECT_NE (std::string::npos, Help.find (" OverviewString" ));
165+ EXPECT_NE (std::string::npos, Help.find (" USAGE:" ));
166+ EXPECT_NE (std::string::npos, Help.find (" Test Usage String" ));
167+ EXPECT_NE (std::string::npos, Help.find (" SUBCOMMANDS:" ));
168+ EXPECT_NE (std::string::npos, Help.find (" foo" ));
169+ EXPECT_NE (std::string::npos, Help.find (" bar" ));
170+ EXPECT_NE (std::string::npos, Help.find (" HelpText for SubCommand foo." ));
171+ EXPECT_NE (std::string::npos, Help.find (" HelpText for SubCommand bar." ));
172+ EXPECT_NE (std::string::npos, Help.find (" OPTIONS:" ));
173+ EXPECT_NE (std::string::npos, Help.find (" --help" ));
174+ EXPECT_NE (std::string::npos, Help.find (" -version" ));
175+ // uppercase is not a global option and should not be shown.
176+ EXPECT_EQ (std::string::npos, Help.find (" -uppercase" ));
177+
178+ // Help for subcommand foo
179+ Help.clear ();
180+ StringRef SC1 = " foo" ;
181+ T.printHelp (RSO, " Test Usage String" , " OverviewString" , false , false ,
182+ Visibility (), SC1);
183+ EXPECT_NE (std::string::npos, Help.find (" OVERVIEW:" ));
184+ EXPECT_NE (std::string::npos, Help.find (" OverviewString" ));
185+ // SubCommand "foo" definition for tablegen has NO dedicated usage string so
186+ // not expected to see USAGE.
187+ EXPECT_EQ (std::string::npos, Help.find (" USAGE:" ));
188+ EXPECT_NE (std::string::npos, Help.find (" HelpText for SubCommand foo." ));
189+ EXPECT_NE (std::string::npos, Help.find (" -uppercase" ));
190+ EXPECT_NE (std::string::npos, Help.find (" -lowercase" ));
191+ EXPECT_EQ (std::string::npos, Help.find (" -version" ));
192+ EXPECT_EQ (std::string::npos, Help.find (" SUBCOMMANDS:" ));
193+
194+ // Help for subcommand bar
195+ Help.clear ();
196+ StringRef SC2 = " bar" ;
197+ T.printHelp (RSO, " Test Usage String" , " OverviewString" , false , false ,
198+ Visibility (), SC2);
199+ EXPECT_NE (std::string::npos, Help.find (" OVERVIEW:" ));
200+ EXPECT_NE (std::string::npos, Help.find (" OverviewString" ));
201+ // SubCommand "bar" definition for tablegen has a dedicated usage string.
202+ EXPECT_NE (std::string::npos, Help.find (" USAGE:" ));
203+ EXPECT_NE (std::string::npos, Help.find (" Subcommand bar <options>" ));
204+ EXPECT_NE (std::string::npos, Help.find (" HelpText for SubCommand bar." ));
205+ EXPECT_NE (std::string::npos, Help.find (" -uppercase" ));
206+ // lowercase is not an option for bar and should not be shown.
207+ EXPECT_EQ (std::string::npos, Help.find (" -lowercase" ));
208+ // version is a global option and should not be shown.
209+ EXPECT_EQ (std::string::npos, Help.find (" -version" ));
210+ }
211+ } // end anonymous namespace
0 commit comments