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.
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"
15 bool GetTokens(const InputFile* input, std::vector<Token>* result) {
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));
29 scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
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));
47 scoped_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
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
58 void DoParserErrorTest(const char* input, int err_line, int err_char) {
59 InputFile input_file(SourceFile("/test"));
60 input_file.SetContents(input);
63 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
64 if (!err.has_error()) {
65 scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
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
76 void DoExpressionErrorTest(const char* input, int err_line, int err_char) {
77 InputFile input_file(SourceFile("/test"));
78 input_file.SetContents(input);
81 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
82 if (!err.has_error()) {
83 scoped_ptr<ParseNode> result = Parser::ParseExpression(tokens, &err);
85 ASSERT_TRUE(err.has_error());
88 EXPECT_EQ(err_line, err.location().line_number());
89 EXPECT_EQ(err_char, err.location().char_offset());
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",
106 DoExpressionPrintTest("5+1",
110 DoExpressionPrintTest("5 - 1 - 2",
118 TEST(Parser, FunctionCall) {
119 DoExpressionPrintTest("foo()",
122 DoExpressionPrintTest("blah(1, 2)",
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 =
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 =
157 DoExpressionPrintTest(input, expected);
160 TEST(Parser, OrderOfOperationsEqualityBoolean) {
162 "if (a == \"b\" && is_stuff) {\n"
165 const char* expected =
172 " IDENTIFIER(is_stuff)\n"
176 " LITERAL(\"hai\")\n";
177 DoParserPrintTest(input, expected);
180 TEST(Parser, UnaryOp) {
181 DoExpressionPrintTest("!foo",
183 " IDENTIFIER(foo)\n");
187 DoExpressionPrintTest("[]", "LIST\n");
188 DoExpressionPrintTest("[1,asd,]",
191 " IDENTIFIER(asd)\n");
192 DoExpressionPrintTest("[1, 2+3 - foo]",
199 " IDENTIFIER(foo)\n");
200 DoExpressionPrintTest("[1,\n2,\n 3,\n 4]",
207 DoExpressionErrorTest("[a, 2+,]", 1, 6);
208 DoExpressionErrorTest("[,]", 1, 2);
209 DoExpressionErrorTest("[a,,]", 1, 4);
212 TEST(Parser, Assignment) {
213 DoParserPrintTest("a=2",
220 TEST(Parser, Accessor) {
221 // Accessor indexing.
222 DoParserPrintTest("a=b[c+2]",
227 " b\n" // AccessorNode is a bit weird in that it holds
228 // a Token, not a ParseNode for the base.
232 DoParserErrorTest("a = b[1][0]", 1, 5);
235 DoParserPrintTest("a=b.c+2",
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 }",
258 DoParserPrintTest("if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }",
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) {
291 "cc_test(\"foo\") {\n"
296 " dependencies = [\n"
300 const char* expected =
302 " FUNCTION(cc_test)\n"
304 " LITERAL(\"foo\")\n"
307 " IDENTIFIER(sources)\n"
309 " LITERAL(\"foo.cc\")\n"
310 " LITERAL(\"foo.h\")\n"
312 " IDENTIFIER(dependencies)\n"
314 " LITERAL(\"base\")\n";
315 DoParserPrintTest(input, expected);
318 TEST(Parser, FunctionWithConditional) {
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"
327 " dependencies += [\"bar.cc\"]\n"
330 const char* expected =
332 " FUNCTION(cc_test)\n"
334 " LITERAL(\"foo\")\n"
337 " IDENTIFIER(sources)\n"
339 " LITERAL(\"foo.cc\")\n"
343 " LITERAL(\"mac\")\n"
346 " IDENTIFIER(sources)\n"
347 " LITERAL(\"bar.cc\")\n"
351 " LITERAL(\"win\")\n"
354 " IDENTIFIER(sources)\n"
356 " LITERAL(\"asd.cc\")\n"
357 " LITERAL(\"foo.cc\")\n"
360 " IDENTIFIER(dependencies)\n"
362 " LITERAL(\"bar.cc\")\n";
363 DoParserPrintTest(input, expected);
366 TEST(Parser, NestedBlocks) {
367 const char* input = "{cc_test(\"foo\") {{foo=1}\n{}}}";
368 const char* expected =
371 " FUNCTION(cc_test)\n"
373 " LITERAL(\"foo\")\n"
380 DoParserPrintTest(input, expected);
381 const char* input_with_newline = "{cc_test(\"foo\") {{foo=1}\n{}}}";
382 DoParserPrintTest(input_with_newline, expected);
385 TEST(Parser, UnterminatedBlock) {
386 DoParserErrorTest("stuff() {", 1, 9);
389 TEST(Parser, BadlyTerminatedNumber) {
390 DoParserErrorTest("1234z", 1, 5);
393 TEST(Parser, NewlinesInUnusualPlaces) {
407 TEST(Parser, NewlinesInUnusualPlaces2) {
415 "x =\ny if\n(1\n) {}",
434 TEST(Parser, NewlineBeforeSubscript) {
435 const char* input = "a = b[1]";
436 const char* input_with_newline = "a = b\n[1]";
437 const char* expected =
452 TEST(Parser, SequenceOfExpressions) {
464 TEST(Parser, BlockAfterFunction) {
465 const char* input = "func(\"stuff\") {\n}";
466 // TODO(scottmg): Do we really want these to mean different things?
467 const char* input_with_newline = "func(\"stuff\")\n{\n}";
468 const char* expected =
472 " LITERAL(\"stuff\")\n"
474 DoParserPrintTest(input, expected);
475 DoParserPrintTest(input_with_newline, expected);
478 TEST(Parser, LongExpression) {
479 const char* input = "a = b + c && d || e";
480 const char* expected =
491 DoParserPrintTest(input, expected);
494 TEST(Parser, CommentsStandalone) {
496 "# Toplevel comment.\n"
498 "executable(\"wee\") {}\n";
499 const char* expected =
501 " BLOCK_COMMENT(# Toplevel comment.)\n"
502 " FUNCTION(executable)\n"
504 " LITERAL(\"wee\")\n"
506 DoParserPrintTest(input, expected);
509 TEST(Parser, CommentsStandaloneEof) {
511 "executable(\"wee\") {}\n"
513 const char* expected =
515 " +AFTER_COMMENT(\"# EOF comment.\")\n"
516 " FUNCTION(executable)\n"
518 " LITERAL(\"wee\")\n"
520 DoParserPrintTest(input, expected);
523 TEST(Parser, CommentsLineAttached) {
525 "executable(\"wee\") {\n"
530 " # This file is special or something.\n"
534 const char* expected =
536 " FUNCTION(executable)\n"
538 " LITERAL(\"wee\")\n"
541 " +BEFORE_COMMENT(\"# Some sources.\")\n"
542 " IDENTIFIER(sources)\n"
544 " LITERAL(\"stuff.cc\")\n"
545 " LITERAL(\"things.cc\")\n"
546 " LITERAL(\"another.cc\")\n"
547 " +BEFORE_COMMENT(\"# This file is special or something.\")\n";
548 DoParserPrintTest(input, expected);
551 TEST(Parser, CommentsSuffix) {
553 "executable(\"wee\") { # This is some stuff.\n"
554 "sources = [ \"a.cc\" # And another comment here.\n"
556 const char* expected =
558 " FUNCTION(executable)\n"
560 " LITERAL(\"wee\")\n"
561 " +SUFFIX_COMMENT(\"# This is some stuff.\")\n"
564 " IDENTIFIER(sources)\n"
566 " LITERAL(\"a.cc\")\n"
567 " +SUFFIX_COMMENT(\"# And another comment here.\")\n";
568 DoParserPrintTest(input, expected);
571 TEST(Parser, CommentsSuffixDifferentLine) {
573 "executable(\"wee\") {\n"
574 " sources = [ \"a\",\n"
575 " \"b\" ] # Comment\n"
577 const char* expected =
579 " FUNCTION(executable)\n"
581 " LITERAL(\"wee\")\n"
584 " IDENTIFIER(sources)\n"
588 " +SUFFIX_COMMENT(\"# Comment\")\n";
589 DoParserPrintTest(input, expected);
592 TEST(Parser, CommentsSuffixMultiple) {
594 "executable(\"wee\") {\n"
596 " \"a\", # This is a comment,\n"
597 " # and some more,\n" // Note that this is aligned with above.
601 const char* expected =
603 " FUNCTION(executable)\n"
605 " LITERAL(\"wee\")\n"
608 " IDENTIFIER(sources)\n"
611 " +SUFFIX_COMMENT(\"# This is a comment,\")\n"
612 " +SUFFIX_COMMENT(\"# and some more,\")\n"
613 " +SUFFIX_COMMENT(\"# then the end.\")\n";
614 DoParserPrintTest(input, expected);
617 TEST(Parser, CommentsConnectedInList) {
621 " # Connected comment.\n"
625 const char* expected =
628 " IDENTIFIER(defines)\n"
630 " LITERAL(\"WEE\")\n"
631 " +BEFORE_COMMENT(\"# Connected comment.\")\n"
632 " LITERAL(\"BLORPY\")\n";
633 DoParserPrintTest(input, expected);
636 TEST(Parser, HangingIf) {
637 DoParserErrorTest("if", 1, 1);
640 TEST(Parser, NegatingList) {
641 DoParserErrorTest("executable(\"wee\") { sources =- [ \"foo.cc\" ] }", 1, 30);