blob: 9d7679da511c5b2addd7062f2618a8ba229e9a9f [file] [log] [blame]
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +00001//===- 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
Reid Klecknera73c7782013-07-18 16:52:05 +000010#include "llvm/ADT/STLExtras.h"
Jeffrey Yasskin14a5cc52009-09-25 21:07:20 +000011#include "llvm/Config/config.h"
Chandler Carruth8a8cd2b2014-01-07 11:48:04 +000012#include "llvm/Support/CommandLine.h"
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +000013#include "gtest/gtest.h"
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +000014#include <stdlib.h>
Chandler Carruth130cec22012-12-04 10:23:08 +000015#include <string>
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +000016
17using namespace llvm;
18
19namespace {
20
21class TempEnvVar {
22 public:
23 TempEnvVar(const char *name, const char *value)
24 : name(name) {
25 const char *old_value = getenv(name);
Craig Topper66f09ad2014-06-08 22:29:17 +000026 EXPECT_EQ(nullptr, old_value) << old_value;
Jeffrey Yasskin14a5cc52009-09-25 21:07:20 +000027#if HAVE_SETENV
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +000028 setenv(name, value, true);
Jeffrey Yasskin14a5cc52009-09-25 21:07:20 +000029#else
30# define SKIP_ENVIRONMENT_TESTS
31#endif
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +000032 }
33
34 ~TempEnvVar() {
Jeffrey Yasskin14a5cc52009-09-25 21:07:20 +000035#if HAVE_SETENV
36 // Assume setenv and unsetenv come together.
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +000037 unsetenv(name);
Reid Kleckner542a4542015-02-26 21:08:21 +000038#else
39 (void)name; // Suppress -Wunused-private-field.
Jeffrey Yasskin14a5cc52009-09-25 21:07:20 +000040#endif
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +000041 }
42
43 private:
44 const char *const name;
45};
46
Jordan Rosec25b0c72014-01-29 18:54:17 +000047template <typename T>
48class StackOption : public cl::opt<T> {
Jordan Rosed2ad22a2014-01-29 19:14:23 +000049 typedef cl::opt<T> Base;
Jordan Rosec25b0c72014-01-29 18:54:17 +000050public:
51 // One option...
52 template<class M0t>
53 explicit StackOption(const M0t &M0) : Base(M0) {}
54
55 // Two options...
56 template<class M0t, class M1t>
57 StackOption(const M0t &M0, const M1t &M1) : Base(M0, M1) {}
58
59 // Three options...
60 template<class M0t, class M1t, class M2t>
61 StackOption(const M0t &M0, const M1t &M1, const M2t &M2) : Base(M0, M1, M2) {}
62
63 // Four options...
64 template<class M0t, class M1t, class M2t, class M3t>
65 StackOption(const M0t &M0, const M1t &M1, const M2t &M2, const M3t &M3)
66 : Base(M0, M1, M2, M3) {}
67
68 ~StackOption() {
69 this->removeArgument();
70 }
71};
72
73
Andrew Trick7cb710d2013-05-06 21:56:35 +000074cl::OptionCategory TestCategory("Test Options", "Description");
Andrew Trick7cb710d2013-05-06 21:56:35 +000075TEST(CommandLineTest, ModifyExisitingOption) {
Chris Bienemand1d94302015-01-28 19:00:25 +000076 StackOption<int> TestOption("test-option", cl::desc("old description"));
77
Andrew Trick7cb710d2013-05-06 21:56:35 +000078 const char Description[] = "New description";
79 const char ArgString[] = "new-test-option";
80 const char ValueString[] = "Integer";
81
Chris Bienemand1d94302015-01-28 19:00:25 +000082 StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
Andrew Trick7cb710d2013-05-06 21:56:35 +000083
84 ASSERT_TRUE(Map.count("test-option") == 1) <<
85 "Could not find option in map.";
86
87 cl::Option *Retrieved = Map["test-option"];
88 ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";
89
90 ASSERT_EQ(&cl::GeneralCategory,Retrieved->Category) <<
91 "Incorrect default option category.";
92
93 Retrieved->setCategory(TestCategory);
94 ASSERT_EQ(&TestCategory,Retrieved->Category) <<
95 "Failed to modify option's option category.";
96
97 Retrieved->setDescription(Description);
98 ASSERT_STREQ(Retrieved->HelpStr, Description) <<
99 "Changing option description failed.";
100
101 Retrieved->setArgStr(ArgString);
102 ASSERT_STREQ(ArgString, Retrieved->ArgStr) <<
103 "Failed to modify option's Argument string.";
104
105 Retrieved->setValueStr(ValueString);
106 ASSERT_STREQ(Retrieved->ValueStr, ValueString) <<
107 "Failed to modify option's Value string.";
108
109 Retrieved->setHiddenFlag(cl::Hidden);
110 ASSERT_EQ(cl::Hidden, TestOption.getOptionHiddenFlag()) <<
111 "Failed to modify option's hidden flag.";
112}
Jeffrey Yasskin14a5cc52009-09-25 21:07:20 +0000113#ifndef SKIP_ENVIRONMENT_TESTS
114
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +0000115const char test_env_var[] = "LLVM_TEST_COMMAND_LINE_FLAGS";
116
117cl::opt<std::string> EnvironmentTestOption("env-test-opt");
118TEST(CommandLineTest, ParseEnvironment) {
119 TempEnvVar TEV(test_env_var, "-env-test-opt=hello");
120 EXPECT_EQ("", EnvironmentTestOption);
121 cl::ParseEnvironmentOptions("CommandLineTest", test_env_var);
122 EXPECT_EQ("hello", EnvironmentTestOption);
123}
124
Alexander Kornienko8c724a72012-08-13 10:43:36 +0000125// This test used to make valgrind complain
126// ("Conditional jump or move depends on uninitialised value(s)")
Andrew Trick7cb710d2013-05-06 21:56:35 +0000127//
128// Warning: Do not run any tests after this one that try to gain access to
129// registered command line options because this will likely result in a
130// SEGFAULT. This can occur because the cl::opt in the test below is declared
131// on the stack which will be destroyed after the test completes but the
132// command line system will still hold a pointer to a deallocated cl::Option.
Alexander Kornienko8c724a72012-08-13 10:43:36 +0000133TEST(CommandLineTest, ParseEnvironmentToLocalVar) {
134 // Put cl::opt on stack to check for proper initialization of fields.
Jordan Rosec25b0c72014-01-29 18:54:17 +0000135 StackOption<std::string> EnvironmentTestOptionLocal("env-test-opt-local");
Alexander Kornienko8c724a72012-08-13 10:43:36 +0000136 TempEnvVar TEV(test_env_var, "-env-test-opt-local=hello-local");
137 EXPECT_EQ("", EnvironmentTestOptionLocal);
138 cl::ParseEnvironmentOptions("CommandLineTest", test_env_var);
139 EXPECT_EQ("hello-local", EnvironmentTestOptionLocal);
140}
141
Jeffrey Yasskin14a5cc52009-09-25 21:07:20 +0000142#endif // SKIP_ENVIRONMENT_TESTS
143
Andrew Trick0537a982013-05-06 21:56:23 +0000144TEST(CommandLineTest, UseOptionCategory) {
Jordan Rosec25b0c72014-01-29 18:54:17 +0000145 StackOption<int> TestOption2("test-option", cl::cat(TestCategory));
Andrew Trick0537a982013-05-06 21:56:23 +0000146
Andrew Trick7cb710d2013-05-06 21:56:35 +0000147 ASSERT_EQ(&TestCategory,TestOption2.Category) << "Failed to assign Option "
Andrew Trick0537a982013-05-06 21:56:23 +0000148 "Category.";
149}
150
Sean Silvadb794842014-08-15 23:39:01 +0000151class StrDupSaver : public cl::StringSaver {
152 const char *SaveString(const char *Str) override {
153 return strdup(Str);
154 }
155};
156
157typedef void ParserFunction(StringRef Source, llvm::cl::StringSaver &Saver,
Reid Klecknere3f146d2014-08-22 19:29:17 +0000158 SmallVectorImpl<const char *> &NewArgv,
159 bool MarkEOLs);
Rui Ueyamaa2222b52013-07-30 19:03:20 +0000160
161void testCommandLineTokenizer(ParserFunction *parse, const char *Input,
162 const char *const Output[], size_t OutputSize) {
163 SmallVector<const char *, 0> Actual;
Sean Silvadb794842014-08-15 23:39:01 +0000164 StrDupSaver Saver;
Reid Klecknere3f146d2014-08-22 19:29:17 +0000165 parse(Input, Saver, Actual, /*MarkEOLs=*/false);
Rui Ueyamaa2222b52013-07-30 19:03:20 +0000166 EXPECT_EQ(OutputSize, Actual.size());
167 for (unsigned I = 0, E = Actual.size(); I != E; ++I) {
168 if (I < OutputSize)
169 EXPECT_STREQ(Output[I], Actual[I]);
Sean Silvadb794842014-08-15 23:39:01 +0000170 free(const_cast<char *>(Actual[I]));
Rui Ueyamaa2222b52013-07-30 19:03:20 +0000171 }
172}
173
Reid Klecknera73c7782013-07-18 16:52:05 +0000174TEST(CommandLineTest, TokenizeGNUCommandLine) {
175 const char *Input = "foo\\ bar \"foo bar\" \'foo bar\' 'foo\\\\bar' "
176 "foo\"bar\"baz C:\\src\\foo.cpp \"C:\\src\\foo.cpp\"";
177 const char *const Output[] = { "foo bar", "foo bar", "foo bar", "foo\\bar",
178 "foobarbaz", "C:\\src\\foo.cpp",
179 "C:\\src\\foo.cpp" };
Rui Ueyamaa2222b52013-07-30 19:03:20 +0000180 testCommandLineTokenizer(cl::TokenizeGNUCommandLine, Input, Output,
181 array_lengthof(Output));
182}
183
184TEST(CommandLineTest, TokenizeWindowsCommandLine) {
185 const char *Input = "a\\b c\\\\d e\\\\\"f g\" h\\\"i j\\\\\\\"k \"lmn\" o pqr "
186 "\"st \\\"u\" \\v";
187 const char *const Output[] = { "a\\b", "c\\\\d", "e\\f g", "h\"i", "j\\\"k",
188 "lmn", "o", "pqr", "st \"u", "\\v" };
189 testCommandLineTokenizer(cl::TokenizeWindowsCommandLine, Input, Output,
190 array_lengthof(Output));
Reid Klecknera73c7782013-07-18 16:52:05 +0000191}
192
Jordan Rosec25b0c72014-01-29 18:54:17 +0000193TEST(CommandLineTest, AliasesWithArguments) {
194 static const size_t ARGC = 3;
195 const char *const Inputs[][ARGC] = {
196 { "-tool", "-actual=x", "-extra" },
197 { "-tool", "-actual", "x" },
198 { "-tool", "-alias=x", "-extra" },
199 { "-tool", "-alias", "x" }
200 };
201
202 for (size_t i = 0, e = array_lengthof(Inputs); i < e; ++i) {
203 StackOption<std::string> Actual("actual");
204 StackOption<bool> Extra("extra");
205 StackOption<std::string> Input(cl::Positional);
206
207 cl::alias Alias("alias", llvm::cl::aliasopt(Actual));
208
209 cl::ParseCommandLineOptions(ARGC, Inputs[i]);
210 EXPECT_EQ("x", Actual);
211 EXPECT_EQ(0, Input.getNumOccurrences());
212
213 Alias.removeArgument();
214 }
215}
216
Justin Bogner759645e2014-07-14 20:53:57 +0000217void testAliasRequired(int argc, const char *const *argv) {
218 StackOption<std::string> Option("option", cl::Required);
219 cl::alias Alias("o", llvm::cl::aliasopt(Option));
220
221 cl::ParseCommandLineOptions(argc, argv);
222 EXPECT_EQ("x", Option);
223 EXPECT_EQ(1, Option.getNumOccurrences());
224
225 Alias.removeArgument();
226}
227
228TEST(CommandLineTest, AliasRequired) {
229 const char *opts1[] = { "-tool", "-option=x" };
230 const char *opts2[] = { "-tool", "-o", "x" };
231 testAliasRequired(array_lengthof(opts1), opts1);
232 testAliasRequired(array_lengthof(opts2), opts2);
233}
234
Chris Bieneman9e13af72015-01-21 22:45:52 +0000235TEST(CommandLineTest, HideUnrelatedOptions) {
Chris Bieneman68169362015-01-27 22:21:06 +0000236 StackOption<int> TestOption1("hide-option-1");
237 StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory));
Chris Bieneman9e13af72015-01-21 22:45:52 +0000238
239 cl::HideUnrelatedOptions(TestCategory);
240
241 ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())
242 << "Failed to hide extra option.";
243 ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
244 << "Hid extra option that should be visable.";
Chris Bieneman831fc5e2015-01-26 16:56:00 +0000245
Chris Bienemand1d94302015-01-28 19:00:25 +0000246 StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
Chris Bieneman68169362015-01-27 22:21:06 +0000247 ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
248 << "Hid default option that should be visable.";
249}
250
251cl::OptionCategory TestCategory2("Test Options set 2", "Description");
252
253TEST(CommandLineTest, HideUnrelatedOptionsMulti) {
254 StackOption<int> TestOption1("multi-hide-option-1");
255 StackOption<int> TestOption2("multi-hide-option-2", cl::cat(TestCategory));
256 StackOption<int> TestOption3("multi-hide-option-3", cl::cat(TestCategory2));
257
258 const cl::OptionCategory *VisibleCategories[] = {&TestCategory,
259 &TestCategory2};
260
261 cl::HideUnrelatedOptions(makeArrayRef(VisibleCategories));
262
263 ASSERT_EQ(cl::ReallyHidden, TestOption1.getOptionHiddenFlag())
264 << "Failed to hide extra option.";
265 ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
266 << "Hid extra option that should be visable.";
267 ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())
268 << "Hid extra option that should be visable.";
269
Chris Bienemand1d94302015-01-28 19:00:25 +0000270 StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
Chris Bieneman831fc5e2015-01-26 16:56:00 +0000271 ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
272 << "Hid default option that should be visable.";
Chris Bieneman9e13af72015-01-21 22:45:52 +0000273}
Justin Bogner759645e2014-07-14 20:53:57 +0000274
Jeffrey Yasskina75d6bf2009-09-24 01:14:07 +0000275} // anonymous namespace