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 "testing/gtest/include/gtest/gtest.h"
6 #include "tools/gn/input_file.h"
7 #include "tools/gn/token.h"
8 #include "tools/gn/tokenizer.h"
12 struct TokenExpectation
{
18 bool CheckTokenizer(const char* input
, const TokenExpectation (&expect
)[len
]) {
19 InputFile
input_file(SourceFile("/test"));
20 input_file
.SetContents(input
);
23 std::vector
<Token
> results
= Tokenizer::Tokenize(&input_file
, &err
);
25 if (results
.size() != len
)
27 for (size_t i
= 0; i
< len
; i
++) {
28 if (expect
[i
].type
!= results
[i
].type())
30 if (expect
[i
].value
!= results
[i
].value())
38 TEST(Tokenizer
, Empty
) {
39 InputFile
empty_string_input(SourceFile("/test"));
40 empty_string_input
.SetContents("");
43 std::vector
<Token
> results
= Tokenizer::Tokenize(&empty_string_input
, &err
);
44 EXPECT_TRUE(results
.empty());
46 InputFile
whitespace_input(SourceFile("/test"));
47 whitespace_input
.SetContents(" \r \n \r\n");
49 results
= Tokenizer::Tokenize(&whitespace_input
, &err
);
50 EXPECT_TRUE(results
.empty());
53 TEST(Tokenizer
, Identifier
) {
54 TokenExpectation one_ident
[] = {
55 { Token::IDENTIFIER
, "foo" }
57 EXPECT_TRUE(CheckTokenizer(" foo ", one_ident
));
60 TEST(Tokenizer
, Integer
) {
61 TokenExpectation integers
[] = {
62 { Token::INTEGER
, "123" },
63 { Token::INTEGER
, "-123" }
65 EXPECT_TRUE(CheckTokenizer(" 123 -123 ", integers
));
68 TEST(Tokenizer
, IntegerNoSpace
) {
69 TokenExpectation integers
[] = {
70 { Token::INTEGER
, "123" },
71 { Token::INTEGER
, "-123" }
73 EXPECT_TRUE(CheckTokenizer(" 123-123 ", integers
));
76 TEST(Tokenizer
, String
) {
77 TokenExpectation strings
[] = {
78 { Token::STRING
, "\"foo\"" },
79 { Token::STRING
, "\"bar\\\"baz\"" },
80 { Token::STRING
, "\"asdf\\\\\"" }
82 EXPECT_TRUE(CheckTokenizer(" \"foo\" \"bar\\\"baz\" \"asdf\\\\\" ",
86 TEST(Tokenizer
, Operator
) {
87 TokenExpectation operators
[] = {
88 { Token::MINUS
, "-" },
90 { Token::EQUAL
, "=" },
91 { Token::PLUS_EQUALS
, "+=" },
92 { Token::MINUS_EQUALS
, "-=" },
93 { Token::NOT_EQUAL
, "!=" },
94 { Token::EQUAL_EQUAL
, "==" },
95 { Token::LESS_THAN
, "<" },
96 { Token::GREATER_THAN
, ">" },
97 { Token::LESS_EQUAL
, "<=" },
98 { Token::GREATER_EQUAL
, ">=" },
100 { Token::BOOLEAN_OR
, "||" },
101 { Token::BOOLEAN_AND
, "&&" },
103 { Token::COMMA
, "," },
105 EXPECT_TRUE(CheckTokenizer("- + = += -= != == < > <= >= ! || && . ,",
109 TEST(Tokenizer
, Scoper
) {
110 TokenExpectation scopers
[] = {
111 { Token::LEFT_BRACE
, "{" },
112 { Token::LEFT_BRACKET
, "[" },
113 { Token::RIGHT_BRACKET
, "]" },
114 { Token::RIGHT_BRACE
, "}" },
115 { Token::LEFT_PAREN
, "(" },
116 { Token::RIGHT_PAREN
, ")" },
118 EXPECT_TRUE(CheckTokenizer("{[ ]} ()", scopers
));
121 TEST(Tokenizer
, FunctionCall
) {
122 TokenExpectation fn
[] = {
123 { Token::IDENTIFIER
, "fun" },
124 { Token::LEFT_PAREN
, "(" },
125 { Token::STRING
, "\"foo\"" },
126 { Token::RIGHT_PAREN
, ")" },
127 { Token::LEFT_BRACE
, "{" },
128 { Token::IDENTIFIER
, "foo" },
129 { Token::EQUAL
, "=" },
130 { Token::INTEGER
, "12" },
131 { Token::RIGHT_BRACE
, "}" },
133 EXPECT_TRUE(CheckTokenizer("fun(\"foo\") {\nfoo = 12}", fn
));
136 TEST(Tokenizer
, Locations
) {
137 InputFile
input(SourceFile("/test"));
138 input
.SetContents("1 2 \"three\"\n 4");
140 std::vector
<Token
> results
= Tokenizer::Tokenize(&input
, &err
);
142 ASSERT_EQ(4u, results
.size());
143 ASSERT_TRUE(results
[0].location() == Location(&input
, 1, 1, 1));
144 ASSERT_TRUE(results
[1].location() == Location(&input
, 1, 3, 3));
145 ASSERT_TRUE(results
[2].location() == Location(&input
, 1, 5, 5));
146 ASSERT_TRUE(results
[3].location() == Location(&input
, 2, 3, 8));
149 TEST(Tokenizer
, ByteOffsetOfNthLine
) {
150 EXPECT_EQ(0u, Tokenizer::ByteOffsetOfNthLine("foo", 1));
152 // Windows and Posix have different line endings, so check the byte at the
153 // location rather than the offset.
154 char input1
[] = "aaa\nxaa\n\nya";
155 EXPECT_EQ('x', input1
[Tokenizer::ByteOffsetOfNthLine(input1
, 2)]);
156 EXPECT_EQ('y', input1
[Tokenizer::ByteOffsetOfNthLine(input1
, 4)]);
160 input2
[1] = '\n'; // Manually set to avoid Windows double-byte endings.
162 EXPECT_EQ(0u, Tokenizer::ByteOffsetOfNthLine(input2
, 1));
163 EXPECT_EQ(2u, Tokenizer::ByteOffsetOfNthLine(input2
, 2));
166 TEST(Tokenizer
, Comments
) {
167 TokenExpectation fn
[] = {
168 { Token::LINE_COMMENT
, "# Stuff" },
169 { Token::IDENTIFIER
, "fun" },
170 { Token::LEFT_PAREN
, "(" },
171 { Token::STRING
, "\"foo\"" },
172 { Token::RIGHT_PAREN
, ")" },
173 { Token::LEFT_BRACE
, "{" },
174 { Token::SUFFIX_COMMENT
, "# Things" },
175 { Token::LINE_COMMENT
, "#Wee" },
176 { Token::IDENTIFIER
, "foo" },
177 { Token::EQUAL
, "=" },
178 { Token::INTEGER
, "12" },
179 { Token::SUFFIX_COMMENT
, "#Zip" },
180 { Token::RIGHT_BRACE
, "}" },
182 EXPECT_TRUE(CheckTokenizer(
184 "fun(\"foo\") { # Things\n"
191 TEST(Tokenizer
, CommentsContinued
) {
192 // In the first test, the comments aren't horizontally aligned, so they're
193 // considered separate. In the second test, they are, so "B" is a
194 // continuation of "A" (another SUFFIX comment).
195 TokenExpectation fn1
[] = {
196 { Token::IDENTIFIER
, "fun" },
197 { Token::LEFT_PAREN
, "(" },
198 { Token::STRING
, "\"foo\"" },
199 { Token::RIGHT_PAREN
, ")" },
200 { Token::LEFT_BRACE
, "{" },
201 { Token::SUFFIX_COMMENT
, "# A" },
202 { Token::LINE_COMMENT
, "# B" },
203 { Token::RIGHT_BRACE
, "}" },
205 EXPECT_TRUE(CheckTokenizer(
206 "fun(\"foo\") { # A\n"
211 TokenExpectation fn2
[] = {
212 { Token::IDENTIFIER
, "fun" },
213 { Token::LEFT_PAREN
, "(" },
214 { Token::STRING
, "\"foo\"" },
215 { Token::RIGHT_PAREN
, ")" },
216 { Token::LEFT_BRACE
, "{" },
217 { Token::SUFFIX_COMMENT
, "# A" },
218 { Token::SUFFIX_COMMENT
, "# B" },
219 { Token::RIGHT_BRACE
, "}" },
221 EXPECT_TRUE(CheckTokenizer(
222 "fun(\"foo\") { # A\n"
223 " # B\n" // Note that these are aligned, the \"s move A out.