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/operators.h"
7 #include "tools/gn/parse_tree.h"
8 #include "tools/gn/pattern.h"
9 #include "tools/gn/test_with_scope.h"
13 bool IsValueIntegerEqualing(const Value
& v
, int64 i
) {
14 if (v
.type() != Value::INTEGER
)
16 return v
.int_value() == i
;
19 bool IsValueStringEqualing(const Value
& v
, const char* s
) {
20 if (v
.type() != Value::STRING
)
22 return v
.string_value() == s
;
25 // Returns a list populated with a single literal Value corresponding to the
26 // given token. The token must outlive the list (since the list will just
27 // copy the reference).
28 scoped_ptr
<ListNode
> ListWithLiteral(const Token
& token
) {
29 scoped_ptr
<ListNode
> list(new ListNode
);
30 list
->append_item(scoped_ptr
<ParseNode
>(new LiteralNode(token
)));
36 TEST(Operators
, SourcesAppend
) {
40 // Set up "sources" with an empty list.
41 const char sources
[] = "sources";
42 setup
.scope()->SetValue(sources
, Value(NULL
, Value::LIST
), NULL
);
44 // Set up the operator.
46 const char token_value
[] = "+=";
47 Token
op(Location(), Token::PLUS_EQUALS
, token_value
);
50 // Append to the sources variable.
51 Token
identifier_token(Location(), Token::IDENTIFIER
, sources
);
52 node
.set_left(scoped_ptr
<ParseNode
>(new IdentifierNode(identifier_token
)));
54 // Set up the filter on the scope to remove everything ending with "rm"
55 scoped_ptr
<PatternList
> pattern_list(new PatternList
);
56 pattern_list
->Append(Pattern("*rm"));
57 setup
.scope()->set_sources_assignment_filter(pattern_list
.Pass());
60 const char integer_value
[] = "5";
61 Token
integer(Location(), Token::INTEGER
, integer_value
);
62 node
.set_right(ListWithLiteral(integer
).PassAs
<ParseNode
>());
63 node
.Execute(setup
.scope(), &err
);
64 EXPECT_FALSE(err
.has_error());
66 // Append a string that doesn't match the pattern, it should get appended.
67 const char string_1_value
[] = "\"good\"";
68 Token
string_1(Location(), Token::STRING
, string_1_value
);
69 node
.set_right(ListWithLiteral(string_1
).PassAs
<ParseNode
>());
70 node
.Execute(setup
.scope(), &err
);
71 EXPECT_FALSE(err
.has_error());
73 // Append a string that does match the pattern, it should be a no-op.
74 const char string_2_value
[] = "\"foo-rm\"";
75 Token
string_2(Location(), Token::STRING
, string_2_value
);
76 node
.set_right(ListWithLiteral(string_2
).PassAs
<ParseNode
>());
77 node
.Execute(setup
.scope(), &err
);
78 EXPECT_FALSE(err
.has_error());
80 // Append a list with the two strings from above.
82 list
.append_item(scoped_ptr
<ParseNode
>(new LiteralNode(string_1
)));
83 list
.append_item(scoped_ptr
<ParseNode
>(new LiteralNode(string_2
)));
84 ExecuteBinaryOperator(setup
.scope(), &node
, node
.left(), &list
, &err
);
85 EXPECT_FALSE(err
.has_error());
87 // The sources variable in the scope should now have: [ 5, "good", "good" ]
88 const Value
* value
= setup
.scope()->GetValue(sources
);
90 ASSERT_EQ(Value::LIST
, value
->type());
91 ASSERT_EQ(3u, value
->list_value().size());
92 EXPECT_TRUE(IsValueIntegerEqualing(value
->list_value()[0], 5));
93 EXPECT_TRUE(IsValueStringEqualing(value
->list_value()[1], "good"));
94 EXPECT_TRUE(IsValueStringEqualing(value
->list_value()[2], "good"));
97 // Note that the SourcesAppend test above tests the basic list + list features,
98 // this test handles the other cases.
99 TEST(Operators
, ListAppend
) {
103 // Set up "foo" with an empty list.
104 const char foo
[] = "foo";
105 setup
.scope()->SetValue(foo
, Value(NULL
, Value::LIST
), NULL
);
107 // Set up the operator.
109 const char token_value
[] = "+=";
110 Token
op(Location(), Token::PLUS_EQUALS
, token_value
);
113 // Append to the foo variable.
114 Token
identifier_token(Location(), Token::IDENTIFIER
, foo
);
115 node
.set_left(scoped_ptr
<ParseNode
>(new IdentifierNode(identifier_token
)));
117 // Append a list with a list, the result should be a nested list.
118 scoped_ptr
<ListNode
> outer_list(new ListNode
);
119 const char twelve_str
[] = "12";
120 Token
twelve(Location(), Token::INTEGER
, twelve_str
);
121 outer_list
->append_item(ListWithLiteral(twelve
).PassAs
<ParseNode
>());
122 node
.set_right(outer_list
.PassAs
<ParseNode
>());
124 Value ret
= ExecuteBinaryOperator(setup
.scope(), &node
, node
.left(),
126 EXPECT_FALSE(err
.has_error());
128 // Return from the operator should always be "none", it should update the
130 EXPECT_EQ(Value::NONE
, ret
.type());
132 // The value should be updated with "[ [ 12 ] ]"
133 Value result
= *setup
.scope()->GetValue(foo
);
134 ASSERT_EQ(Value::LIST
, result
.type());
135 ASSERT_EQ(1u, result
.list_value().size());
136 ASSERT_EQ(Value::LIST
, result
.list_value()[0].type());
137 ASSERT_EQ(1u, result
.list_value()[0].list_value().size());
138 ASSERT_EQ(Value::INTEGER
, result
.list_value()[0].list_value()[0].type());
139 ASSERT_EQ(12, result
.list_value()[0].list_value()[0].int_value());
141 // Try to append an integer and a string directly (e.g. foo += "hi").
143 const char str_str
[] = "\"hi\"";
144 Token
str(Location(), Token::STRING
, str_str
);
145 node
.set_right(scoped_ptr
<ParseNode
>(new LiteralNode(str
)));
146 ExecuteBinaryOperator(setup
.scope(), &node
, node
.left(), node
.right(), &err
);
147 EXPECT_TRUE(err
.has_error());
150 node
.set_right(scoped_ptr
<ParseNode
>(new LiteralNode(twelve
)));
151 ExecuteBinaryOperator(setup
.scope(), &node
, node
.left(), node
.right(), &err
);
152 EXPECT_TRUE(err
.has_error());
155 TEST(Operators
, ShortCircuitAnd
) {
159 // Set up the operator.
161 const char token_value
[] = "&&";
162 Token
op(Location(), Token::BOOLEAN_AND
, token_value
);
165 // Set the left to false.
166 const char false_str
[] = "false";
167 Token
false_tok(Location(), Token::FALSE_TOKEN
, false_str
);
168 node
.set_left(scoped_ptr
<ParseNode
>(new LiteralNode(false_tok
)));
170 // Set right as foo, but don't define a value for it.
171 const char foo
[] = "foo";
172 Token
identifier_token(Location(), Token::IDENTIFIER
, foo
);
173 node
.set_right(scoped_ptr
<ParseNode
>(new IdentifierNode(identifier_token
)));
175 Value ret
= ExecuteBinaryOperator(setup
.scope(), &node
, node
.left(),
177 EXPECT_FALSE(err
.has_error());
180 TEST(Operators
, ShortCircuitOr
) {
184 // Set up the operator.
186 const char token_value
[] = "||";
187 Token
op(Location(), Token::BOOLEAN_OR
, token_value
);
190 // Set the left to false.
191 const char false_str
[] = "true";
192 Token
false_tok(Location(), Token::TRUE_TOKEN
, false_str
);
193 node
.set_left(scoped_ptr
<ParseNode
>(new LiteralNode(false_tok
)));
195 // Set right as foo, but don't define a value for it.
196 const char foo
[] = "foo";
197 Token
identifier_token(Location(), Token::IDENTIFIER
, foo
);
198 node
.set_right(scoped_ptr
<ParseNode
>(new IdentifierNode(identifier_token
)));
200 Value ret
= ExecuteBinaryOperator(setup
.scope(), &node
, node
.left(),
202 EXPECT_FALSE(err
.has_error());