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
72
73cl::OptionCategory TestCategory("Test Options", "Description");
74TEST(CommandLineTest, ModifyExisitingOption) {
75  StackOption<int> TestOption("test-option", cl::desc("old description"));
76
77  const char Description[] = "New description";
78  const char ArgString[] = "new-test-option";
79  const char ValueString[] = "Integer";
80
81  StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
82
83  ASSERT_TRUE(Map.count("test-option") == 1) <<
84    "Could not find option in map.";
85
86  cl::Option *Retrieved = Map["test-option"];
87  ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";
88
89  ASSERT_EQ(&cl::GeneralCategory,Retrieved->Category) <<
90    "Incorrect default option category.";
91
92  Retrieved->setCategory(TestCategory);
93  ASSERT_EQ(&TestCategory,Retrieved->Category) <<
94    "Failed to modify option's option category.";
95
96  Retrieved->setDescription(Description);
97  ASSERT_STREQ(Retrieved->HelpStr.data(), Description)
98      << "Changing option description failed.";
99
100  Retrieved->setArgStr(ArgString);
101  ASSERT_STREQ(ArgString, Retrieved->ArgStr.data())
102      << "Failed to modify option's Argument string.";
103
104  Retrieved->setValueStr(ValueString);
105  ASSERT_STREQ(Retrieved->ValueStr.data(), ValueString)
106      << "Failed to modify option's Value string.";
107
108  Retrieved->setHiddenFlag(cl::Hidden);
109  ASSERT_EQ(cl::Hidden, TestOption.getOptionHiddenFlag()) <<
110    "Failed to modify option's hidden flag.";
111}
112#ifndef SKIP_ENVIRONMENT_TESTS
113
114const char test_env_var[] = "LLVM_TEST_COMMAND_LINE_FLAGS";
115
116cl::opt<std::string> EnvironmentTestOption("env-test-opt");
117TEST(CommandLineTest, ParseEnvironment) {
118  TempEnvVar TEV(test_env_var, "-env-test-opt=hello");
119  EXPECT_EQ("", EnvironmentTestOption);
120  cl::ParseEnvironmentOptions("CommandLineTest", test_env_var);
121  EXPECT_EQ("hello", EnvironmentTestOption);
122}
123
124// This test used to make valgrind complain
125// ("Conditional jump or move depends on uninitialised value(s)")
126//
127// Warning: Do not run any tests after this one that try to gain access to
128// registered command line options because this will likely result in a
129// SEGFAULT. This can occur because the cl::opt in the test below is declared
130// on the stack which will be destroyed after the test completes but the
131// command line system will still hold a pointer to a deallocated cl::Option.
132TEST(CommandLineTest, ParseEnvironmentToLocalVar) {
133  // Put cl::opt on stack to check for proper initialization of fields.
134  StackOption<std::string> EnvironmentTestOptionLocal("env-test-opt-local");
135  TempEnvVar TEV(test_env_var, "-env-test-opt-local=hello-local");
136  EXPECT_EQ("", EnvironmentTestOptionLocal);
137  cl::ParseEnvironmentOptions("CommandLineTest", test_env_var);
138  EXPECT_EQ("hello-local", EnvironmentTestOptionLocal);
139}
140
141#endif  // SKIP_ENVIRONMENT_TESTS
142
143TEST(CommandLineTest, UseOptionCategory) {
144  StackOption<int> TestOption2("test-option", cl::cat(TestCategory));
145
146  ASSERT_EQ(&TestCategory,TestOption2.Category) << "Failed to assign Option "
147                                                  "Category.";
148}
149
150typedef void ParserFunction(StringRef Source, StringSaver &Saver,
151                            SmallVectorImpl<const char *> &NewArgv,
152                            bool MarkEOLs);
153
154void testCommandLineTokenizer(ParserFunction *parse, const char *Input,
155                              const char *const Output[], size_t OutputSize) {
156  SmallVector<const char *, 0> Actual;
157  BumpPtrAllocator A;
158  StringSaver Saver(A);
159  parse(Input, Saver, Actual, /*MarkEOLs=*/false);
160  EXPECT_EQ(OutputSize, Actual.size());
161  for (unsigned I = 0, E = Actual.size(); I != E; ++I) {
162    if (I < OutputSize)
163      EXPECT_STREQ(Output[I], Actual[I]);
164  }
165}
166
167TEST(CommandLineTest, TokenizeGNUCommandLine) {
168  const char *Input = "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' "
169                      "foo\"bar\"baz C:\\src\\foo.cpp \"C:\\src\\foo.cpp\"";
170  const char *const Output[] = { "foo bar", "foo bar", "foo bar", "foo\\bar",
171                                 "foobarbaz", "C:\\src\\foo.cpp",
172                                 "C:\\src\\foo.cpp" };
173  testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output,
174                           array_lengthof(Output));
175}
176
177TEST(CommandLineTest, TokenizeWindowsCommandLine) {
178  const char *Input = "a\\b c\\\\d e\\\\\"f g\" h\\\"i j\\\\\\\"k \"lmn\" o pqr "
179                      "\"st \\\"u\" \\v";
180  const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k",
181                                 "lmn", "o", "pqr", "st \"u", "\\v" };
182  testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output,
183                           array_lengthof(Output));
184}
185
186TEST(CommandLineTest, AliasesWithArguments) {
187  static const size_t ARGC = 3;
188  const char *const Inputs[][ARGC] = {
189    { "-tool", "-actual=x", "-extra" },
190    { "-tool", "-actual", "x" },
191    { "-tool", "-alias=x", "-extra" },
192    { "-tool", "-alias", "x" }
193  };
194
195  for (size_t i = 0, e = array_lengthof(Inputs); i < e; ++i) {
196    StackOption<std::string> Actual("actual");
197    StackOption<bool> Extra("extra");
198    StackOption<std::string> Input(cl::Positional);
199
200    cl::alias Alias("alias", llvm::cl::aliasopt(Actual));
201
202    cl::ParseCommandLineOptions(ARGC, Inputs[i]);
203    EXPECT_EQ("x", Actual);
204    EXPECT_EQ(0, Input.getNumOccurrences());
205
206    Alias.removeArgument();
207  }
208}
209
210void testAliasRequired(int argc, const char *const *argv) {
211  StackOption<std::string> Option("option", cl::Required);
212  cl::alias Alias("o", llvm::cl::aliasopt(Option));
213
214  cl::ParseCommandLineOptions(argc, argv);
215  EXPECT_EQ("x", Option);
216  EXPECT_EQ(1, Option.getNumOccurrences());
217
218  Alias.removeArgument();
219}
220
221TEST(CommandLineTest, AliasRequired) {
222  const char *opts1[] = { "-tool", "-option=x" };
223  const char *opts2[] = { "-tool", "-o", "x" };
224  testAliasRequired(array_lengthof(opts1), opts1);
225  testAliasRequired(array_lengthof(opts2), opts2);
226}
227
228TEST(CommandLineTest, HideUnrelatedOptions) {
229  StackOption<int> TestOption1("hide-option-1");
230  StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory));
231
232  cl::HideUnrelatedOptions(TestCategory);
233
234  ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())
235      << "Failed to hide extra option.";
236  ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
237      << "Hid extra option that should be visable.";
238
239  StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
240  ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
241      << "Hid default option that should be visable.";
242}
243
244cl::OptionCategory TestCategory2("Test Options set 2", "Description");
245
246TEST(CommandLineTest, HideUnrelatedOptionsMulti) {
247  StackOption<int> TestOption1("multi-hide-option-1");
248  StackOption<int> TestOption2("multi-hide-option-2", cl::cat(TestCategory));
249  StackOption<int> TestOption3("multi-hide-option-3", cl::cat(TestCategory2));
250
251  const cl::OptionCategory *VisibleCategories[] = {&TestCategory,
252                                                   &TestCategory2};
253
254  cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
255
256  ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())
257      << "Failed to hide extra option.";
258  ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
259      << "Hid extra option that should be visable.";
260  ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())
261      << "Hid extra option that should be visable.";
262
263  StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
264  ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
265      << "Hid default option that should be visable.";
266}
267
268}  // anonymous namespace
269