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

Side by Side 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, 4 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/gn/parser.cc ('k') | tools/gn/path_output.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include <iostream>
6 #include <sstream>
7
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"
12
13 namespace {
14
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();
20 }
21
22 bool IsIdentifierEqual(const ParseNode* node, const char* val) {
23 if (!node)
24 return false;
25 const IdentifierNode* ident = node->AsIdentifier();
26 if (!ident)
27 return false;
28 return ident->value().value() == val;
29 }
30
31 bool IsLiteralEqual(const ParseNode* node, const char* val) {
32 if (!node)
33 return false;
34 const LiteralNode* lit = node->AsLiteral();
35 if (!lit)
36 return false;
37 return lit->value().value() == val;
38 }
39
40 // Returns true if the given node as a simple assignment to a given value.
41 bool IsAssignment(const ParseNode* node, const char* ident, const char* value) {
42 if (!node)
43 return false;
44 const BinaryOpNode* binary = node->AsBinaryOp();
45 if (!binary)
46 return false;
47 return binary->op().IsOperatorEqualTo("=") &&
48 IsIdentifierEqual(binary->left(), ident) &&
49 IsLiteralEqual(binary->right(), value);
50 }
51
52 // Returns true if the given node is a block with one assignment statement.
53 bool IsBlockWithAssignment(const ParseNode* node,
54 const char* ident, const char* value) {
55 if (!node)
56 return false;
57 const BlockNode* block = node->AsBlock();
58 if (!block)
59 return false;
60 if (block->statements().size() != 1)
61 return false;
62 return IsAssignment(block->statements()[0], ident, value);
63 }
64
65 void DoParserPrintTest(const char* input, const char* expected) {
66 std::vector<Token> tokens;
67 InputFile input_file(SourceFile("/test"));
68 input_file.SetContents(input);
69 ASSERT_TRUE(GetTokens(&input_file, &tokens));
70
71 Err err;
72 scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
73 ASSERT_TRUE(result);
74
75 std::ostringstream collector;
76 result->Print(collector, 0);
77
78 EXPECT_EQ(expected, collector.str());
79 }
80
81 // Expects the tokenizer or parser to identify an error at the given line and
82 // character.
83 void DoParserErrorTest(const char* input, int err_line, int err_char) {
84 InputFile input_file(SourceFile("/test"));
85 input_file.SetContents(input);
86
87 Err err;
88 std::vector<Token> tokens = Tokenizer::Tokenize(&input_file, &err);
89 if (!err.has_error()) {
90 scoped_ptr<ParseNode> result = Parser::Parse(tokens, &err);
91 ASSERT_FALSE(result);
92 ASSERT_TRUE(err.has_error());
93 }
94
95 EXPECT_EQ(err_line, err.location().line_number());
96 EXPECT_EQ(err_char, err.location().char_offset());
97 }
98
99 } // namespace
100
101 TEST(Parser, BinaryOp) {
102 std::vector<Token> tokens;
103
104 // Simple set expression.
105 InputFile expr_input(SourceFile("/test"));
106 expr_input.SetContents("a=2");
107 ASSERT_TRUE(GetTokens(&expr_input, &tokens));
108 Err err;
109 Parser set(tokens, &err);
110 scoped_ptr<ParseNode> expr = set.ParseExpression();
111 ASSERT_TRUE(expr);
112
113 const BinaryOpNode* binary_op = expr->AsBinaryOp();
114 ASSERT_TRUE(binary_op);
115
116 EXPECT_TRUE(binary_op->left()->AsIdentifier());
117
118 EXPECT_TRUE(binary_op->op().type() == Token::OPERATOR);
119 EXPECT_TRUE(binary_op->op().value() == "=");
120
121 EXPECT_TRUE(binary_op->right()->AsLiteral());
122 }
123
124 TEST(Parser, Condition) {
125 std::vector<Token> tokens;
126
127 InputFile cond_input(SourceFile("/test"));
128 cond_input.SetContents("if(1) { a = 2 }");
129 ASSERT_TRUE(GetTokens(&cond_input, &tokens));
130 Err err;
131 Parser simple_if(tokens, &err);
132 scoped_ptr<ConditionNode> cond = simple_if.ParseCondition();
133 ASSERT_TRUE(cond);
134
135 EXPECT_TRUE(IsLiteralEqual(cond->condition(), "1"));
136 EXPECT_FALSE(cond->if_false()); // No else block.
137 EXPECT_TRUE(IsBlockWithAssignment(cond->if_true(), "a", "2"));
138
139 // Now try a complicated if/else if/else one.
140 InputFile complex_if_input(SourceFile("/test"));
141 complex_if_input.SetContents(
142 "if(1) { a = 2 } else if (0) { a = 3 } else { a = 4 }");
143 ASSERT_TRUE(GetTokens(&complex_if_input, &tokens));
144 Parser complex_if(tokens, &err);
145 cond = complex_if.ParseCondition();
146 ASSERT_TRUE(cond);
147
148 EXPECT_TRUE(IsLiteralEqual(cond->condition(), "1"));
149 EXPECT_TRUE(IsBlockWithAssignment(cond->if_true(), "a", "2"));
150
151 ASSERT_TRUE(cond->if_false());
152 const ConditionNode* nested_cond = cond->if_false()->AsConditionNode();
153 ASSERT_TRUE(nested_cond);
154 EXPECT_TRUE(IsLiteralEqual(nested_cond->condition(), "0"));
155 EXPECT_TRUE(IsBlockWithAssignment(nested_cond->if_true(), "a", "3"));
156 EXPECT_TRUE(IsBlockWithAssignment(nested_cond->if_false(), "a", "4"));
157 }
158
159 TEST(Parser, FunctionCall) {
160 const char* input = "foo(a, 1, 2,) bar()";
161 const char* expected =
162 "BLOCK\n"
163 " FUNCTION(foo)\n"
164 " LIST\n"
165 " IDENTIFIER(a)\n"
166 " LITERAL(1)\n"
167 " LITERAL(2)\n"
168 " FUNCTION(bar)\n"
169 " LIST\n";
170 DoParserPrintTest(input, expected);
171 }
172
173 TEST(Parser, ParenExpression) {
174 const char* input = "(foo(1)) + (a + b)";
175 const char* expected =
176 "BLOCK\n"
177 " BINARY(+)\n"
178 " FUNCTION(foo)\n"
179 " LIST\n"
180 " LITERAL(1)\n"
181 " BINARY(+)\n"
182 " IDENTIFIER(a)\n"
183 " IDENTIFIER(b)\n";
184 DoParserPrintTest(input, expected);
185 DoParserErrorTest("(a +", 1, 4);
186 }
187
188 TEST(Parser, UnaryOp) {
189 std::vector<Token> tokens;
190
191 InputFile ident_input(SourceFile("/test"));
192 ident_input.SetContents("!foo");
193 ASSERT_TRUE(GetTokens(&ident_input, &tokens));
194 Err err;
195 Parser ident(tokens, &err);
196 scoped_ptr<UnaryOpNode> op = ident.ParseUnaryOp();
197
198 ASSERT_TRUE(op);
199 EXPECT_TRUE(op->op().type() == Token::OPERATOR);
200 EXPECT_TRUE(op->op().value() == "!");
201 }
202
203 TEST(Parser, CompleteFunction) {
204 const char* input =
205 "cc_test(\"foo\") {\n"
206 " sources = [\n"
207 " \"foo.cc\",\n"
208 " \"foo.h\"\n"
209 " ]\n"
210 " dependencies = [\n"
211 " \"base\"\n"
212 " ]\n"
213 "}\n";
214 const char* expected =
215 "BLOCK\n"
216 " FUNCTION(cc_test)\n"
217 " LIST\n"
218 " LITERAL(\"foo\")\n"
219 " BLOCK\n"
220 " BINARY(=)\n"
221 " IDENTIFIER(sources)\n"
222 " LIST\n"
223 " LITERAL(\"foo.cc\")\n"
224 " LITERAL(\"foo.h\")\n"
225 " BINARY(=)\n"
226 " IDENTIFIER(dependencies)\n"
227 " LIST\n"
228 " LITERAL(\"base\")\n";
229 DoParserPrintTest(input, expected);
230 }
231
232 TEST(Parser, FunctionWithConditional) {
233 const char* input =
234 "cc_test(\"foo\") {\n"
235 " sources = [\"foo.cc\"]\n"
236 " if (OS == \"mac\") {\n"
237 " sources += \"bar.cc\"\n"
238 " } else if (OS == \"win\") {\n"
239 " sources -= [\"asd.cc\", \"foo.cc\"]\n"
240 " } else {\n"
241 " dependencies += [\"bar.cc\"]\n"
242 " }\n"
243 "}\n";
244 const char* expected =
245 "BLOCK\n"
246 " FUNCTION(cc_test)\n"
247 " LIST\n"
248 " LITERAL(\"foo\")\n"
249 " BLOCK\n"
250 " BINARY(=)\n"
251 " IDENTIFIER(sources)\n"
252 " LIST\n"
253 " LITERAL(\"foo.cc\")\n"
254 " CONDITION\n"
255 " BINARY(==)\n"
256 " IDENTIFIER(OS)\n"
257 " LITERAL(\"mac\")\n"
258 " BLOCK\n"
259 " BINARY(+=)\n"
260 " IDENTIFIER(sources)\n"
261 " LITERAL(\"bar.cc\")\n"
262 " CONDITION\n"
263 " BINARY(==)\n"
264 " IDENTIFIER(OS)\n"
265 " LITERAL(\"win\")\n"
266 " BLOCK\n"
267 " BINARY(-=)\n"
268 " IDENTIFIER(sources)\n"
269 " LIST\n"
270 " LITERAL(\"asd.cc\")\n"
271 " LITERAL(\"foo.cc\")\n"
272 " BLOCK\n"
273 " BINARY(+=)\n"
274 " IDENTIFIER(dependencies)\n"
275 " LIST\n"
276 " LITERAL(\"bar.cc\")\n";
277 DoParserPrintTest(input, expected);
278 }
279
280 TEST(Parser, NestedBlocks) {
281 const char* input = "{cc_test(\"foo\") {{foo=1}{}}}";
282 const char* expected =
283 "BLOCK\n"
284 " BLOCK\n"
285 " FUNCTION(cc_test)\n"
286 " LIST\n"
287 " LITERAL(\"foo\")\n"
288 " BLOCK\n"
289 " BLOCK\n"
290 " BINARY(=)\n"
291 " IDENTIFIER(foo)\n"
292 " LITERAL(1)\n"
293 " BLOCK\n";
294 DoParserPrintTest(input, expected);
295 }
296
297 TEST(Parser, List) {
298 const char* input = "[] a = [1,asd,] b = [1, 2+3 - foo]";
299 const char* expected =
300 "BLOCK\n"
301 " LIST\n"
302 " BINARY(=)\n"
303 " IDENTIFIER(a)\n"
304 " LIST\n"
305 " LITERAL(1)\n"
306 " IDENTIFIER(asd)\n"
307 " BINARY(=)\n"
308 " IDENTIFIER(b)\n"
309 " LIST\n"
310 " LITERAL(1)\n"
311 " BINARY(+)\n"
312 " LITERAL(2)\n"
313 " BINARY(-)\n"
314 " LITERAL(3)\n"
315 " IDENTIFIER(foo)\n";
316 DoParserPrintTest(input, expected);
317
318 DoParserErrorTest("[a, 2+,]", 1, 7);
319 DoParserErrorTest("[,]", 1, 2);
320 DoParserErrorTest("[a,,]", 1, 4);
321 }
322
323 TEST(Parser, UnterminatedBlock) {
324 DoParserErrorTest("hello {", 1, 7);
325 }
326
327 TEST(Parser, BadlyTerminatedNumber) {
328 DoParserErrorTest("1234z", 1, 5);
329 }
OLDNEW
« 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