Port Android relocation packer to chromium build
[chromium-blink-merge.git] / tools / gn / parser_unittest.cc
blob6216ee4d6b178a06a91092593be0896093f7a247
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include <iostream>
6 #include <sstream>
8 #include "testing/gtest/include/gtest/gtest.h"
9 #include "tools/gn/input_file.h"
10 #include "tools/gn/parser.h"
11 #include "tools/gn/tokenizer.h"
13 namespace {
15 bool GetTokens(const InputFile* input, std::vector<Token>* result) {
16 result->clear();
17 Err err;
18 *result = Tokenizer::Tokenize(input, &err);
19 return !err.has_error();
22 void DoParserPrintTest(const char* input, const char* expected) {
23 std::vector<Token> tokens;
24 InputFile input_file(SourceFile("/test"));
25 input_file.SetContents(input);
26 ASSERT_TRUE(GetTokens(&input_file, &tokens));
28 Err err;
29 scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
30 if (!result)
31 err.PrintToStdout();
32 ASSERT_TRUE(result);
34 std::ostringstream collector;
35 result->Print(collector, 0);
37 EXPECT_EQ(expected, collector.str());
40 void DoExpressionPrintTest(const char* input, const char* expected) {
41 std::vector<Token> tokens;
42 InputFile input_file(SourceFile("/test"));
43 input_file.SetContents(input);
44 ASSERT_TRUE(GetTokens(&input_file, &tokens));
46 Err err;
47 scoped_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
48 ASSERT_TRUE(result);
50 std::ostringstream collector;
51 result->Print(collector, 0);
53 EXPECT_EQ(expected, collector.str());
56 // Expects the tokenizer or parser to identify an error at the given line and
57 // character.
58 void DoParserErrorTest(const char* input, int err_line, int err_char) {
59 InputFile input_file(SourceFile("/test"));
60 input_file.SetContents(input);
62 Err err;
63 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
64 if (!err.has_error()) {
65 scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
66 ASSERT_FALSE(result);
67 ASSERT_TRUE(err.has_error());
70 EXPECT_EQ(err_line, err.location().line_number());
71 EXPECT_EQ(err_char, err.location().char_offset());
74 // Expects the tokenizer or parser to identify an error at the given line and
75 // character.
76 void DoExpressionErrorTest(const char* input, int err_line, int err_char) {
77 InputFile input_file(SourceFile("/test"));
78 input_file.SetContents(input);
80 Err err;
81 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
82 if (!err.has_error()) {
83 scoped_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
84 ASSERT_FALSE(result);
85 ASSERT_TRUE(err.has_error());
88 EXPECT_EQ(err_line, err.location().line_number());
89 EXPECT_EQ(err_char, err.location().char_offset());
92 } // namespace
94 TEST(Parser, Literal) {
95 DoExpressionPrintTest("5", "LITERAL(5)\n");
96 DoExpressionPrintTest("\"stuff\"", "LITERAL(\"stuff\")\n");
99 TEST(Parser, BinaryOp) {
100 // TODO(scottmg): The tokenizer is dumb, and treats "5-1" as two integers,
101 // not a binary operator between two positive integers.
102 DoExpressionPrintTest("5 - 1",
103 "BINARY(-)\n"
104 " LITERAL(5)\n"
105 " LITERAL(1)\n");
106 DoExpressionPrintTest("5+1",
107 "BINARY(+)\n"
108 " LITERAL(5)\n"
109 " LITERAL(1)\n");
110 DoExpressionPrintTest("5 - 1 - 2",
111 "BINARY(-)\n"
112 " BINARY(-)\n"
113 " LITERAL(5)\n"
114 " LITERAL(1)\n"
115 " LITERAL(2)\n");
118 TEST(Parser, FunctionCall) {
119 DoExpressionPrintTest("foo()",
120 "FUNCTION(foo)\n"
121 " LIST\n");
122 DoExpressionPrintTest("blah(1, 2)",
123 "FUNCTION(blah)\n"
124 " LIST\n"
125 " LITERAL(1)\n"
126 " LITERAL(2)\n");
127 DoExpressionErrorTest("foo(1, 2,)", 1, 10);
128 DoExpressionErrorTest("foo(1 2)", 1, 7);
131 TEST(Parser, ParenExpression) {
132 const char* input = "(foo(1)) + (a + (b - c) + d)";
133 const char* expected =
134 "BINARY(+)\n"
135 " FUNCTION(foo)\n"
136 " LIST\n"
137 " LITERAL(1)\n"
138 " BINARY(+)\n"
139 " BINARY(+)\n"
140 " IDENTIFIER(a)\n"
141 " BINARY(-)\n"
142 " IDENTIFIER(b)\n"
143 " IDENTIFIER(c)\n"
144 " IDENTIFIER(d)\n";
145 DoExpressionPrintTest(input, expected);
146 DoExpressionErrorTest("(a +", 1, 4);
149 TEST(Parser, OrderOfOperationsLeftAssociative) {
150 const char* input = "5 - 1 - 2\n";
151 const char* expected =
152 "BINARY(-)\n"
153 " BINARY(-)\n"
154 " LITERAL(5)\n"
155 " LITERAL(1)\n"
156 " LITERAL(2)\n";
157 DoExpressionPrintTest(input, expected);
160 TEST(Parser, OrderOfOperationsEqualityBoolean) {
161 const char* input =
162 "if (a == \"b\" && is_stuff) {\n"
163 " print(\"hai\")\n"
164 "}\n";
165 const char* expected =
166 "BLOCK\n"
167 " CONDITION\n"
168 " BINARY(&&)\n"
169 " BINARY(==)\n"
170 " IDENTIFIER(a)\n"
171 " LITERAL(\"b\")\n"
172 " IDENTIFIER(is_stuff)\n"
173 " BLOCK\n"
174 " FUNCTION(print)\n"
175 " LIST\n"
176 " LITERAL(\"hai\")\n";
177 DoParserPrintTest(input, expected);
180 TEST(Parser, UnaryOp) {
181 DoExpressionPrintTest("!foo",
182 "UNARY(!)\n"
183 " IDENTIFIER(foo)\n");
186 TEST(Parser, List) {
187 DoExpressionPrintTest("[]", "LIST\n");
188 DoExpressionPrintTest("[1,asd,]",
189 "LIST\n"
190 " LITERAL(1)\n"
191 " IDENTIFIER(asd)\n");
192 DoExpressionPrintTest("[1, 2+3 - foo]",
193 "LIST\n"
194 " LITERAL(1)\n"
195 " BINARY(-)\n"
196 " BINARY(+)\n"
197 " LITERAL(2)\n"
198 " LITERAL(3)\n"
199 " IDENTIFIER(foo)\n");
200 DoExpressionPrintTest("[1,\n2,\n 3,\n 4]",
201 "LIST\n"
202 " LITERAL(1)\n"
203 " LITERAL(2)\n"
204 " LITERAL(3)\n"
205 " LITERAL(4)\n");
207 DoExpressionErrorTest("[a, 2+,]", 1, 6);
208 DoExpressionErrorTest("[,]", 1, 2);
209 DoExpressionErrorTest("[a,,]", 1, 4);
212 TEST(Parser, Assignment) {
213 DoParserPrintTest("a=2",
214 "BLOCK\n"
215 " BINARY(=)\n"
216 " IDENTIFIER(a)\n"
217 " LITERAL(2)\n");
220 TEST(Parser, Accessor) {
221 // Accessor indexing.
222 DoParserPrintTest("a=b[c+2]",
223 "BLOCK\n"
224 " BINARY(=)\n"
225 " IDENTIFIER(a)\n"
226 " ACCESSOR\n"
227 " b\n" // AccessorNode is a bit weird in that it holds
228 // a Token, not a ParseNode for the base.
229 " BINARY(+)\n"
230 " IDENTIFIER(c)\n"
231 " LITERAL(2)\n");
232 DoParserErrorTest("a = b[1][0]", 1, 5);
234 // Member accessors.
235 DoParserPrintTest("a=b.c+2",
236 "BLOCK\n"
237 " BINARY(=)\n"
238 " IDENTIFIER(a)\n"
239 " BINARY(+)\n"
240 " ACCESSOR\n"
241 " b\n"
242 " IDENTIFIER(c)\n"
243 " LITERAL(2)\n");
244 DoParserErrorTest("a = b.c.d", 1, 6); // Can't nest accessors (currently).
245 DoParserErrorTest("a.b = 5", 1, 1); // Can't assign to accessors (currently).
248 TEST(Parser, Condition) {
249 DoParserPrintTest("if(1) { a = 2 }",
250 "BLOCK\n"
251 " CONDITION\n"
252 " LITERAL(1)\n"
253 " BLOCK\n"
254 " BINARY(=)\n"
255 " IDENTIFIER(a)\n"
256 " LITERAL(2)\n");
258 DoParserPrintTest("if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }",
259 "BLOCK\n"
260 " CONDITION\n"
261 " LITERAL(1)\n"
262 " BLOCK\n"
263 " BINARY(=)\n"
264 " IDENTIFIER(a)\n"
265 " LITERAL(2)\n"
266 " CONDITION\n"
267 " LITERAL(0)\n"
268 " BLOCK\n"
269 " BINARY(=)\n"
270 " IDENTIFIER(a)\n"
271 " LITERAL(3)\n"
272 " BLOCK\n"
273 " BINARY(=)\n"
274 " IDENTIFIER(a)\n"
275 " LITERAL(4)\n");
278 TEST(Parser, OnlyCallAndAssignInBody) {
279 DoParserErrorTest("[]", 1, 2);
280 DoParserErrorTest("3 + 4", 1, 5);
281 DoParserErrorTest("6 - 7", 1, 5);
282 DoParserErrorTest("if (1) { 5 } else { print(4) }", 1, 12);
285 TEST(Parser, NoAssignmentInCondition) {
286 DoParserErrorTest("if (a=2) {}", 1, 5);
289 TEST(Parser, CompleteFunction) {
290 const char* input =
291 "cc_test(\"foo\") {\n"
292 " sources = [\n"
293 " \"foo.cc\",\n"
294 " \"foo.h\"\n"
295 " ]\n"
296 " dependencies = [\n"
297 " \"base\"\n"
298 " ]\n"
299 "}\n";
300 const char* expected =
301 "BLOCK\n"
302 " FUNCTION(cc_test)\n"
303 " LIST\n"
304 " LITERAL(\"foo\")\n"
305 " BLOCK\n"
306 " BINARY(=)\n"
307 " IDENTIFIER(sources)\n"
308 " LIST\n"
309 " LITERAL(\"foo.cc\")\n"
310 " LITERAL(\"foo.h\")\n"
311 " BINARY(=)\n"
312 " IDENTIFIER(dependencies)\n"
313 " LIST\n"
314 " LITERAL(\"base\")\n";
315 DoParserPrintTest(input, expected);
318 TEST(Parser, FunctionWithConditional) {
319 const char* input =
320 "cc_test(\"foo\") {\n"
321 " sources = [\"foo.cc\"]\n"
322 " if (OS == \"mac\") {\n"
323 " sources += \"bar.cc\"\n"
324 " } else if (OS == \"win\") {\n"
325 " sources -= [\"asd.cc\", \"foo.cc\"]\n"
326 " } else {\n"
327 " dependencies += [\"bar.cc\"]\n"
328 " }\n"
329 "}\n";
330 const char* expected =
331 "BLOCK\n"
332 " FUNCTION(cc_test)\n"
333 " LIST\n"
334 " LITERAL(\"foo\")\n"
335 " BLOCK\n"
336 " BINARY(=)\n"
337 " IDENTIFIER(sources)\n"
338 " LIST\n"
339 " LITERAL(\"foo.cc\")\n"
340 " CONDITION\n"
341 " BINARY(==)\n"
342 " IDENTIFIER(OS)\n"
343 " LITERAL(\"mac\")\n"
344 " BLOCK\n"
345 " BINARY(+=)\n"
346 " IDENTIFIER(sources)\n"
347 " LITERAL(\"bar.cc\")\n"
348 " CONDITION\n"
349 " BINARY(==)\n"
350 " IDENTIFIER(OS)\n"
351 " LITERAL(\"win\")\n"
352 " BLOCK\n"
353 " BINARY(-=)\n"
354 " IDENTIFIER(sources)\n"
355 " LIST\n"
356 " LITERAL(\"asd.cc\")\n"
357 " LITERAL(\"foo.cc\")\n"
358 " BLOCK\n"
359 " BINARY(+=)\n"
360 " IDENTIFIER(dependencies)\n"
361 " LIST\n"
362 " LITERAL(\"bar.cc\")\n";
363 DoParserPrintTest(input, expected);
366 TEST(Parser, UnterminatedBlock) {
367 DoParserErrorTest("stuff() {", 1, 9);
370 TEST(Parser, BadlyTerminatedNumber) {
371 DoParserErrorTest("1234z", 1, 5);
374 TEST(Parser, NewlinesInUnusualPlaces) {
375 DoParserPrintTest(
376 "if\n"
377 "(\n"
378 "a\n"
379 ")\n"
380 "{\n"
381 "}\n",
382 "BLOCK\n"
383 " CONDITION\n"
384 " IDENTIFIER(a)\n"
385 " BLOCK\n");
388 TEST(Parser, NewlinesInUnusualPlaces2) {
389 DoParserPrintTest(
390 "a\n=\n2\n",
391 "BLOCK\n"
392 " BINARY(=)\n"
393 " IDENTIFIER(a)\n"
394 " LITERAL(2)\n");
395 DoParserPrintTest(
396 "x =\ny if\n(1\n) {}",
397 "BLOCK\n"
398 " BINARY(=)\n"
399 " IDENTIFIER(x)\n"
400 " IDENTIFIER(y)\n"
401 " CONDITION\n"
402 " LITERAL(1)\n"
403 " BLOCK\n");
404 DoParserPrintTest(
405 "x = 3\n+2",
406 "BLOCK\n"
407 " BINARY(=)\n"
408 " IDENTIFIER(x)\n"
409 " BINARY(+)\n"
410 " LITERAL(3)\n"
411 " LITERAL(2)\n"
415 TEST(Parser, NewlineBeforeSubscript) {
416 const char* input = "a = b[1]";
417 const char* input_with_newline = "a = b\n[1]";
418 const char* expected =
419 "BLOCK\n"
420 " BINARY(=)\n"
421 " IDENTIFIER(a)\n"
422 " ACCESSOR\n"
423 " b\n"
424 " LITERAL(1)\n";
425 DoParserPrintTest(
426 input,
427 expected);
428 DoParserPrintTest(
429 input_with_newline,
430 expected);
433 TEST(Parser, SequenceOfExpressions) {
434 DoParserPrintTest(
435 "a = 1 b = 2",
436 "BLOCK\n"
437 " BINARY(=)\n"
438 " IDENTIFIER(a)\n"
439 " LITERAL(1)\n"
440 " BINARY(=)\n"
441 " IDENTIFIER(b)\n"
442 " LITERAL(2)\n");
445 TEST(Parser, BlockAfterFunction) {
446 const char* input = "func(\"stuff\") {\n}";
447 // TODO(scottmg): Do we really want these to mean different things?
448 const char* input_with_newline = "func(\"stuff\")\n{\n}";
449 const char* expected =
450 "BLOCK\n"
451 " FUNCTION(func)\n"
452 " LIST\n"
453 " LITERAL(\"stuff\")\n"
454 " BLOCK\n";
455 DoParserPrintTest(input, expected);
456 DoParserPrintTest(input_with_newline, expected);
459 TEST(Parser, LongExpression) {
460 const char* input = "a = b + c && d || e";
461 const char* expected =
462 "BLOCK\n"
463 " BINARY(=)\n"
464 " IDENTIFIER(a)\n"
465 " BINARY(||)\n"
466 " BINARY(&&)\n"
467 " BINARY(+)\n"
468 " IDENTIFIER(b)\n"
469 " IDENTIFIER(c)\n"
470 " IDENTIFIER(d)\n"
471 " IDENTIFIER(e)\n";
472 DoParserPrintTest(input, expected);
475 TEST(Parser, CommentsStandalone) {
476 const char* input =
477 "# Toplevel comment.\n"
478 "\n"
479 "executable(\"wee\") {}\n";
480 const char* expected =
481 "BLOCK\n"
482 " BLOCK_COMMENT(# Toplevel comment.)\n"
483 " FUNCTION(executable)\n"
484 " LIST\n"
485 " LITERAL(\"wee\")\n"
486 " BLOCK\n";
487 DoParserPrintTest(input, expected);
490 TEST(Parser, CommentsStandaloneEof) {
491 const char* input =
492 "executable(\"wee\") {}\n"
493 "# EOF comment.\n";
494 const char* expected =
495 "BLOCK\n"
496 " +AFTER_COMMENT(\"# EOF comment.\")\n"
497 " FUNCTION(executable)\n"
498 " LIST\n"
499 " LITERAL(\"wee\")\n"
500 " BLOCK\n";
501 DoParserPrintTest(input, expected);
504 TEST(Parser, CommentsLineAttached) {
505 const char* input =
506 "executable(\"wee\") {\n"
507 " # Some sources.\n"
508 " sources = [\n"
509 " \"stuff.cc\",\n"
510 " \"things.cc\",\n"
511 " # This file is special or something.\n"
512 " \"another.cc\",\n"
513 " ]\n"
514 "}\n";
515 const char* expected =
516 "BLOCK\n"
517 " FUNCTION(executable)\n"
518 " LIST\n"
519 " LITERAL(\"wee\")\n"
520 " BLOCK\n"
521 " BINARY(=)\n"
522 " +BEFORE_COMMENT(\"# Some sources.\")\n"
523 " IDENTIFIER(sources)\n"
524 " LIST\n"
525 " LITERAL(\"stuff.cc\")\n"
526 " LITERAL(\"things.cc\")\n"
527 " LITERAL(\"another.cc\")\n"
528 " +BEFORE_COMMENT(\"# This file is special or something.\")\n";
529 DoParserPrintTest(input, expected);
532 TEST(Parser, CommentsSuffix) {
533 const char* input =
534 "executable(\"wee\") { # This is some stuff.\n"
535 "sources = [ \"a.cc\" # And another comment here.\n"
536 "] }";
537 const char* expected =
538 "BLOCK\n"
539 " FUNCTION(executable)\n"
540 " LIST\n"
541 " LITERAL(\"wee\")\n"
542 " END())\n"
543 " +SUFFIX_COMMENT(\"# This is some stuff.\")\n"
544 " BLOCK\n"
545 " BINARY(=)\n"
546 " IDENTIFIER(sources)\n"
547 " LIST\n"
548 " LITERAL(\"a.cc\")\n"
549 " +SUFFIX_COMMENT(\"# And another comment here.\")\n";
550 DoParserPrintTest(input, expected);
553 TEST(Parser, CommentsSuffixDifferentLine) {
554 const char* input =
555 "executable(\"wee\") {\n"
556 " sources = [ \"a\",\n"
557 " \"b\" ] # Comment\n"
558 "}\n";
559 const char* expected =
560 "BLOCK\n"
561 " FUNCTION(executable)\n"
562 " LIST\n"
563 " LITERAL(\"wee\")\n"
564 " BLOCK\n"
565 " BINARY(=)\n"
566 " IDENTIFIER(sources)\n"
567 " LIST\n"
568 " LITERAL(\"a\")\n"
569 " LITERAL(\"b\")\n"
570 " END(])\n"
571 " +SUFFIX_COMMENT(\"# Comment\")\n";
572 DoParserPrintTest(input, expected);
575 TEST(Parser, CommentsSuffixMultiple) {
576 const char* input =
577 "executable(\"wee\") {\n"
578 " sources = [\n"
579 " \"a\", # This is a comment,\n"
580 " # and some more,\n" // Note that this is aligned with above.
581 " # then the end.\n"
582 " ]\n"
583 "}\n";
584 const char* expected =
585 "BLOCK\n"
586 " FUNCTION(executable)\n"
587 " LIST\n"
588 " LITERAL(\"wee\")\n"
589 " BLOCK\n"
590 " BINARY(=)\n"
591 " IDENTIFIER(sources)\n"
592 " LIST\n"
593 " LITERAL(\"a\")\n"
594 " +SUFFIX_COMMENT(\"# This is a comment,\")\n"
595 " +SUFFIX_COMMENT(\"# and some more,\")\n"
596 " +SUFFIX_COMMENT(\"# then the end.\")\n";
597 DoParserPrintTest(input, expected);
600 TEST(Parser, CommentsConnectedInList) {
601 const char* input =
602 "defines = [\n"
603 "\n"
604 " # Connected comment.\n"
605 " \"WEE\",\n"
606 " \"BLORPY\",\n"
607 "]\n";
608 const char* expected =
609 "BLOCK\n"
610 " BINARY(=)\n"
611 " IDENTIFIER(defines)\n"
612 " LIST\n"
613 " LITERAL(\"WEE\")\n"
614 " +BEFORE_COMMENT(\"# Connected comment.\")\n"
615 " LITERAL(\"BLORPY\")\n";
616 DoParserPrintTest(input, expected);
619 TEST(Parser, CommentsAtEndOfBlock) {
620 const char* input =
621 "if (is_win) {\n"
622 " sources = [\"a.cc\"]\n"
623 " # Some comment at end.\n"
624 "}\n";
625 const char* expected =
626 "BLOCK\n"
627 " CONDITION\n"
628 " IDENTIFIER(is_win)\n"
629 " BLOCK\n"
630 " BINARY(=)\n"
631 " IDENTIFIER(sources)\n"
632 " LIST\n"
633 " LITERAL(\"a.cc\")\n"
634 " END(})\n"
635 " +BEFORE_COMMENT(\"# Some comment at end.\")\n";
636 DoParserPrintTest(input, expected);
639 // TODO(scottmg): I could be convinced this is incorrect. It's not clear to me
640 // which thing this comment is intended to be attached to.
641 TEST(Parser, CommentsEndOfBlockSingleLine) {
642 const char* input =
643 "defines = [ # EOL defines.\n"
644 "]\n";
645 const char* expected =
646 "BLOCK\n"
647 " BINARY(=)\n"
648 " IDENTIFIER(defines)\n"
649 " +SUFFIX_COMMENT(\"# EOL defines.\")\n"
650 " LIST\n";
651 DoParserPrintTest(input, expected);
654 TEST(Parser, HangingIf) {
655 DoParserErrorTest("if", 1, 1);
658 TEST(Parser, NegatingList) {
659 DoParserErrorTest("executable(\"wee\") { sources =- [ \"foo.cc\" ] }", 1, 30);
662 TEST(Parser, ConditionNoBracesIf) {
663 DoParserErrorTest(
664 "if (true)\n"
665 " foreach(foo, []) {}\n"
666 "else {\n"
667 " foreach(bar, []) {}\n"
668 "}\n",
669 2, 3);
672 TEST(Parser, ConditionNoBracesElse) {
673 DoParserErrorTest(
674 "if (true) {\n"
675 " foreach(foo, []) {}\n"
676 "} else\n"
677 " foreach(bar, []) {}\n",
678 4, 3);
681 TEST(Parser, ConditionNoBracesElseIf) {
682 DoParserErrorTest(
683 "if (true) {\n"
684 " foreach(foo, []) {}\n"
685 "} else if (true)\n"
686 " foreach(bar, []) {}\n",
687 4, 3);
690 // Disallow standalone {} for introducing new scopes. These are ambiguous with
691 // target declarations (e.g. is:
692 // foo("bar") {}
693 // a function with an associated block, or a standalone function with a
694 // freestanding block.
695 TEST(Parser, StandaloneBlock) {
696 DoParserErrorTest(
697 "if (true) {\n"
698 "}\n"
699 "{\n"
700 " assert(false)\n"
701 "}\n",
702 3, 1);