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/parse_tree.h"
8 #include "tools/gn/scope.h"
9 #include "tools/gn/template.h"
10 #include "tools/gn/test_with_scope.h"
14 bool HasStringValueEqualTo(const Scope
* scope
,
16 const char* expected_value
) {
17 const Value
* value
= scope
->GetValue(name
);
20 if (value
->type() != Value::STRING
)
22 return value
->string_value() == expected_value
;
27 TEST(Scope
, NonRecursiveMergeTo
) {
30 // Make a pretend parse node with proper tracking that we can blame for the
32 InputFile
input_file(SourceFile("//foo"));
33 Token
assignment_token(Location(&input_file
, 1, 1, 1), Token::STRING
,
35 LiteralNode assignment
;
36 assignment
.set_value(assignment_token
);
38 // Add some values to the scope.
39 Value
old_value(&assignment
, "hello");
40 setup
.scope()->SetValue("v", old_value
, &assignment
);
41 base::StringPiece
private_var_name("_private");
42 setup
.scope()->SetValue(private_var_name
, old_value
, &assignment
);
44 // Add some templates to the scope.
45 FunctionCallNode templ_definition
;
46 scoped_refptr
<Template
> templ(new Template(setup
.scope(), &templ_definition
));
47 setup
.scope()->AddTemplate("templ", templ
.get());
48 scoped_refptr
<Template
> private_templ(
49 new Template(setup
.scope(), &templ_definition
));
50 setup
.scope()->AddTemplate("_templ", private_templ
.get());
52 // Detect collisions of values' values.
54 Scope
new_scope(setup
.settings());
55 Value
new_value(&assignment
, "goodbye");
56 new_scope
.SetValue("v", new_value
, &assignment
);
59 EXPECT_FALSE(setup
.scope()->NonRecursiveMergeTo(
60 &new_scope
, Scope::MergeOptions(),
61 &assignment
, "error", &err
));
62 EXPECT_TRUE(err
.has_error());
65 // Template name collisions.
67 Scope
new_scope(setup
.settings());
69 scoped_refptr
<Template
> new_templ(
70 new Template(&new_scope
, &templ_definition
));
71 new_scope
.AddTemplate("templ", new_templ
.get());
74 EXPECT_FALSE(setup
.scope()->NonRecursiveMergeTo(
75 &new_scope
, Scope::MergeOptions(), &assignment
, "error", &err
));
76 EXPECT_TRUE(err
.has_error());
79 // The clobber flag should just overwrite colliding values.
81 Scope
new_scope(setup
.settings());
82 Value
new_value(&assignment
, "goodbye");
83 new_scope
.SetValue("v", new_value
, &assignment
);
86 Scope::MergeOptions options
;
87 options
.clobber_existing
= true;
88 EXPECT_TRUE(setup
.scope()->NonRecursiveMergeTo(
89 &new_scope
, options
, &assignment
, "error", &err
));
90 EXPECT_FALSE(err
.has_error());
92 const Value
* found_value
= new_scope
.GetValue("v");
93 ASSERT_TRUE(found_value
);
94 EXPECT_TRUE(old_value
== *found_value
);
97 // Clobber flag for templates.
99 Scope
new_scope(setup
.settings());
101 scoped_refptr
<Template
> new_templ(
102 new Template(&new_scope
, &templ_definition
));
103 new_scope
.AddTemplate("templ", new_templ
.get());
104 Scope::MergeOptions options
;
105 options
.clobber_existing
= true;
108 EXPECT_TRUE(setup
.scope()->NonRecursiveMergeTo(
109 &new_scope
, options
, &assignment
, "error", &err
));
110 EXPECT_FALSE(err
.has_error());
112 const Template
* found_value
= new_scope
.GetTemplate("templ");
113 ASSERT_TRUE(found_value
);
114 EXPECT_TRUE(templ
.get() == found_value
);
117 // Don't flag values that technically collide but have the same value.
119 Scope
new_scope(setup
.settings());
120 Value
new_value(&assignment
, "hello");
121 new_scope
.SetValue("v", new_value
, &assignment
);
124 EXPECT_TRUE(setup
.scope()->NonRecursiveMergeTo(
125 &new_scope
, Scope::MergeOptions(), &assignment
, "error", &err
));
126 EXPECT_FALSE(err
.has_error());
129 // Templates that technically collide but are the same.
131 Scope
new_scope(setup
.settings());
133 scoped_refptr
<Template
> new_templ(
134 new Template(&new_scope
, &templ_definition
));
135 new_scope
.AddTemplate("templ", templ
.get());
138 EXPECT_TRUE(setup
.scope()->NonRecursiveMergeTo(
139 &new_scope
, Scope::MergeOptions(), &assignment
, "error", &err
));
140 EXPECT_FALSE(err
.has_error());
143 // Copy private values and templates.
145 Scope
new_scope(setup
.settings());
148 EXPECT_TRUE(setup
.scope()->NonRecursiveMergeTo(
149 &new_scope
, Scope::MergeOptions(), &assignment
, "error", &err
));
150 EXPECT_FALSE(err
.has_error());
151 EXPECT_TRUE(new_scope
.GetValue(private_var_name
));
152 EXPECT_TRUE(new_scope
.GetTemplate("_templ"));
155 // Skip private values and templates.
157 Scope
new_scope(setup
.settings());
160 Scope::MergeOptions options
;
161 options
.skip_private_vars
= true;
162 EXPECT_TRUE(setup
.scope()->NonRecursiveMergeTo(
163 &new_scope
, options
, &assignment
, "error", &err
));
164 EXPECT_FALSE(err
.has_error());
165 EXPECT_FALSE(new_scope
.GetValue(private_var_name
));
166 EXPECT_FALSE(new_scope
.GetTemplate("_templ"));
171 Scope
new_scope(setup
.settings());
174 Scope::MergeOptions options
;
175 EXPECT_TRUE(setup
.scope()->NonRecursiveMergeTo(
176 &new_scope
, options
, &assignment
, "error", &err
));
177 EXPECT_FALSE(err
.has_error());
178 EXPECT_FALSE(new_scope
.CheckForUnusedVars(&err
));
179 EXPECT_TRUE(err
.has_error());
184 Scope
new_scope(setup
.settings());
187 Scope::MergeOptions options
;
188 options
.mark_used
= true;
189 EXPECT_TRUE(setup
.scope()->NonRecursiveMergeTo(
190 &new_scope
, options
, &assignment
, "error", &err
));
191 EXPECT_FALSE(err
.has_error());
192 EXPECT_TRUE(new_scope
.CheckForUnusedVars(&err
));
193 EXPECT_FALSE(err
.has_error());
197 TEST(Scope
, MakeClosure
) {
198 // Create 3 nested scopes [const root from setup] <- nested1 <- nested2.
201 // Make a pretend parse node with proper tracking that we can blame for the
203 InputFile
input_file(SourceFile("//foo"));
204 Token
assignment_token(Location(&input_file
, 1, 1, 1), Token::STRING
,
206 LiteralNode assignment
;
207 assignment
.set_value(assignment_token
);
208 setup
.scope()->SetValue("on_root", Value(&assignment
, "on_root"),
211 // Root scope should be const from the nested caller's perspective.
212 Scope
nested1(static_cast<const Scope
*>(setup
.scope()));
213 nested1
.SetValue("on_one", Value(&assignment
, "on_one"), &assignment
);
215 Scope
nested2(&nested1
);
216 nested2
.SetValue("on_one", Value(&assignment
, "on_two"), &assignment
);
217 nested2
.SetValue("on_two", Value(&assignment
, "on_two2"), &assignment
);
219 // Making a closure from the root scope.
220 scoped_ptr
<Scope
> result
= setup
.scope()->MakeClosure();
221 EXPECT_FALSE(result
->containing()); // Should have no containing scope.
222 EXPECT_TRUE(result
->GetValue("on_root")); // Value should be copied.
224 // Making a closure from the second nested scope.
225 result
= nested2
.MakeClosure();
226 EXPECT_EQ(setup
.scope(),
227 result
->containing()); // Containing scope should be the root.
228 EXPECT_TRUE(HasStringValueEqualTo(result
.get(), "on_root", "on_root"));
229 EXPECT_TRUE(HasStringValueEqualTo(result
.get(), "on_one", "on_two"));
230 EXPECT_TRUE(HasStringValueEqualTo(result
.get(), "on_two", "on_two2"));
233 TEST(Scope
, GetMutableValue
) {
236 // Make a pretend parse node with proper tracking that we can blame for the
238 InputFile
input_file(SourceFile("//foo"));
239 Token
assignment_token(Location(&input_file
, 1, 1, 1), Token::STRING
,
241 LiteralNode assignment
;
242 assignment
.set_value(assignment_token
);
244 const char kOnConst
[] = "on_const";
245 const char kOnMutable1
[] = "on_mutable1";
246 const char kOnMutable2
[] = "on_mutable2";
248 Value
value(&assignment
, "hello");
250 // Create a root scope with one value.
251 Scope
root_scope(setup
.settings());
252 root_scope
.SetValue(kOnConst
, value
, &assignment
);
254 // Create a first nested scope with a different value.
255 const Scope
* const_root_scope
= &root_scope
;
256 Scope
mutable_scope1(const_root_scope
);
257 mutable_scope1
.SetValue(kOnMutable1
, value
, &assignment
);
259 // Create a second nested scope with a different value.
260 Scope
mutable_scope2(&mutable_scope1
);
261 mutable_scope2
.SetValue(kOnMutable2
, value
, &assignment
);
263 // Check getting root scope values.
264 EXPECT_TRUE(mutable_scope2
.GetValue(kOnConst
, true));
265 EXPECT_FALSE(mutable_scope2
.GetMutableValue(kOnConst
, true));
267 // Test reading a value from scope 1.
268 Value
* mutable1_result
= mutable_scope2
.GetMutableValue(kOnMutable1
, false);
269 ASSERT_TRUE(mutable1_result
);
270 EXPECT_TRUE(*mutable1_result
== value
);
272 // Make sure CheckForUnusedVars works on scope1 (we didn't mark the value as
273 // used in the previous step).
275 EXPECT_FALSE(mutable_scope1
.CheckForUnusedVars(&err
));
276 mutable1_result
= mutable_scope2
.GetMutableValue(kOnMutable1
, true);
277 EXPECT_TRUE(mutable1_result
);
279 EXPECT_TRUE(mutable_scope1
.CheckForUnusedVars(&err
));
281 // Test reading a value from scope 2.
282 Value
* mutable2_result
= mutable_scope2
.GetMutableValue(kOnMutable2
, true);
283 ASSERT_TRUE(mutable2_result
);
284 EXPECT_TRUE(*mutable2_result
== value
);
287 TEST(Scope
, RemovePrivateIdentifiers
) {
289 setup
.scope()->SetValue("a", Value(nullptr, true), nullptr);
290 setup
.scope()->SetValue("_b", Value(nullptr, true), nullptr);
292 setup
.scope()->RemovePrivateIdentifiers();
293 EXPECT_TRUE(setup
.scope()->GetValue("a"));
294 EXPECT_FALSE(setup
.scope()->GetValue("_b"));