[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / tools / gn / parser_unittest.cc
blob39b69b7845c619f1d2709b2ae3330957d7944d69
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");
185 // No contents for binary operator.
186 DoExpressionErrorTest("a = !", 1, 5);
189 TEST(Parser, List) {
190 DoExpressionPrintTest("[]", "LIST\n");
191 DoExpressionPrintTest("[1,asd,]",
192 "LIST\n"
193 " LITERAL(1)\n"
194 " IDENTIFIER(asd)\n");
195 DoExpressionPrintTest("[1, 2+3 - foo]",
196 "LIST\n"
197 " LITERAL(1)\n"
198 " BINARY(-)\n"
199 " BINARY(+)\n"
200 " LITERAL(2)\n"
201 " LITERAL(3)\n"
202 " IDENTIFIER(foo)\n");
203 DoExpressionPrintTest("[1,\n2,\n 3,\n 4]",
204 "LIST\n"
205 " LITERAL(1)\n"
206 " LITERAL(2)\n"
207 " LITERAL(3)\n"
208 " LITERAL(4)\n");
210 DoExpressionErrorTest("[a, 2+,]", 1, 7);
211 DoExpressionErrorTest("[,]", 1, 2);
212 DoExpressionErrorTest("[a,,]", 1, 4);
215 TEST(Parser, Assignment) {
216 DoParserPrintTest("a=2",
217 "BLOCK\n"
218 " BINARY(=)\n"
219 " IDENTIFIER(a)\n"
220 " LITERAL(2)\n");
222 DoExpressionErrorTest("a = ", 1, 3);
225 TEST(Parser, Accessor) {
226 // Accessor indexing.
227 DoParserPrintTest("a=b[c+2]",
228 "BLOCK\n"
229 " BINARY(=)\n"
230 " IDENTIFIER(a)\n"
231 " ACCESSOR\n"
232 " b\n" // AccessorNode is a bit weird in that it holds
233 // a Token, not a ParseNode for the base.
234 " BINARY(+)\n"
235 " IDENTIFIER(c)\n"
236 " LITERAL(2)\n");
237 DoParserErrorTest("a = b[1][0]", 1, 5);
239 // Member accessors.
240 DoParserPrintTest("a=b.c+2",
241 "BLOCK\n"
242 " BINARY(=)\n"
243 " IDENTIFIER(a)\n"
244 " BINARY(+)\n"
245 " ACCESSOR\n"
246 " b\n"
247 " IDENTIFIER(c)\n"
248 " LITERAL(2)\n");
249 DoParserErrorTest("a = b.c.d", 1, 6); // Can't nest accessors (currently).
250 DoParserErrorTest("a.b = 5", 1, 1); // Can't assign to accessors (currently).
252 // Error at the bad dot in the RHS, not the + operator (crbug.com/472038).
253 DoParserErrorTest("foo(a + b.c.d)", 1, 10);
256 TEST(Parser, Condition) {
257 DoParserPrintTest("if(1) { a = 2 }",
258 "BLOCK\n"
259 " CONDITION\n"
260 " LITERAL(1)\n"
261 " BLOCK\n"
262 " BINARY(=)\n"
263 " IDENTIFIER(a)\n"
264 " LITERAL(2)\n");
266 DoParserPrintTest("if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }",
267 "BLOCK\n"
268 " CONDITION\n"
269 " LITERAL(1)\n"
270 " BLOCK\n"
271 " BINARY(=)\n"
272 " IDENTIFIER(a)\n"
273 " LITERAL(2)\n"
274 " CONDITION\n"
275 " LITERAL(0)\n"
276 " BLOCK\n"
277 " BINARY(=)\n"
278 " IDENTIFIER(a)\n"
279 " LITERAL(3)\n"
280 " BLOCK\n"
281 " BINARY(=)\n"
282 " IDENTIFIER(a)\n"
283 " LITERAL(4)\n");
286 TEST(Parser, OnlyCallAndAssignInBody) {
287 DoParserErrorTest("[]", 1, 2);
288 DoParserErrorTest("3 + 4", 1, 5);
289 DoParserErrorTest("6 - 7", 1, 5);
290 DoParserErrorTest("if (1) { 5 } else { print(4) }", 1, 12);
293 TEST(Parser, NoAssignmentInCondition) {
294 DoParserErrorTest("if (a=2) {}", 1, 5);
297 TEST(Parser, CompleteFunction) {
298 const char* input =
299 "cc_test(\"foo\") {\n"
300 " sources = [\n"
301 " \"foo.cc\",\n"
302 " \"foo.h\"\n"
303 " ]\n"
304 " dependencies = [\n"
305 " \"base\"\n"
306 " ]\n"
307 "}\n";
308 const char* expected =
309 "BLOCK\n"
310 " FUNCTION(cc_test)\n"
311 " LIST\n"
312 " LITERAL(\"foo\")\n"
313 " BLOCK\n"
314 " BINARY(=)\n"
315 " IDENTIFIER(sources)\n"
316 " LIST\n"
317 " LITERAL(\"foo.cc\")\n"
318 " LITERAL(\"foo.h\")\n"
319 " BINARY(=)\n"
320 " IDENTIFIER(dependencies)\n"
321 " LIST\n"
322 " LITERAL(\"base\")\n";
323 DoParserPrintTest(input, expected);
326 TEST(Parser, FunctionWithConditional) {
327 const char* input =
328 "cc_test(\"foo\") {\n"
329 " sources = [\"foo.cc\"]\n"
330 " if (OS == \"mac\") {\n"
331 " sources += \"bar.cc\"\n"
332 " } else if (OS == \"win\") {\n"
333 " sources -= [\"asd.cc\", \"foo.cc\"]\n"
334 " } else {\n"
335 " dependencies += [\"bar.cc\"]\n"
336 " }\n"
337 "}\n";
338 const char* expected =
339 "BLOCK\n"
340 " FUNCTION(cc_test)\n"
341 " LIST\n"
342 " LITERAL(\"foo\")\n"
343 " BLOCK\n"
344 " BINARY(=)\n"
345 " IDENTIFIER(sources)\n"
346 " LIST\n"
347 " LITERAL(\"foo.cc\")\n"
348 " CONDITION\n"
349 " BINARY(==)\n"
350 " IDENTIFIER(OS)\n"
351 " LITERAL(\"mac\")\n"
352 " BLOCK\n"
353 " BINARY(+=)\n"
354 " IDENTIFIER(sources)\n"
355 " LITERAL(\"bar.cc\")\n"
356 " CONDITION\n"
357 " BINARY(==)\n"
358 " IDENTIFIER(OS)\n"
359 " LITERAL(\"win\")\n"
360 " BLOCK\n"
361 " BINARY(-=)\n"
362 " IDENTIFIER(sources)\n"
363 " LIST\n"
364 " LITERAL(\"asd.cc\")\n"
365 " LITERAL(\"foo.cc\")\n"
366 " BLOCK\n"
367 " BINARY(+=)\n"
368 " IDENTIFIER(dependencies)\n"
369 " LIST\n"
370 " LITERAL(\"bar.cc\")\n";
371 DoParserPrintTest(input, expected);
374 TEST(Parser, UnterminatedBlock) {
375 DoParserErrorTest("stuff() {", 1, 9);
378 TEST(Parser, BadlyTerminatedNumber) {
379 DoParserErrorTest("1234z", 1, 5);
382 TEST(Parser, NewlinesInUnusualPlaces) {
383 DoParserPrintTest(
384 "if\n"
385 "(\n"
386 "a\n"
387 ")\n"
388 "{\n"
389 "}\n",
390 "BLOCK\n"
391 " CONDITION\n"
392 " IDENTIFIER(a)\n"
393 " BLOCK\n");
396 TEST(Parser, NewlinesInUnusualPlaces2) {
397 DoParserPrintTest(
398 "a\n=\n2\n",
399 "BLOCK\n"
400 " BINARY(=)\n"
401 " IDENTIFIER(a)\n"
402 " LITERAL(2)\n");
403 DoParserPrintTest(
404 "x =\ny if\n(1\n) {}",
405 "BLOCK\n"
406 " BINARY(=)\n"
407 " IDENTIFIER(x)\n"
408 " IDENTIFIER(y)\n"
409 " CONDITION\n"
410 " LITERAL(1)\n"
411 " BLOCK\n");
412 DoParserPrintTest(
413 "x = 3\n+2",
414 "BLOCK\n"
415 " BINARY(=)\n"
416 " IDENTIFIER(x)\n"
417 " BINARY(+)\n"
418 " LITERAL(3)\n"
419 " LITERAL(2)\n"
423 TEST(Parser, NewlineBeforeSubscript) {
424 const char* input = "a = b[1]";
425 const char* input_with_newline = "a = b\n[1]";
426 const char* expected =
427 "BLOCK\n"
428 " BINARY(=)\n"
429 " IDENTIFIER(a)\n"
430 " ACCESSOR\n"
431 " b\n"
432 " LITERAL(1)\n";
433 DoParserPrintTest(
434 input,
435 expected);
436 DoParserPrintTest(
437 input_with_newline,
438 expected);
441 TEST(Parser, SequenceOfExpressions) {
442 DoParserPrintTest(
443 "a = 1 b = 2",
444 "BLOCK\n"
445 " BINARY(=)\n"
446 " IDENTIFIER(a)\n"
447 " LITERAL(1)\n"
448 " BINARY(=)\n"
449 " IDENTIFIER(b)\n"
450 " LITERAL(2)\n");
453 TEST(Parser, BlockAfterFunction) {
454 const char* input = "func(\"stuff\") {\n}";
455 // TODO(scottmg): Do we really want these to mean different things?
456 const char* input_with_newline = "func(\"stuff\")\n{\n}";
457 const char* expected =
458 "BLOCK\n"
459 " FUNCTION(func)\n"
460 " LIST\n"
461 " LITERAL(\"stuff\")\n"
462 " BLOCK\n";
463 DoParserPrintTest(input, expected);
464 DoParserPrintTest(input_with_newline, expected);
467 TEST(Parser, LongExpression) {
468 const char* input = "a = b + c && d || e";
469 const char* expected =
470 "BLOCK\n"
471 " BINARY(=)\n"
472 " IDENTIFIER(a)\n"
473 " BINARY(||)\n"
474 " BINARY(&&)\n"
475 " BINARY(+)\n"
476 " IDENTIFIER(b)\n"
477 " IDENTIFIER(c)\n"
478 " IDENTIFIER(d)\n"
479 " IDENTIFIER(e)\n";
480 DoParserPrintTest(input, expected);
483 TEST(Parser, CommentsStandalone) {
484 const char* input =
485 "# Toplevel comment.\n"
486 "\n"
487 "executable(\"wee\") {}\n";
488 const char* expected =
489 "BLOCK\n"
490 " BLOCK_COMMENT(# Toplevel comment.)\n"
491 " FUNCTION(executable)\n"
492 " LIST\n"
493 " LITERAL(\"wee\")\n"
494 " BLOCK\n";
495 DoParserPrintTest(input, expected);
498 TEST(Parser, CommentsStandaloneEof) {
499 const char* input =
500 "executable(\"wee\") {}\n"
501 "# EOF comment.\n";
502 const char* expected =
503 "BLOCK\n"
504 " +AFTER_COMMENT(\"# EOF comment.\")\n"
505 " FUNCTION(executable)\n"
506 " LIST\n"
507 " LITERAL(\"wee\")\n"
508 " BLOCK\n";
509 DoParserPrintTest(input, expected);
512 TEST(Parser, CommentsLineAttached) {
513 const char* input =
514 "executable(\"wee\") {\n"
515 " # Some sources.\n"
516 " sources = [\n"
517 " \"stuff.cc\",\n"
518 " \"things.cc\",\n"
519 " # This file is special or something.\n"
520 " \"another.cc\",\n"
521 " ]\n"
522 "}\n";
523 const char* expected =
524 "BLOCK\n"
525 " FUNCTION(executable)\n"
526 " LIST\n"
527 " LITERAL(\"wee\")\n"
528 " BLOCK\n"
529 " BINARY(=)\n"
530 " +BEFORE_COMMENT(\"# Some sources.\")\n"
531 " IDENTIFIER(sources)\n"
532 " LIST\n"
533 " LITERAL(\"stuff.cc\")\n"
534 " LITERAL(\"things.cc\")\n"
535 " LITERAL(\"another.cc\")\n"
536 " +BEFORE_COMMENT(\"# This file is special or something.\")\n";
537 DoParserPrintTest(input, expected);
540 TEST(Parser, CommentsSuffix) {
541 const char* input =
542 "executable(\"wee\") { # This is some stuff.\n"
543 "sources = [ \"a.cc\" # And another comment here.\n"
544 "] }";
545 const char* expected =
546 "BLOCK\n"
547 " FUNCTION(executable)\n"
548 " LIST\n"
549 " LITERAL(\"wee\")\n"
550 " END())\n"
551 " +SUFFIX_COMMENT(\"# This is some stuff.\")\n"
552 " BLOCK\n"
553 " BINARY(=)\n"
554 " IDENTIFIER(sources)\n"
555 " LIST\n"
556 " LITERAL(\"a.cc\")\n"
557 " +SUFFIX_COMMENT(\"# And another comment here.\")\n";
558 DoParserPrintTest(input, expected);
561 TEST(Parser, CommentsSuffixDifferentLine) {
562 const char* input =
563 "executable(\"wee\") {\n"
564 " sources = [ \"a\",\n"
565 " \"b\" ] # Comment\n"
566 "}\n";
567 const char* expected =
568 "BLOCK\n"
569 " FUNCTION(executable)\n"
570 " LIST\n"
571 " LITERAL(\"wee\")\n"
572 " BLOCK\n"
573 " BINARY(=)\n"
574 " IDENTIFIER(sources)\n"
575 " LIST\n"
576 " LITERAL(\"a\")\n"
577 " LITERAL(\"b\")\n"
578 " END(])\n"
579 " +SUFFIX_COMMENT(\"# Comment\")\n";
580 DoParserPrintTest(input, expected);
583 TEST(Parser, CommentsSuffixMultiple) {
584 const char* input =
585 "executable(\"wee\") {\n"
586 " sources = [\n"
587 " \"a\", # This is a comment,\n"
588 " # and some more,\n" // Note that this is aligned with above.
589 " # then the end.\n"
590 " ]\n"
591 "}\n";
592 const char* expected =
593 "BLOCK\n"
594 " FUNCTION(executable)\n"
595 " LIST\n"
596 " LITERAL(\"wee\")\n"
597 " BLOCK\n"
598 " BINARY(=)\n"
599 " IDENTIFIER(sources)\n"
600 " LIST\n"
601 " LITERAL(\"a\")\n"
602 " +SUFFIX_COMMENT(\"# This is a comment,\")\n"
603 " +SUFFIX_COMMENT(\"# and some more,\")\n"
604 " +SUFFIX_COMMENT(\"# then the end.\")\n";
605 DoParserPrintTest(input, expected);
608 TEST(Parser, CommentsConnectedInList) {
609 const char* input =
610 "defines = [\n"
611 "\n"
612 " # Connected comment.\n"
613 " \"WEE\",\n"
614 " \"BLORPY\",\n"
615 "]\n";
616 const char* expected =
617 "BLOCK\n"
618 " BINARY(=)\n"
619 " IDENTIFIER(defines)\n"
620 " LIST\n"
621 " LITERAL(\"WEE\")\n"
622 " +BEFORE_COMMENT(\"# Connected comment.\")\n"
623 " LITERAL(\"BLORPY\")\n";
624 DoParserPrintTest(input, expected);
627 TEST(Parser, CommentsAtEndOfBlock) {
628 const char* input =
629 "if (is_win) {\n"
630 " sources = [\"a.cc\"]\n"
631 " # Some comment at end.\n"
632 "}\n";
633 const char* expected =
634 "BLOCK\n"
635 " CONDITION\n"
636 " IDENTIFIER(is_win)\n"
637 " BLOCK\n"
638 " BINARY(=)\n"
639 " IDENTIFIER(sources)\n"
640 " LIST\n"
641 " LITERAL(\"a.cc\")\n"
642 " END(})\n"
643 " +BEFORE_COMMENT(\"# Some comment at end.\")\n";
644 DoParserPrintTest(input, expected);
647 // TODO(scottmg): I could be convinced this is incorrect. It's not clear to me
648 // which thing this comment is intended to be attached to.
649 TEST(Parser, CommentsEndOfBlockSingleLine) {
650 const char* input =
651 "defines = [ # EOL defines.\n"
652 "]\n";
653 const char* expected =
654 "BLOCK\n"
655 " BINARY(=)\n"
656 " IDENTIFIER(defines)\n"
657 " +SUFFIX_COMMENT(\"# EOL defines.\")\n"
658 " LIST\n";
659 DoParserPrintTest(input, expected);
662 TEST(Parser, HangingIf) {
663 DoParserErrorTest("if", 1, 1);
666 TEST(Parser, NegatingList) {
667 DoParserErrorTest("executable(\"wee\") { sources =- [ \"foo.cc\" ] }", 1, 30);
670 TEST(Parser, ConditionNoBracesIf) {
671 DoParserErrorTest(
672 "if (true)\n"
673 " foreach(foo, []) {}\n"
674 "else {\n"
675 " foreach(bar, []) {}\n"
676 "}\n",
677 2, 3);
680 TEST(Parser, ConditionNoBracesElse) {
681 DoParserErrorTest(
682 "if (true) {\n"
683 " foreach(foo, []) {}\n"
684 "} else\n"
685 " foreach(bar, []) {}\n",
686 4, 3);
689 TEST(Parser, ConditionNoBracesElseIf) {
690 DoParserErrorTest(
691 "if (true) {\n"
692 " foreach(foo, []) {}\n"
693 "} else if (true)\n"
694 " foreach(bar, []) {}\n",
695 4, 3);
698 // Disallow standalone {} for introducing new scopes. These are ambiguous with
699 // target declarations (e.g. is:
700 // foo("bar") {}
701 // a function with an associated block, or a standalone function with a
702 // freestanding block.
703 TEST(Parser, StandaloneBlock) {
704 DoParserErrorTest(
705 "if (true) {\n"
706 "}\n"
707 "{\n"
708 " assert(false)\n"
709 "}\n",
710 3, 1);