@@ -1347,29 +1347,32 @@ struct AutoDeleteFile {
1347
1347
}
1348
1348
};
1349
1349
1350
+ static std::string interceptStdout (std::function<void ()> F) {
1351
+ outs ().flush (); // flush any output from previous tests
1352
+ AutoDeleteFile File;
1353
+ {
1354
+ OutputRedirector Stdout (fileno (stdout));
1355
+ if (!Stdout.Valid )
1356
+ return " " ;
1357
+ File.FilePath = Stdout.FilePath ;
1358
+ F ();
1359
+ outs ().flush ();
1360
+ }
1361
+ auto Buffer = MemoryBuffer::getFile (File.FilePath );
1362
+ if (!Buffer)
1363
+ return " " ;
1364
+ return Buffer->get ()->getBuffer ().str ();
1365
+ }
1366
+
1350
1367
template <void (*Func)(const cl::Option &)>
1351
1368
class PrintOptionTestBase : public ::testing::Test {
1352
1369
public:
1353
1370
// Return std::string because the output of a failing EXPECT check is
1354
1371
// unreadable for StringRef. It also avoids any lifetime issues.
1355
1372
template <typename ... Ts> std::string runTest (Ts... OptionAttributes) {
1356
- outs ().flush (); // flush any output from previous tests
1357
- AutoDeleteFile File;
1358
- {
1359
- OutputRedirector Stdout (fileno (stdout));
1360
- if (!Stdout.Valid )
1361
- return " " ;
1362
- File.FilePath = Stdout.FilePath ;
1363
-
1364
- StackOption<OptionValue> TestOption (Opt, cl::desc (HelpText),
1365
- OptionAttributes...);
1366
- Func (TestOption);
1367
- outs ().flush ();
1368
- }
1369
- auto Buffer = MemoryBuffer::getFile (File.FilePath );
1370
- if (!Buffer)
1371
- return " " ;
1372
- return Buffer->get ()->getBuffer ().str ();
1373
+ StackOption<OptionValue> TestOption (Opt, cl::desc (HelpText),
1374
+ OptionAttributes...);
1375
+ return interceptStdout ([&]() { Func (TestOption); });
1373
1376
}
1374
1377
1375
1378
enum class OptionValue { Val };
@@ -2206,4 +2209,40 @@ TEST(CommandLineTest, DefaultValue) {
2206
2209
EXPECT_EQ (1 , StrInitOption.getNumOccurrences ());
2207
2210
}
2208
2211
2212
+ TEST (CommandLineTest, HelpWithoutSubcommands) {
2213
+ // Check that the help message does not contain the "[subcommand]" placeholder
2214
+ // and the "SUBCOMMANDS" section if there are no subcommands.
2215
+ cl::ResetCommandLineParser ();
2216
+ StackOption<bool > Opt (" opt" , cl::init (false ));
2217
+ const char *args[] = {" prog" };
2218
+ EXPECT_TRUE (cl::ParseCommandLineOptions (std::size (args), args, StringRef (),
2219
+ &llvm::nulls ()));
2220
+ auto Output = interceptStdout ([]() { cl::PrintHelpMessage (); });
2221
+ EXPECT_NE (std::string::npos, Output.find (" USAGE: prog [options]" )) << Output;
2222
+ EXPECT_EQ (std::string::npos, Output.find (" SUBCOMMANDS:" )) << Output;
2223
+ cl::ResetCommandLineParser ();
2224
+ }
2225
+
2226
+ TEST (CommandLineTest, HelpWithSubcommands) {
2227
+ // Check that the help message contains the "[subcommand]" placeholder in the
2228
+ // "USAGE" line and describes subcommands.
2229
+ cl::ResetCommandLineParser ();
2230
+ StackSubCommand SC1 (" sc1" , " First Subcommand" );
2231
+ StackSubCommand SC2 (" sc2" , " Second Subcommand" );
2232
+ StackOption<bool > SC1Opt (" sc1" , cl::sub (SC1), cl::init (false ));
2233
+ StackOption<bool > SC2Opt (" sc2" , cl::sub (SC2), cl::init (false ));
2234
+ const char *args[] = {" prog" };
2235
+ EXPECT_TRUE (cl::ParseCommandLineOptions (std::size (args), args, StringRef (),
2236
+ &llvm::nulls ()));
2237
+ auto Output = interceptStdout ([]() { cl::PrintHelpMessage (); });
2238
+ EXPECT_NE (std::string::npos,
2239
+ Output.find (" USAGE: prog [subcommand] [options]" ))
2240
+ << Output;
2241
+ EXPECT_NE (std::string::npos, Output.find (" SUBCOMMANDS:" )) << Output;
2242
+ EXPECT_NE (std::string::npos, Output.find (" sc1 - First Subcommand" )) << Output;
2243
+ EXPECT_NE (std::string::npos, Output.find (" sc2 - Second Subcommand" ))
2244
+ << Output;
2245
+ cl::ResetCommandLineParser ();
2246
+ }
2247
+
2209
2248
} // anonymous namespace
0 commit comments