Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(220)

Unified Diff: tools/gn/parser_unittest.cc

Issue 21114002: Add initial prototype for the GN meta-buildsystem. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add owners and readme Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/parser.cc ('k') | tools/gn/path_output.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/parser_unittest.cc
diff --git a/tools/gn/parser_unittest.cc b/tools/gn/parser_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3fd8ebe28db8c70776fa671ae6d02749a53d6448
--- /dev/null
+++ b/tools/gn/parser_unittest.cc
@@ -0,0 +1,329 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <iostream>
+#include <sstream>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/gn/input_file.h"
+#include "tools/gn/parser.h"
+#include "tools/gn/tokenizer.h"
+
+namespace {
+
+bool GetTokens(const InputFile* input, std::vector<Token>* result) {
+ result->clear();
+ Err err;
+ *result = Tokenizer::Tokenize(input, &err);
+ return !err.has_error();
+}
+
+bool IsIdentifierEqual(const ParseNode* node, const char* val) {
+ if (!node)
+ return false;
+ const IdentifierNode* ident = node->AsIdentifier();
+ if (!ident)
+ return false;
+ return ident->value().value() == val;
+}
+
+bool IsLiteralEqual(const ParseNode* node, const char* val) {
+ if (!node)
+ return false;
+ const LiteralNode* lit = node->AsLiteral();
+ if (!lit)
+ return false;
+ return lit->value().value() == val;
+}
+
+// Returns true if the given node as a simple assignment to a given value.
+bool IsAssignment(const ParseNode* node, const char* ident, const char* value) {
+ if (!node)
+ return false;
+ const BinaryOpNode* binary = node->AsBinaryOp();
+ if (!binary)
+ return false;
+ return binary->op().IsOperatorEqualTo("=") &&
+ IsIdentifierEqual(binary->left(), ident) &&
+ IsLiteralEqual(binary->right(), value);
+}
+
+// Returns true if the given node is a block with one assignment statement.
+bool IsBlockWithAssignment(const ParseNode* node,
+ const char* ident, const char* value) {
+ if (!node)
+ return false;
+ const BlockNode* block = node->AsBlock();
+ if (!block)
+ return false;
+ if (block->statements().size() != 1)
+ return false;
+ return IsAssignment(block->statements()[0], ident, value);
+}
+
+void DoParserPrintTest(const char* input, const char* expected) {
+ std::vector<Token> tokens;
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+ ASSERT_TRUE(GetTokens(&input_file, &tokens));
+
+ Err err;
+ scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
+ ASSERT_TRUE(result);
+
+ std::ostringstream collector;
+ result->Print(collector, 0);
+
+ EXPECT_EQ(expected, collector.str());
+}
+
+// Expects the tokenizer or parser to identify an error at the given line and
+// character.
+void DoParserErrorTest(const char* input, int err_line, int err_char) {
+ InputFile input_file(SourceFile("/test"));
+ input_file.SetContents(input);
+
+ Err err;
+ std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
+ if (!err.has_error()) {
+ scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
+ ASSERT_FALSE(result);
+ ASSERT_TRUE(err.has_error());
+ }
+
+ EXPECT_EQ(err_line, err.location().line_number());
+ EXPECT_EQ(err_char, err.location().char_offset());
+}
+
+} // namespace
+
+TEST(Parser, BinaryOp) {
+ std::vector<Token> tokens;
+
+ // Simple set expression.
+ InputFile expr_input(SourceFile("/test"));
+ expr_input.SetContents("a=2");
+ ASSERT_TRUE(GetTokens(&expr_input, &tokens));
+ Err err;
+ Parser set(tokens, &err);
+ scoped_ptr<ParseNode> expr = set.ParseExpression();
+ ASSERT_TRUE(expr);
+
+ const BinaryOpNode* binary_op = expr->AsBinaryOp();
+ ASSERT_TRUE(binary_op);
+
+ EXPECT_TRUE(binary_op->left()->AsIdentifier());
+
+ EXPECT_TRUE(binary_op->op().type() == Token::OPERATOR);
+ EXPECT_TRUE(binary_op->op().value() == "=");
+
+ EXPECT_TRUE(binary_op->right()->AsLiteral());
+}
+
+TEST(Parser, Condition) {
+ std::vector<Token> tokens;
+
+ InputFile cond_input(SourceFile("/test"));
+ cond_input.SetContents("if(1) { a = 2 }");
+ ASSERT_TRUE(GetTokens(&cond_input, &tokens));
+ Err err;
+ Parser simple_if(tokens, &err);
+ scoped_ptr<ConditionNode> cond = simple_if.ParseCondition();
+ ASSERT_TRUE(cond);
+
+ EXPECT_TRUE(IsLiteralEqual(cond->condition(), "1"));
+ EXPECT_FALSE(cond->if_false()); // No else block.
+ EXPECT_TRUE(IsBlockWithAssignment(cond->if_true(), "a", "2"));
+
+ // Now try a complicated if/else if/else one.
+ InputFile complex_if_input(SourceFile("/test"));
+ complex_if_input.SetContents(
+ "if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }");
+ ASSERT_TRUE(GetTokens(&complex_if_input, &tokens));
+ Parser complex_if(tokens, &err);
+ cond = complex_if.ParseCondition();
+ ASSERT_TRUE(cond);
+
+ EXPECT_TRUE(IsLiteralEqual(cond->condition(), "1"));
+ EXPECT_TRUE(IsBlockWithAssignment(cond->if_true(), "a", "2"));
+
+ ASSERT_TRUE(cond->if_false());
+ const ConditionNode* nested_cond = cond->if_false()->AsConditionNode();
+ ASSERT_TRUE(nested_cond);
+ EXPECT_TRUE(IsLiteralEqual(nested_cond->condition(), "0"));
+ EXPECT_TRUE(IsBlockWithAssignment(nested_cond->if_true(), "a", "3"));
+ EXPECT_TRUE(IsBlockWithAssignment(nested_cond->if_false(), "a", "4"));
+}
+
+TEST(Parser, FunctionCall) {
+ const char* input = "foo(a, 1, 2,) bar()";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(foo)\n"
+ " LIST\n"
+ " IDENTIFIER(a)\n"
+ " LITERAL(1)\n"
+ " LITERAL(2)\n"
+ " FUNCTION(bar)\n"
+ " LIST\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, ParenExpression) {
+ const char* input = "(foo(1)) + (a + b)";
+ const char* expected =
+ "BLOCK\n"
+ " BINARY(+)\n"
+ " FUNCTION(foo)\n"
+ " LIST\n"
+ " LITERAL(1)\n"
+ " BINARY(+)\n"
+ " IDENTIFIER(a)\n"
+ " IDENTIFIER(b)\n";
+ DoParserPrintTest(input, expected);
+ DoParserErrorTest("(a +", 1, 4);
+}
+
+TEST(Parser, UnaryOp) {
+ std::vector<Token> tokens;
+
+ InputFile ident_input(SourceFile("/test"));
+ ident_input.SetContents("!foo");
+ ASSERT_TRUE(GetTokens(&ident_input, &tokens));
+ Err err;
+ Parser ident(tokens, &err);
+ scoped_ptr<UnaryOpNode> op = ident.ParseUnaryOp();
+
+ ASSERT_TRUE(op);
+ EXPECT_TRUE(op->op().type() == Token::OPERATOR);
+ EXPECT_TRUE(op->op().value() == "!");
+}
+
+TEST(Parser, CompleteFunction) {
+ const char* input =
+ "cc_test(\"foo\") {\n"
+ " sources = [\n"
+ " \"foo.cc\",\n"
+ " \"foo.h\"\n"
+ " ]\n"
+ " dependencies = [\n"
+ " \"base\"\n"
+ " ]\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(cc_test)\n"
+ " LIST\n"
+ " LITERAL(\"foo\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"foo.cc\")\n"
+ " LITERAL(\"foo.h\")\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(dependencies)\n"
+ " LIST\n"
+ " LITERAL(\"base\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, FunctionWithConditional) {
+ const char* input =
+ "cc_test(\"foo\") {\n"
+ " sources = [\"foo.cc\"]\n"
+ " if (OS == \"mac\") {\n"
+ " sources += \"bar.cc\"\n"
+ " } else if (OS == \"win\") {\n"
+ " sources -= [\"asd.cc\", \"foo.cc\"]\n"
+ " } else {\n"
+ " dependencies += [\"bar.cc\"]\n"
+ " }\n"
+ "}\n";
+ const char* expected =
+ "BLOCK\n"
+ " FUNCTION(cc_test)\n"
+ " LIST\n"
+ " LITERAL(\"foo\")\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"foo.cc\")\n"
+ " CONDITION\n"
+ " BINARY(==)\n"
+ " IDENTIFIER(OS)\n"
+ " LITERAL(\"mac\")\n"
+ " BLOCK\n"
+ " BINARY(+=)\n"
+ " IDENTIFIER(sources)\n"
+ " LITERAL(\"bar.cc\")\n"
+ " CONDITION\n"
+ " BINARY(==)\n"
+ " IDENTIFIER(OS)\n"
+ " LITERAL(\"win\")\n"
+ " BLOCK\n"
+ " BINARY(-=)\n"
+ " IDENTIFIER(sources)\n"
+ " LIST\n"
+ " LITERAL(\"asd.cc\")\n"
+ " LITERAL(\"foo.cc\")\n"
+ " BLOCK\n"
+ " BINARY(+=)\n"
+ " IDENTIFIER(dependencies)\n"
+ " LIST\n"
+ " LITERAL(\"bar.cc\")\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, NestedBlocks) {
+ const char* input = "{cc_test(\"foo\") {{foo=1}{}}}";
+ const char* expected =
+ "BLOCK\n"
+ " BLOCK\n"
+ " FUNCTION(cc_test)\n"
+ " LIST\n"
+ " LITERAL(\"foo\")\n"
+ " BLOCK\n"
+ " BLOCK\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(foo)\n"
+ " LITERAL(1)\n"
+ " BLOCK\n";
+ DoParserPrintTest(input, expected);
+}
+
+TEST(Parser, List) {
+ const char* input = "[] a = [1,asd,] b = [1, 2+3 - foo]";
+ const char* expected =
+ "BLOCK\n"
+ " LIST\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(a)\n"
+ " LIST\n"
+ " LITERAL(1)\n"
+ " IDENTIFIER(asd)\n"
+ " BINARY(=)\n"
+ " IDENTIFIER(b)\n"
+ " LIST\n"
+ " LITERAL(1)\n"
+ " BINARY(+)\n"
+ " LITERAL(2)\n"
+ " BINARY(-)\n"
+ " LITERAL(3)\n"
+ " IDENTIFIER(foo)\n";
+ DoParserPrintTest(input, expected);
+
+ DoParserErrorTest("[a, 2+,]", 1, 7);
+ DoParserErrorTest("[,]", 1, 2);
+ DoParserErrorTest("[a,,]", 1, 4);
+}
+
+TEST(Parser, UnterminatedBlock) {
+ DoParserErrorTest("hello {", 1, 7);
+}
+
+TEST(Parser, BadlyTerminatedNumber) {
+ DoParserErrorTest("1234z", 1, 5);
+}
« no previous file with comments | « tools/gn/parser.cc ('k') | tools/gn/path_output.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698