1//===- llvm/unittest/Support/CommandLineTest.cpp - CommandLine tests ------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/ADT/STLExtras.h"
11#include "llvm/Config/config.h"
12#include "llvm/Support/CommandLine.h"
13#include "llvm/Support/StringSaver.h"
14#include "gtest/gtest.h"
15#include <stdlib.h>
16#include <string>
17
18using namespace llvm;
19
20namespace {
21
22class TempEnvVar {
23 public:
24  TempEnvVar(const char *name, const char *value)
25      : name(name) {
26    const char *old_value = getenv(name);
27    EXPECT_EQ(nullptr, old_value) << old_value;
28#if HAVE_SETENV
29    setenv(name, value, true);
30#else
31#   define SKIP_ENVIRONMENT_TESTS
32#endif
33  }
34
35  ~TempEnvVar() {
36#if HAVE_SETENV
37    // Assume setenv and unsetenv come together.
38    unsetenv(name);
39#else
40    (void)name; // Suppress -Wunused-private-field.
41#endif
42  }
43
44 private:
45  const char *const name;
46};
47
48template <typename T>
49class StackOption : public cl::opt<T> {
50  typedef cl::opt<T> Base;
51public:
52  // One option...
53  template<class M0t>
54  explicit StackOption(const M0t &M0) : Base(M0) {}
55
56  // Two options...
57  template<class M0t, class M1t>
58  StackOption(const M0t &M0, const M1t &M1) : Base(M0, M1) {}
59
60  // Three options...
61  template<class M0t, class M1t, class M2t>
62  StackOption(const M0t &M0, const M1t &M1, const M2t &M2) : Base(M0, M1, M2) {}
63
64  // Four options...
65  template<class M0t, class M1t, class M2t, class M3t>
66  StackOption(const M0t &M0, const M1t &M1, const M2t &M2, const M3t &M3)
67    : Base(M0, M1, M2, M3) {}
68
69  ~StackOption() override { this->removeArgument(); }
70
71  template <class DT> StackOption<T> &operator=(const DT &V) {
72    this->setValue(V);
73    return *this;
74  }
75};
76
77class StackSubCommand : public cl::SubCommand {
78public:
79  StackSubCommand(const char *const Name,
80                  const char *const Description = nullptr)
81      : SubCommand(Name, Description) {}
82
83  StackSubCommand() : SubCommand() {}
84
85  ~StackSubCommand() { unregisterSubCommand(); }
86};
87
88
89cl::OptionCategory TestCategory("Test Options", "Description");
90TEST(CommandLineTest, ModifyExisitingOption) {
91  StackOption<int> TestOption("test-option", cl::desc("old description"));
92
93  const char Description[] = "New description";
94  const char ArgString[] = "new-test-option";
95  const char ValueString[] = "Integer";
96
97  StringMap<cl::Option *> &Map =
98      cl::getRegisteredOptions(*cl::TopLevelSubCommand);
99
100  ASSERT_TRUE(Map.count("test-option") == 1) <<
101    "Could not find option in map.";
102
103  cl::Option *Retrieved = Map["test-option"];
104  ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";
105
106  ASSERT_EQ(&cl::GeneralCategory,Retrieved->Category) <<
107    "Incorrect default option category.";
108
109  Retrieved->setCategory(TestCategory);
110  ASSERT_EQ(&TestCategory,Retrieved->Category) <<
111    "Failed to modify option's option category.";
112
113  Retrieved->setDescription(Description);
114  ASSERT_STREQ(Retrieved->HelpStr.data(), Description)
115      << "Changing option description failed.";
116
117  Retrieved->setArgStr(ArgString);
118  ASSERT_STREQ(ArgString, Retrieved->ArgStr.data())
119      << "Failed to modify option's Argument string.";
120
121  Retrieved->setValueStr(ValueString);
122  ASSERT_STREQ(Retrieved->ValueStr.data(), ValueString)
123      << "Failed to modify option's Value string.";
124
125  Retrieved->setHiddenFlag(cl::Hidden);
126  ASSERT_EQ(cl::Hidden, TestOption.getOptionHiddenFlag()) <<
127    "Failed to modify option's hidden flag.";
128}
129#ifndef SKIP_ENVIRONMENT_TESTS
130
131const char test_env_var[] = "LLVM_TEST_COMMAND_LINE_FLAGS";
132
133cl::opt<std::string> EnvironmentTestOption("env-test-opt");
134TEST(CommandLineTest, ParseEnvironment) {
135  TempEnvVar TEV(test_env_var, "-env-test-opt=hello");
136  EXPECT_EQ("", EnvironmentTestOption);
137  cl::ParseEnvironmentOptions("CommandLineTest", test_env_var);
138  EXPECT_EQ("hello", EnvironmentTestOption);
139}
140
141// This test used to make valgrind complain
142// ("Conditional jump or move depends on uninitialised value(s)")
143//
144// Warning: Do not run any tests after this one that try to gain access to
145// registered command line options because this will likely result in a
146// SEGFAULT. This can occur because the cl::opt in the test below is declared
147// on the stack which will be destroyed after the test completes but the
148// command line system will still hold a pointer to a deallocated cl::Option.
149TEST(CommandLineTest, ParseEnvironmentToLocalVar) {
150  // Put cl::opt on stack to check for proper initialization of fields.
151  StackOption<std::string> EnvironmentTestOptionLocal("env-test-opt-local");
152  TempEnvVar TEV(test_env_var, "-env-test-opt-local=hello-local");
153  EXPECT_EQ("", EnvironmentTestOptionLocal);
154  cl::ParseEnvironmentOptions("CommandLineTest", test_env_var);
155  EXPECT_EQ("hello-local", EnvironmentTestOptionLocal);
156}
157
158#endif  // SKIP_ENVIRONMENT_TESTS
159
160TEST(CommandLineTest, UseOptionCategory) {
161  StackOption<int> TestOption2("test-option", cl::cat(TestCategory));
162
163  ASSERT_EQ(&TestCategory,TestOption2.Category) << "Failed to assign Option "
164                                                  "Category.";
165}
166
167typedef void ParserFunction(StringRef Source, StringSaver &Saver,
168                            SmallVectorImpl<const char *> &NewArgv,
169                            bool MarkEOLs);
170
171void testCommandLineTokenizer(ParserFunction *parse, const char *Input,
172                              const char *const Output[], size_t OutputSize) {
173  SmallVector<const char *, 0> Actual;
174  BumpPtrAllocator A;
175  StringSaver Saver(A);
176  parse(Input, Saver, Actual, /*MarkEOLs=*/false);
177  EXPECT_EQ(OutputSize, Actual.size());
178  for (unsigned I = 0, E = Actual.size(); I != E; ++I) {
179    if (I < OutputSize)
180      EXPECT_STREQ(Output[I], Actual[I]);
181  }
182}
183
184TEST(CommandLineTest, TokenizeGNUCommandLine) {
185  const char *Input =
186      "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' -DFOO=bar\\(\\) "
187      "foo\"bar\"baz C:\\\\src\\\\foo.cpp \"C:\\src\\foo.cpp\"";
188  const char *const Output[] = {
189      "foo bar",     "foo bar",   "foo bar",          "foo\\bar",
190      "-DFOO=bar()", "foobarbaz", "C:\\src\\foo.cpp", "C:srcfoo.cpp"};
191  testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output,
192                           array_lengthof(Output));
193}
194
195TEST(CommandLineTest, TokenizeWindowsCommandLine) {
196  const char *Input = "a\\b c\\\\d e\\\\\"f g\" h\\\"i j\\\\\\\"k \"lmn\" o pqr "
197                      "\"st \\\"u\" \\v";
198  const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k",
199                                 "lmn", "o", "pqr", "st \"u", "\\v" };
200  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output,
201                           array_lengthof(Output));
202}
203
204TEST(CommandLineTest, AliasesWithArguments) {
205  static const size_t ARGC = 3;
206  const char *const Inputs[][ARGC] = {
207    { "-tool", "-actual=x", "-extra" },
208    { "-tool", "-actual", "x" },
209    { "-tool", "-alias=x", "-extra" },
210    { "-tool", "-alias", "x" }
211  };
212
213  for (size_t i = 0, e = array_lengthof(Inputs); i < e; ++i) {
214    StackOption<std::string> Actual("actual");
215    StackOption<bool> Extra("extra");
216    StackOption<std::string> Input(cl::Positional);
217
218    cl::alias Alias("alias", llvm::cl::aliasopt(Actual));
219
220    cl::ParseCommandLineOptions(ARGC, Inputs[i]);
221    EXPECT_EQ("x", Actual);
222    EXPECT_EQ(0, Input.getNumOccurrences());
223
224    Alias.removeArgument();
225  }
226}
227
228void testAliasRequired(int argc, const char *const *argv) {
229  StackOption<std::string> Option("option", cl::Required);
230  cl::alias Alias("o", llvm::cl::aliasopt(Option));
231
232  cl::ParseCommandLineOptions(argc, argv);
233  EXPECT_EQ("x", Option);
234  EXPECT_EQ(1, Option.getNumOccurrences());
235
236  Alias.removeArgument();
237}
238
239TEST(CommandLineTest, AliasRequired) {
240  const char *opts1[] = { "-tool", "-option=x" };
241  const char *opts2[] = { "-tool", "-o", "x" };
242  testAliasRequired(array_lengthof(opts1), opts1);
243  testAliasRequired(array_lengthof(opts2), opts2);
244}
245
246TEST(CommandLineTest, HideUnrelatedOptions) {
247  StackOption<int> TestOption1("hide-option-1");
248  StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory));
249
250  cl::HideUnrelatedOptions(TestCategory);
251
252  ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())
253      << "Failed to hide extra option.";
254  ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
255      << "Hid extra option that should be visable.";
256
257  StringMap<cl::Option *> &Map =
258      cl::getRegisteredOptions(*cl::TopLevelSubCommand);
259  ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
260      << "Hid default option that should be visable.";
261}
262
263cl::OptionCategory TestCategory2("Test Options set 2", "Description");
264
265TEST(CommandLineTest, HideUnrelatedOptionsMulti) {
266  StackOption<int> TestOption1("multi-hide-option-1");
267  StackOption<int> TestOption2("multi-hide-option-2", cl::cat(TestCategory));
268  StackOption<int> TestOption3("multi-hide-option-3", cl::cat(TestCategory2));
269
270  const cl::OptionCategory *VisibleCategories[] = {&TestCategory,
271                                                   &TestCategory2};
272
273  cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
274
275  ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())
276      << "Failed to hide extra option.";
277  ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
278      << "Hid extra option that should be visable.";
279  ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())
280      << "Hid extra option that should be visable.";
281
282  StringMap<cl::Option *> &Map =
283      cl::getRegisteredOptions(*cl::TopLevelSubCommand);
284  ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
285      << "Hid default option that should be visable.";
286}
287
288TEST(CommandLineTest, SetValueInSubcategories) {
289  cl::ResetCommandLineParser();
290
291  StackSubCommand SC1("sc1", "First subcommand");
292  StackSubCommand SC2("sc2", "Second subcommand");
293
294  StackOption<bool> TopLevelOpt("top-level", cl::init(false));
295  StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
296  StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
297
298  EXPECT_FALSE(TopLevelOpt);
299  EXPECT_FALSE(SC1Opt);
300  EXPECT_FALSE(SC2Opt);
301  const char *args[] = {"prog", "-top-level"};
302  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
303  EXPECT_TRUE(TopLevelOpt);
304  EXPECT_FALSE(SC1Opt);
305  EXPECT_FALSE(SC2Opt);
306
307  TopLevelOpt = false;
308
309  cl::ResetAllOptionOccurrences();
310  EXPECT_FALSE(TopLevelOpt);
311  EXPECT_FALSE(SC1Opt);
312  EXPECT_FALSE(SC2Opt);
313  const char *args2[] = {"prog", "sc1", "-sc1"};
314  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
315  EXPECT_FALSE(TopLevelOpt);
316  EXPECT_TRUE(SC1Opt);
317  EXPECT_FALSE(SC2Opt);
318
319  SC1Opt = false;
320
321  cl::ResetAllOptionOccurrences();
322  EXPECT_FALSE(TopLevelOpt);
323  EXPECT_FALSE(SC1Opt);
324  EXPECT_FALSE(SC2Opt);
325  const char *args3[] = {"prog", "sc2", "-sc2"};
326  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
327  EXPECT_FALSE(TopLevelOpt);
328  EXPECT_FALSE(SC1Opt);
329  EXPECT_TRUE(SC2Opt);
330}
331
332TEST(CommandLineTest, LookupFailsInWrongSubCommand) {
333  cl::ResetCommandLineParser();
334
335  StackSubCommand SC1("sc1", "First subcommand");
336  StackSubCommand SC2("sc2", "Second subcommand");
337
338  StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
339  StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
340
341  const char *args[] = {"prog", "sc1", "-sc2"};
342  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
343}
344
345TEST(CommandLineTest, AddToAllSubCommands) {
346  cl::ResetCommandLineParser();
347
348  StackSubCommand SC1("sc1", "First subcommand");
349  StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands),
350                           cl::init(false));
351  StackSubCommand SC2("sc2", "Second subcommand");
352
353  const char *args[] = {"prog", "-everywhere"};
354  const char *args2[] = {"prog", "sc1", "-everywhere"};
355  const char *args3[] = {"prog", "sc2", "-everywhere"};
356
357  EXPECT_FALSE(AllOpt);
358  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
359  EXPECT_TRUE(AllOpt);
360
361  AllOpt = false;
362
363  cl::ResetAllOptionOccurrences();
364  EXPECT_FALSE(AllOpt);
365  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
366  EXPECT_TRUE(AllOpt);
367
368  AllOpt = false;
369
370  cl::ResetAllOptionOccurrences();
371  EXPECT_FALSE(AllOpt);
372  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
373  EXPECT_TRUE(AllOpt);
374}
375
376TEST(CommandLineTest, ReparseCommandLineOptions) {
377  cl::ResetCommandLineParser();
378
379  StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::TopLevelSubCommand),
380                                cl::init(false));
381
382  const char *args[] = {"prog", "-top-level"};
383
384  EXPECT_FALSE(TopLevelOpt);
385  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
386  EXPECT_TRUE(TopLevelOpt);
387
388  TopLevelOpt = false;
389
390  cl::ResetAllOptionOccurrences();
391  EXPECT_FALSE(TopLevelOpt);
392  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
393  EXPECT_TRUE(TopLevelOpt);
394}
395
396TEST(CommandLineTest, RemoveFromRegularSubCommand) {
397  cl::ResetCommandLineParser();
398
399  StackSubCommand SC("sc", "Subcommand");
400  StackOption<bool> RemoveOption("remove-option", cl::sub(SC), cl::init(false));
401  StackOption<bool> KeepOption("keep-option", cl::sub(SC), cl::init(false));
402
403  const char *args[] = {"prog", "sc", "-remove-option"};
404
405  EXPECT_FALSE(RemoveOption);
406  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, nullptr, true));
407  EXPECT_TRUE(RemoveOption);
408
409  RemoveOption.removeArgument();
410
411  cl::ResetAllOptionOccurrences();
412  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
413}
414
415TEST(CommandLineTest, RemoveFromTopLevelSubCommand) {
416  cl::ResetCommandLineParser();
417
418  StackOption<bool> TopLevelRemove(
419      "top-level-remove", cl::sub(*cl::TopLevelSubCommand), cl::init(false));
420  StackOption<bool> TopLevelKeep(
421      "top-level-keep", cl::sub(*cl::TopLevelSubCommand), cl::init(false));
422
423  const char *args[] = {"prog", "-top-level-remove"};
424
425  EXPECT_FALSE(TopLevelRemove);
426  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
427  EXPECT_TRUE(TopLevelRemove);
428
429  TopLevelRemove.removeArgument();
430
431  cl::ResetAllOptionOccurrences();
432  EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, nullptr, true));
433}
434
435TEST(CommandLineTest, RemoveFromAllSubCommands) {
436  cl::ResetCommandLineParser();
437
438  StackSubCommand SC1("sc1", "First Subcommand");
439  StackSubCommand SC2("sc2", "Second Subcommand");
440  StackOption<bool> RemoveOption("remove-option", cl::sub(*cl::AllSubCommands),
441                                 cl::init(false));
442  StackOption<bool> KeepOption("keep-option", cl::sub(*cl::AllSubCommands),
443                               cl::init(false));
444
445  const char *args0[] = {"prog", "-remove-option"};
446  const char *args1[] = {"prog", "sc1", "-remove-option"};
447  const char *args2[] = {"prog", "sc2", "-remove-option"};
448
449  // It should work for all subcommands including the top-level.
450  EXPECT_FALSE(RemoveOption);
451  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
452  EXPECT_TRUE(RemoveOption);
453
454  RemoveOption = false;
455
456  cl::ResetAllOptionOccurrences();
457  EXPECT_FALSE(RemoveOption);
458  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
459  EXPECT_TRUE(RemoveOption);
460
461  RemoveOption = false;
462
463  cl::ResetAllOptionOccurrences();
464  EXPECT_FALSE(RemoveOption);
465  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
466  EXPECT_TRUE(RemoveOption);
467
468  RemoveOption.removeArgument();
469
470  // It should not work for any subcommands including the top-level.
471  cl::ResetAllOptionOccurrences();
472  EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
473  cl::ResetAllOptionOccurrences();
474  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
475  cl::ResetAllOptionOccurrences();
476  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
477}
478
479}  // anonymous namespace
480