blob: 418639e9411fa6df805bd7f2c9d95413c002aed0 [file] [log] [blame]
ccalvarin58acb7b2018-03-08 10:53:24 -08001// Copyright 2018 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <vector>
16
17#include "src/main/cpp/blaze_util.h"
18#include "src/main/cpp/blaze_util_platform.h"
19#include "src/main/cpp/option_processor.h"
20#include "src/main/cpp/util/file.h"
21#include "src/main/cpp/util/file_platform.h"
ccalvarinac69da02018-06-05 15:27:26 -070022#include "src/main/cpp/util/path.h"
ccalvarin58acb7b2018-03-08 10:53:24 -080023#include "src/main/cpp/util/strings.h"
24#include "src/main/cpp/workspace_layout.h"
ccalvarin8ceaa652018-08-24 12:44:31 -070025#include "googlemock/include/gmock/gmock.h"
ccalvarin8e9f4a82018-03-23 08:19:37 -070026#include "googletest/include/gtest/gtest.h"
ccalvarin58acb7b2018-03-08 10:53:24 -080027
28namespace blaze {
29using std::string;
30using std::unordered_map;
31using std::vector;
ccalvarin8ceaa652018-08-24 12:44:31 -070032using ::testing::MatchesRegex;
ccalvarin58acb7b2018-03-08 10:53:24 -080033
34class RcOptionsTest : public ::testing::Test {
35 protected:
36 RcOptionsTest()
Laszlo Csomor0d107492019-03-13 09:04:27 -070037 : test_file_dir_(blaze::GetPathEnv("TEST_TMPDIR")), workspace_layout_() {}
ccalvarin58acb7b2018-03-08 10:53:24 -080038
39 const string test_file_dir_;
40 const WorkspaceLayout workspace_layout_;
41
42 void WriteRc(const string& filename, const string& contents) {
43 bool success = blaze_util::WriteFile(
44 contents, blaze_util::JoinPath(test_file_dir_, filename));
45 ASSERT_TRUE(success) << "Failed to write " << filename;
46 }
47
48 std::unique_ptr<RcFile> Parse(const string& filename,
49 RcFile::ParseError* error,
50 std::string* error_text) {
51 return RcFile::Parse(
52 blaze_util::JoinPath(test_file_dir_, filename),
53 &workspace_layout_,
54 // Set workspace to test_file_dir_ so importing %workspace%/foo works.
55 test_file_dir_,
56 error,
57 error_text);
58 }
59
60 void SuccessfullyParseRcWithExpectedArgs(
61 const string& filename,
62 const unordered_map<string, vector<string>>& expected_args_map) {
63 RcFile::ParseError error;
64 string error_text;
65 std::unique_ptr<RcFile> rc = Parse(filename, &error, &error_text);
66 EXPECT_EQ(error_text, "");
67 ASSERT_EQ(error, RcFile::ParseError::NONE);
68
69 // Test that exactly each command in the expected map was in the results,
70 // and that for each of these, exactly the expected args are found, in the
71 // correct order. Note that this is not just an exercise in rewritting map
72 // equality - the results have type RcOption, and the expected values
73 // are just strings. This is ignoring the source_path for convenience.
74 const RcFile::OptionMap& result = rc->options();
75 ASSERT_EQ(expected_args_map.size(), result.size());
76 for (const auto& command_args_pair : expected_args_map) {
77 const string& expected_command = command_args_pair.first;
78 const vector<string>& expected_args = command_args_pair.second;
79 const auto result_args_iter = result.find(expected_command);
80 ASSERT_NE(result_args_iter, rc->options().end());
81 const std::vector<RcOption>& result_args = result_args_iter->second;
82 ASSERT_EQ(result_args.size(), expected_args.size());
83 for (size_t i = 0; i < result_args.size(); ++i) {
84 EXPECT_EQ(result_args[i].option, expected_args[i]);
85 }
86 }
87 }
88};
89
ccalvarin58acb7b2018-03-08 10:53:24 -080090TEST_F(RcOptionsTest, Empty) {
91 WriteRc("empty.bazelrc",
92 "");
93 unordered_map<string, vector<string>> no_expected_args;
94 SuccessfullyParseRcWithExpectedArgs("empty.bazelrc", no_expected_args);
95}
96
97TEST_F(RcOptionsTest, Whitespace) {
98 WriteRc("whitespace.bazelrc",
99 " \n\t ");
100 unordered_map<string, vector<string>> no_expected_args;
101 SuccessfullyParseRcWithExpectedArgs("whitespace.bazelrc", no_expected_args);
102}
103
104TEST_F(RcOptionsTest, CommentedStartup) {
105 WriteRc("commented_startup.bazelrc",
106 "# startup foo");
107 unordered_map<string, vector<string>> no_expected_args;
108 SuccessfullyParseRcWithExpectedArgs("whitespace.bazelrc", no_expected_args);
109}
110
111TEST_F(RcOptionsTest, EmptyStartupLine) {
112 WriteRc("empty_startup_line.bazelrc",
113 "startup");
114 unordered_map<string, vector<string>> no_expected_args;
115 SuccessfullyParseRcWithExpectedArgs("empty_startup_line.bazelrc",
116 no_expected_args);
117}
118
119TEST_F(RcOptionsTest, StartupWithOnlyCommentedArg) {
120 WriteRc("startup_with_comment.bazelrc",
121 "startup # bar");
122 unordered_map<string, vector<string>> no_expected_args;
123 SuccessfullyParseRcWithExpectedArgs("startup_with_comment.bazelrc",
124 no_expected_args);
125}
126
ccalvarin58acb7b2018-03-08 10:53:24 -0800127TEST_F(RcOptionsTest, SingleStartupArg) {
128 WriteRc("startup_foo.bazelrc",
129 "startup foo");
130 SuccessfullyParseRcWithExpectedArgs(
131 "startup_foo.bazelrc",
132 {{"startup", {"foo"}}});
133}
134
135TEST_F(RcOptionsTest, SingleStartupArgWithComment) {
136 WriteRc("startup_foo_and_comment.bazelrc",
137 "startup foo # comment");
138 SuccessfullyParseRcWithExpectedArgs(
139 "startup_foo_and_comment.bazelrc",
140 {{"startup", {"foo"}}});
141}
142
143TEST_F(RcOptionsTest, TwoStartupArgsOnOneLine) {
144 WriteRc("startup_foo_bar.bazelrc",
145 "startup foo bar");
146 SuccessfullyParseRcWithExpectedArgs(
147 "startup_foo_bar.bazelrc",
148 {{"startup", {"foo", "bar"}}});
149}
150
151TEST_F(RcOptionsTest, TwoStartupArgsOnOneLineTabSeparated) {
152 WriteRc("startup_with_tabs.bazelrc",
153 "startup\tfoo\tbar");
154 SuccessfullyParseRcWithExpectedArgs(
155 "startup_with_tabs.bazelrc",
156 {{"startup", {"foo", "bar"}}});
157}
158
159TEST_F(RcOptionsTest, StartupOptWithSimpleValue) {
160 WriteRc("startup_opt_with_simple_value.bazelrc",
161 "startup --opt=foo");
162 SuccessfullyParseRcWithExpectedArgs(
163 "startup_opt_with_simple_value.bazelrc",
164 {{"startup", {"--opt=foo"}}});
165}
166
167TEST_F(RcOptionsTest, StartupQuotedArg) {
168 WriteRc("startup_quoted_foo_bar.bazelrc",
169 "startup \"foo bar\"");
170 SuccessfullyParseRcWithExpectedArgs(
171 "startup_quoted_foo_bar.bazelrc",
172 {{"startup", {"foo bar"}}});
173}
174
175TEST_F(RcOptionsTest, QuotedValueStartupArgAfterEquals) {
176 WriteRc("startup_opt_quoted_arg.bazelrc",
177 "startup --opt=\"foo bar\"");
178 SuccessfullyParseRcWithExpectedArgs(
179 "startup_opt_quoted_arg.bazelrc",
180 {{"startup", {"--opt=foo bar"}}});
181}
182
183TEST_F(RcOptionsTest, QuotedValueStartupArgAfterWhitespace) {
184 WriteRc("startup_opt_quoted_arg_as_separate_token.bazelrc",
185 "startup --opt \"foo bar\"");
186 SuccessfullyParseRcWithExpectedArgs(
187 "startup_opt_quoted_arg_as_separate_token.bazelrc",
188 {{"startup", {"--opt", "foo bar"}}});
189}
190
191TEST_F(RcOptionsTest, QuotedValueStartupArgOnNewLine) {
192 WriteRc("startup_opt_quoted_arg_different_line.bazelrc",
193 "startup --opt\n"
194 "startup \"foo bar\"");
195 SuccessfullyParseRcWithExpectedArgs(
196 "startup_opt_quoted_arg_different_line.bazelrc",
197 {{"startup", {"--opt", "foo bar"}}});
198}
199
200TEST_F(RcOptionsTest, TwoOptStartup) {
201 WriteRc("startup_two_options.bazelrc",
202 "startup --opt1\n"
203 "startup --opt2");
204 SuccessfullyParseRcWithExpectedArgs(
205 "startup_two_options.bazelrc",
206 {{"startup", {"--opt1", "--opt2"}}});
207}
208
209TEST_F(RcOptionsTest, WhitespaceBeforeStartup) {
210 WriteRc("whitespace_before_command.bazelrc",
211 " startup foo\n"
212 " # indented comments\n"
213 "startup bar\n"
214 "\tstartup \t baz");
215 SuccessfullyParseRcWithExpectedArgs(
216 "whitespace_before_command.bazelrc",
217 {{"startup", {"foo", "bar", "baz"}}});
218}
219
220TEST_F(RcOptionsTest, StartupLineContinuation) {
221 WriteRc("startup_line_continuation.bazelrc",
222 "startup \\\n"
223 "foo\n"
224 "startup bar \\\n"
225 "baz");
226 SuccessfullyParseRcWithExpectedArgs(
227 "startup_line_continuation.bazelrc",
228 {{"startup", {"foo", "bar", "baz"}}});
229}
230
231TEST_F(RcOptionsTest, ManyArgStartup) {
232 WriteRc("startup_with_many_args.bazelrc",
233 "# Many arguments\n"
234 "startup foo # First argument has reasons.\n"
235 "startup --opt1 --opt2 # These arguments are split wide\n"
236 "#startup --this_is_not_an_arg\n"
237 "\n\n\n # A few empty lines for good measure\n"
238 "startup\t \"string input Value 123. \" --bar");
239 SuccessfullyParseRcWithExpectedArgs(
240 "startup_with_many_args.bazelrc",
241 {{
242 "startup",
243 {"foo", "--opt1", "--opt2", "string input Value 123. ", "--bar"}
244 }});
245}
246
ccalvarin58acb7b2018-03-08 10:53:24 -0800247TEST_F(RcOptionsTest, MultipleCommands) {
248 WriteRc("multiple_commands_intermixed.bazelrc",
249 "startup foo\n"
250 "build aaa\n"
251 "startup bar baz\n"
252 "build bbb\n"
253 "build ccc\n");
254 SuccessfullyParseRcWithExpectedArgs(
255 "multiple_commands_intermixed.bazelrc",
256 {{"startup", {"foo", "bar", "baz"}}, {"build", {"aaa", "bbb", "ccc"}}});
257}
258
ccalvarin58acb7b2018-03-08 10:53:24 -0800259TEST_F(RcOptionsTest, SimpleImportFoo) {
260 WriteRc("startup_foo.bazelrc",
261 "startup foo");
262 WriteRc("import_simple.bazelrc",
263 "import %workspace%/startup_foo.bazelrc");
264 SuccessfullyParseRcWithExpectedArgs(
265 "import_simple.bazelrc",
266 {{"startup", {"foo"}}});
267}
268
269TEST_F(RcOptionsTest, ImportFooThenAddBar) {
270 WriteRc("startup_foo.bazelrc",
271 "startup foo");
272 WriteRc("import_foo_then_bar.bazelrc",
273 "import %workspace%/startup_foo.bazelrc\n"
274 "startup bar");
275 SuccessfullyParseRcWithExpectedArgs(
276 "import_foo_then_bar.bazelrc",
277 {{"startup", {"foo", "bar"}}});
278}
279
280TEST_F(RcOptionsTest, StartupBarThenImportFoo) {
281 WriteRc("startup_foo.bazelrc",
282 "startup foo");
283 WriteRc("bar_then_import_foo.bazelrc",
284 "startup bar\n"
285 "import %workspace%/startup_foo.bazelrc");
286 SuccessfullyParseRcWithExpectedArgs(
287 "bar_then_import_foo.bazelrc",
288 {{"startup", {"bar", "foo"}}});
289}
290
ccalvarinf03acca2018-09-06 14:25:27 -0700291TEST_F(RcOptionsTest, SimpleTryImportFoo) {
292 WriteRc("startup_foo.bazelrc", "startup foo");
293 WriteRc("import_simple.bazelrc",
294 "try-import %workspace%/startup_foo.bazelrc");
295 SuccessfullyParseRcWithExpectedArgs("import_simple.bazelrc",
296 {{"startup", {"foo"}}});
297}
298
299TEST_F(RcOptionsTest, ImportTryFooThenAddBar) {
300 WriteRc("startup_foo.bazelrc", "startup foo");
301 WriteRc("import_foo_then_bar.bazelrc",
302 "try-import %workspace%/startup_foo.bazelrc\n"
303 "startup bar");
304 SuccessfullyParseRcWithExpectedArgs("import_foo_then_bar.bazelrc",
305 {{"startup", {"foo", "bar"}}});
306}
307
308TEST_F(RcOptionsTest, StartupBarThenTryImportFoo) {
309 WriteRc("startup_foo.bazelrc", "startup foo");
310 WriteRc("bar_then_import_foo.bazelrc",
311 "startup bar\n"
312 "try-import %workspace%/startup_foo.bazelrc");
313 SuccessfullyParseRcWithExpectedArgs("bar_then_import_foo.bazelrc",
314 {{"startup", {"bar", "foo"}}});
315}
316
317// Most likely, import diamonds like this are unintended, and they might lead
318// to surprising doubled values for allow_multiple options. This causes a
319// warning in option_processor, which checks for duplicates across multiple rc
320// files.
ccalvarin58acb7b2018-03-08 10:53:24 -0800321TEST_F(RcOptionsTest, ImportDiamond) {
322 WriteRc("startup_foo.bazelrc",
323 "startup foo");
324 WriteRc("import_foo_then_bar.bazelrc",
325 "import %workspace%/startup_foo.bazelrc\n"
326 "startup bar");
327 WriteRc("bar_then_import_foo.bazelrc",
328 "startup bar\n"
329 "import %workspace%/startup_foo.bazelrc");
330 WriteRc("import_diamond.bazelrc",
331 "import %workspace%/import_foo_then_bar.bazelrc\n"
332 "import %workspace%/bar_then_import_foo.bazelrc");
333 SuccessfullyParseRcWithExpectedArgs(
334 "import_diamond.bazelrc",
335 {{"startup", {"foo", "bar", "bar", "foo"}}});
336}
337
ccalvarin58acb7b2018-03-08 10:53:24 -0800338
339TEST_F(RcOptionsTest, ImportCycleFails) {
340 WriteRc("import_cycle_1.bazelrc",
341 "import %workspace%/import_cycle_2.bazelrc");
342 WriteRc("import_cycle_2.bazelrc",
343 "import %workspace%/import_cycle_1.bazelrc");
344
345 RcFile::ParseError error;
346 string error_text;
347 std::unique_ptr<RcFile> rc =
348 Parse("import_cycle_1.bazelrc", &error, &error_text);
349 EXPECT_EQ(error, RcFile::ParseError::IMPORT_LOOP);
ccalvarin8ceaa652018-08-24 12:44:31 -0700350 ASSERT_THAT(
351 error_text,
352 MatchesRegex("Import loop detected:\n"
353 " .*import_cycle_1.bazelrc\n"
354 " .*import_cycle_2.bazelrc\n"
355 " .*import_cycle_1.bazelrc\n"));
ccalvarin58acb7b2018-03-08 10:53:24 -0800356}
357
358TEST_F(RcOptionsTest, LongImportCycleFails) {
359 WriteRc("chain_to_cycle_1.bazelrc",
360 "import %workspace%/chain_to_cycle_2.bazelrc");
361 WriteRc("chain_to_cycle_2.bazelrc",
362 "import %workspace%/chain_to_cycle_3.bazelrc");
363 WriteRc("chain_to_cycle_3.bazelrc",
364 "import %workspace%/chain_to_cycle_4.bazelrc");
365 WriteRc("chain_to_cycle_4.bazelrc",
366 "import %workspace%/import_cycle_1.bazelrc");
367 WriteRc("import_cycle_1.bazelrc",
368 "import %workspace%/import_cycle_2.bazelrc");
369 WriteRc("import_cycle_2.bazelrc",
370 "import %workspace%/import_cycle_1.bazelrc");
371
372 RcFile::ParseError error;
373 string error_text;
374 std::unique_ptr<RcFile> rc =
375 Parse("chain_to_cycle_1.bazelrc", &error, &error_text);
376 EXPECT_EQ(error, RcFile::ParseError::IMPORT_LOOP);
ccalvarin8ceaa652018-08-24 12:44:31 -0700377 ASSERT_THAT(
378 error_text,
379 MatchesRegex("Import loop detected:\n"
380 " .*chain_to_cycle_1.bazelrc\n"
381 " .*chain_to_cycle_2.bazelrc\n"
382 " .*chain_to_cycle_3.bazelrc\n"
383 " .*chain_to_cycle_4.bazelrc\n"
384 " .*import_cycle_1.bazelrc\n"
385 " .*import_cycle_2.bazelrc\n"
386 " .*import_cycle_1.bazelrc\n"));
ccalvarin58acb7b2018-03-08 10:53:24 -0800387}
388
389TEST_F(RcOptionsTest, FileDoesNotExist) {
390 RcFile::ParseError error;
391 string error_text;
392 std::unique_ptr<RcFile> rc = Parse("not_a_file.bazelrc", &error, &error_text);
393 EXPECT_EQ(error, RcFile::ParseError::UNREADABLE_FILE);
ccalvarin8ceaa652018-08-24 12:44:31 -0700394 ASSERT_THAT(
395 error_text,
396 MatchesRegex(
397 "Unexpected error reading .blazerc file '.*not_a_file.bazelrc'"));
ccalvarin58acb7b2018-03-08 10:53:24 -0800398}
399
400TEST_F(RcOptionsTest, ImportedFileDoesNotExist) {
401 WriteRc("import_fake_file.bazelrc",
402 "import somefile");
403
404 RcFile::ParseError error;
405 string error_text;
406 std::unique_ptr<RcFile> rc =
407 Parse("import_fake_file.bazelrc", &error, &error_text);
408 EXPECT_EQ(error, RcFile::ParseError::UNREADABLE_FILE);
409 ASSERT_EQ(error_text, "Unexpected error reading .blazerc file 'somefile'");
410}
411
ccalvarinf03acca2018-09-06 14:25:27 -0700412TEST_F(RcOptionsTest, TryImportedFileDoesNotExist) {
413 WriteRc("try_import_fake_file.bazelrc", "try-import somefile");
414
415 unordered_map<string, vector<string>> no_expected_args;
416 SuccessfullyParseRcWithExpectedArgs("try_import_fake_file.bazelrc",
417 no_expected_args);
418}
419
ccalvarin58acb7b2018-03-08 10:53:24 -0800420TEST_F(RcOptionsTest, ImportHasTooManyArgs) {
421 WriteRc("bad_import.bazelrc",
422 "import somefile bar");
423
424 RcFile::ParseError error;
425 string error_text;
426 std::unique_ptr<RcFile> rc = Parse("bad_import.bazelrc", &error, &error_text);
427 EXPECT_EQ(error, RcFile::ParseError::INVALID_FORMAT);
ccalvarin8ceaa652018-08-24 12:44:31 -0700428 ASSERT_THAT(
429 error_text,
430 MatchesRegex("Invalid import declaration in .blazerc file "
431 "'.*bad_import.bazelrc': 'import somefile bar' \\(are you "
432 "in your source checkout/WORKSPACE\\?\\)"));
ccalvarin58acb7b2018-03-08 10:53:24 -0800433}
434
ccalvarinf03acca2018-09-06 14:25:27 -0700435TEST_F(RcOptionsTest, TryImportHasTooManyArgs) {
436 WriteRc("bad_import.bazelrc", "try-import somefile bar");
437
438 RcFile::ParseError error;
439 string error_text;
440 std::unique_ptr<RcFile> rc = Parse("bad_import.bazelrc", &error, &error_text);
441 EXPECT_EQ(error, RcFile::ParseError::INVALID_FORMAT);
442 ASSERT_THAT(
443 error_text,
444 MatchesRegex("Invalid import declaration in .blazerc file "
445 "'.*bad_import.bazelrc': 'try-import somefile bar' \\(are "
446 "you in your source checkout/WORKSPACE\\?\\)"));
447}
448
ccalvarin58acb7b2018-03-08 10:53:24 -0800449// TODO(b/34811299) The tests below identify ways that '\' used as a line
450// continuation is broken. This is on top of user-reported cases where an
451// unintentional '\' made the command on the following line show up as
452// an argument, which lead to cryptic messages. There is no value added by '\',
453// since the following line could just repeat the command, so it might be best
454// to remove this feature entirely.
455//
456// For now, these tests serve as documentation of the brokenness, and to keep
457// broken behavior consistent before we get around to fixing it.
458
459TEST_F(RcOptionsTest, BadStartupLineContinuation_HasWhitespaceAfterSlash) {
460 WriteRc("bad_startup_line_continuation.bazelrc",
461 "startup foo \\ \n"
462 "bar");
463 SuccessfullyParseRcWithExpectedArgs(
464 "bad_startup_line_continuation.bazelrc",
465 {{"startup", {"foo"}}}); // Does not contain "bar" from the next line.
466}
467
468TEST_F(RcOptionsTest, BadStartupLineContinuation_HasErroneousSlash) {
469 WriteRc("bad_startup_line_continuation.bazelrc",
470 "startup foo \\ bar");
471 SuccessfullyParseRcWithExpectedArgs(
472 "bad_startup_line_continuation.bazelrc",
473 // Whitespace between the slash and bar gets counted as part of the token.
474 {{"startup", {"foo", " bar"}}});
475}
476
477TEST_F(RcOptionsTest, BadStartupLineContinuation_HasCommentAfterSlash) {
478 WriteRc("bad_startup_line_continuation.bazelrc",
479 "startup foo \\ # comment\n"
480 "bar");
481 SuccessfullyParseRcWithExpectedArgs(
482 "bad_startup_line_continuation.bazelrc",
483 // Whitespace between the slash and comment gets counted as a new token,
484 // and the bar on the next line is ignored (it's an argumentless command).
485 {{"startup", {"foo", " "}}});
486}
487
488TEST_F(RcOptionsTest, BadStartupLineContinuation_InterpretsNextLineAsNewline) {
489 WriteRc("bad_startup_line_continuation.bazelrc",
490 "startup foo \\ #comment\n"
491 "bar baz");
492 SuccessfullyParseRcWithExpectedArgs(
493 "bad_startup_line_continuation.bazelrc",
494 // Whitespace between the slash and comment gets counted as a new token,
495 // and the bar on the next line treated as its own command, instead of as
496 // a "startup" args.
497 {{"startup", {"foo", " "}}, {"bar", {"baz"}}});
498}
499
500} // namespace blaze
501