OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "tools/gn/operators.h" | 5 #include "tools/gn/operators.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "testing/gtest/include/gtest/gtest.h" | 10 #include "testing/gtest/include/gtest/gtest.h" |
11 #include "tools/gn/parse_tree.h" | 11 #include "tools/gn/parse_tree.h" |
12 #include "tools/gn/pattern.h" | 12 #include "tools/gn/pattern.h" |
13 #include "tools/gn/test_with_scope.h" | 13 #include "tools/gn/test_with_scope.h" |
14 | 14 |
15 namespace { | 15 namespace { |
16 | 16 |
17 bool IsValueIntegerEqualing(const Value& v, int64_t i) { | 17 bool IsValueIntegerEqualing(const Value& v, int64_t i) { |
18 if (v.type() != Value::INTEGER) | 18 if (v.type() != Value::INTEGER) |
19 return false; | 19 return false; |
20 return v.int_value() == i; | 20 return v.int_value() == i; |
21 } | 21 } |
22 | 22 |
23 bool IsValueStringEqualing(const Value& v, const char* s) { | 23 bool IsValueStringEqualing(const Value& v, const char* s) { |
24 if (v.type() != Value::STRING) | 24 if (v.type() != Value::STRING) |
25 return false; | 25 return false; |
26 return v.string_value() == s; | 26 return v.string_value() == s; |
27 } | 27 } |
28 | 28 |
29 // Returns a list populated with a single literal Value corresponding to the | |
30 // given token. The token must outlive the list (since the list will just | |
31 // copy the reference). | |
32 std::unique_ptr<ListNode> ListWithLiteral(const Token& token) { | |
33 std::unique_ptr<ListNode> list(new ListNode); | |
34 list->append_item(std::unique_ptr<ParseNode>(new LiteralNode(token))); | |
35 return list; | |
36 } | |
37 | |
38 // This parse node is for passing to tests. It returns a canned value for | 29 // This parse node is for passing to tests. It returns a canned value for |
39 // Execute(). | 30 // Execute(). |
40 class TestParseNode : public ParseNode { | 31 class TestParseNode : public ParseNode { |
41 public: | 32 public: |
42 TestParseNode(const Value& v) : value_(v) { | 33 TestParseNode(const Value& v) : value_(v) { |
43 } | 34 } |
44 | 35 |
45 Value Execute(Scope* scope, Err* err) const override { | 36 Value Execute(Scope* scope, Err* err) const override { |
46 return value_; | 37 return value_; |
47 } | 38 } |
48 LocationRange GetRange() const override { | 39 LocationRange GetRange() const override { |
49 return LocationRange(); | 40 return LocationRange(); |
50 } | 41 } |
51 Err MakeErrorDescribing(const std::string& msg, | 42 Err MakeErrorDescribing(const std::string& msg, |
52 const std::string& help) const override { | 43 const std::string& help) const override { |
53 return Err(this, msg); | 44 return Err(this, msg); |
54 } | 45 } |
55 void Print(std::ostream& out, int indent) const override { | 46 void Print(std::ostream& out, int indent) const override { |
56 } | 47 } |
57 | 48 |
58 private: | 49 private: |
59 Value value_; | 50 Value value_; |
60 }; | 51 }; |
61 | 52 |
| 53 // Sets up a BinaryOpNode for testing. |
| 54 class TestBinaryOpNode : public BinaryOpNode { |
| 55 public: |
| 56 // Input token value string must outlive class. |
| 57 TestBinaryOpNode(Token::Type op_token_type, |
| 58 const char* op_token_value) |
| 59 : BinaryOpNode(), |
| 60 op_token_ownership_(Location(), op_token_type, op_token_value) { |
| 61 set_op(op_token_ownership_); |
| 62 } |
| 63 |
| 64 void SetLeftToValue(const Value& value) { |
| 65 set_left(std::unique_ptr<ParseNode>(new TestParseNode(value))); |
| 66 } |
| 67 |
| 68 // Sets the left-hand side of the operator to an identifier node, this is |
| 69 // used for testing assignments. Input string must outlive class. |
| 70 void SetLeftToIdentifier(const char* identifier) { |
| 71 left_identifier_token_ownership_ = |
| 72 Token(Location(), Token::IDENTIFIER, identifier); |
| 73 set_left(std::unique_ptr<ParseNode>( |
| 74 new IdentifierNode(left_identifier_token_ownership_))); |
| 75 } |
| 76 |
| 77 void SetRightToValue(const Value& value) { |
| 78 set_right(std::unique_ptr<ParseNode>(new TestParseNode(value))); |
| 79 } |
| 80 void SetRightToListOfValue(const Value& value) { |
| 81 Value list(nullptr, Value::LIST); |
| 82 list.list_value().push_back(value); |
| 83 set_right(std::unique_ptr<ParseNode>(new TestParseNode(list))); |
| 84 } |
| 85 void SetRightToListOfValue(const Value& value1, const Value& value2) { |
| 86 Value list(nullptr, Value::LIST); |
| 87 list.list_value().push_back(value1); |
| 88 list.list_value().push_back(value2); |
| 89 set_right(std::unique_ptr<ParseNode>(new TestParseNode(list))); |
| 90 } |
| 91 |
| 92 private: |
| 93 // The base class takes the Token by reference, this manages the lifetime. |
| 94 Token op_token_ownership_; |
| 95 |
| 96 // When setting the left to an identifier, this manages the lifetime of |
| 97 // the identifier token. |
| 98 Token left_identifier_token_ownership_; |
| 99 }; |
| 100 |
62 } // namespace | 101 } // namespace |
63 | 102 |
64 TEST(Operators, SourcesAppend) { | 103 TEST(Operators, SourcesAppend) { |
65 Err err; | 104 Err err; |
66 TestWithScope setup; | 105 TestWithScope setup; |
67 | 106 |
68 // Set up "sources" with an empty list. | 107 // Set up "sources" with an empty list. |
69 const char sources[] = "sources"; | 108 const char sources[] = "sources"; |
70 setup.scope()->SetValue(sources, Value(nullptr, Value::LIST), nullptr); | 109 setup.scope()->SetValue(sources, Value(nullptr, Value::LIST), nullptr); |
71 | 110 |
72 // Set up the operator. | 111 // Set up the operator. |
73 BinaryOpNode node; | 112 TestBinaryOpNode node(Token::PLUS_EQUALS, "+="); |
74 const char token_value[] = "+="; | 113 node.SetLeftToIdentifier(sources); |
75 Token op(Location(), Token::PLUS_EQUALS, token_value); | |
76 node.set_op(op); | |
77 | |
78 // Append to the sources variable. | |
79 Token identifier_token(Location(), Token::IDENTIFIER, sources); | |
80 node.set_left( | |
81 std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token))); | |
82 | 114 |
83 // Set up the filter on the scope to remove everything ending with "rm" | 115 // Set up the filter on the scope to remove everything ending with "rm" |
84 std::unique_ptr<PatternList> pattern_list(new PatternList); | 116 std::unique_ptr<PatternList> pattern_list(new PatternList); |
85 pattern_list->Append(Pattern("*rm")); | 117 pattern_list->Append(Pattern("*rm")); |
86 setup.scope()->set_sources_assignment_filter(std::move(pattern_list)); | 118 setup.scope()->set_sources_assignment_filter(std::move(pattern_list)); |
87 | 119 |
88 // Append an integer. | 120 // Append an integer. |
89 const char integer_value[] = "5"; | 121 node.SetRightToListOfValue(Value(nullptr, static_cast<int64_t>(5))); |
90 Token integer(Location(), Token::INTEGER, integer_value); | |
91 node.set_right(ListWithLiteral(integer)); | |
92 node.Execute(setup.scope(), &err); | 122 node.Execute(setup.scope(), &err); |
93 EXPECT_FALSE(err.has_error()); | 123 EXPECT_FALSE(err.has_error()); |
94 | 124 |
95 // Append a string that doesn't match the pattern, it should get appended. | 125 // Append a string that doesn't match the pattern, it should get appended. |
96 const char string_1_value[] = "\"good\""; | 126 const char string1[] = "good"; |
97 Token string_1(Location(), Token::STRING, string_1_value); | 127 node.SetRightToListOfValue(Value(nullptr, string1)); |
98 node.set_right(ListWithLiteral(string_1)); | |
99 node.Execute(setup.scope(), &err); | 128 node.Execute(setup.scope(), &err); |
100 EXPECT_FALSE(err.has_error()); | 129 EXPECT_FALSE(err.has_error()); |
101 | 130 |
102 // Append a string that does match the pattern, it should be a no-op. | 131 // Append a string that does match the pattern, it should be a no-op. |
103 const char string_2_value[] = "\"foo-rm\""; | 132 const char string2[] = "foo-rm"; |
104 Token string_2(Location(), Token::STRING, string_2_value); | 133 node.SetRightToListOfValue(Value(nullptr, string2)); |
105 node.set_right(ListWithLiteral(string_2)); | |
106 node.Execute(setup.scope(), &err); | 134 node.Execute(setup.scope(), &err); |
107 EXPECT_FALSE(err.has_error()); | 135 EXPECT_FALSE(err.has_error()); |
108 | 136 |
109 // Append a list with the two strings from above. | 137 // Append a list with the two strings from above. |
110 ListNode list; | 138 node.SetRightToListOfValue(Value(nullptr, string1), Value(nullptr, string2)); |
111 list.append_item(std::unique_ptr<ParseNode>(new LiteralNode(string_1))); | 139 node.Execute(setup.scope(), &err); |
112 list.append_item(std::unique_ptr<ParseNode>(new LiteralNode(string_2))); | |
113 ExecuteBinaryOperator(setup.scope(), &node, node.left(), &list, &err); | |
114 EXPECT_FALSE(err.has_error()); | 140 EXPECT_FALSE(err.has_error()); |
115 | 141 |
116 // The sources variable in the scope should now have: [ 5, "good", "good" ] | 142 // The sources variable in the scope should now have: [ 5, "good", "good" ] |
117 const Value* value = setup.scope()->GetValue(sources); | 143 const Value* value = setup.scope()->GetValue(sources); |
118 ASSERT_TRUE(value); | 144 ASSERT_TRUE(value); |
119 ASSERT_EQ(Value::LIST, value->type()); | 145 ASSERT_EQ(Value::LIST, value->type()); |
120 ASSERT_EQ(3u, value->list_value().size()); | 146 ASSERT_EQ(3u, value->list_value().size()); |
121 EXPECT_TRUE(IsValueIntegerEqualing(value->list_value()[0], 5)); | 147 EXPECT_TRUE(IsValueIntegerEqualing(value->list_value()[0], 5)); |
122 EXPECT_TRUE(IsValueStringEqualing(value->list_value()[1], "good")); | 148 EXPECT_TRUE(IsValueStringEqualing(value->list_value()[1], "good")); |
123 EXPECT_TRUE(IsValueStringEqualing(value->list_value()[2], "good")); | 149 EXPECT_TRUE(IsValueStringEqualing(value->list_value()[2], "good")); |
124 } | 150 } |
125 | 151 |
126 // Note that the SourcesAppend test above tests the basic list + list features, | 152 // Note that the SourcesAppend test above tests the basic list + list features, |
127 // this test handles the other cases. | 153 // this test handles the other cases. |
128 TEST(Operators, ListAppend) { | 154 TEST(Operators, ListAppend) { |
129 Err err; | 155 Err err; |
130 TestWithScope setup; | 156 TestWithScope setup; |
131 | 157 |
132 // Set up "foo" with an empty list. | 158 // Set up "foo" with an empty list. |
133 const char foo[] = "foo"; | 159 const char foo[] = "foo"; |
134 setup.scope()->SetValue(foo, Value(nullptr, Value::LIST), nullptr); | 160 setup.scope()->SetValue(foo, Value(nullptr, Value::LIST), nullptr); |
135 | 161 |
136 // Set up the operator. | 162 // Set up the operator to append to "foo". |
137 BinaryOpNode node; | 163 TestBinaryOpNode node(Token::PLUS_EQUALS, "+="); |
138 const char token_value[] = "+="; | 164 node.SetLeftToIdentifier(foo); |
139 Token op(Location(), Token::PLUS_EQUALS, token_value); | |
140 node.set_op(op); | |
141 | |
142 // Append to the foo variable. | |
143 Token identifier_token(Location(), Token::IDENTIFIER, foo); | |
144 node.set_left( | |
145 std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token))); | |
146 | 165 |
147 // Append a list with a list, the result should be a nested list. | 166 // Append a list with a list, the result should be a nested list. |
148 std::unique_ptr<ListNode> outer_list(new ListNode); | 167 Value inner_list(nullptr, Value::LIST); |
149 const char twelve_str[] = "12"; | 168 inner_list.list_value().push_back(Value(nullptr, static_cast<int64_t>(12))); |
150 Token twelve(Location(), Token::INTEGER, twelve_str); | 169 node.SetRightToListOfValue(inner_list); |
151 outer_list->append_item(ListWithLiteral(twelve)); | |
152 node.set_right(std::move(outer_list)); | |
153 | 170 |
154 Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(), | 171 Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(), |
155 node.right(), &err); | 172 node.right(), &err); |
156 EXPECT_FALSE(err.has_error()); | 173 EXPECT_FALSE(err.has_error()); |
157 | 174 |
158 // Return from the operator should always be "none", it should update the | 175 // Return from the operator should always be "none", it should update the |
159 // value only. | 176 // value only. |
160 EXPECT_EQ(Value::NONE, ret.type()); | 177 EXPECT_EQ(Value::NONE, ret.type()); |
161 | 178 |
162 // The value should be updated with "[ [ 12 ] ]" | 179 // The value should be updated with "[ [ 12 ] ]" |
163 Value result = *setup.scope()->GetValue(foo); | 180 Value result = *setup.scope()->GetValue(foo); |
164 ASSERT_EQ(Value::LIST, result.type()); | 181 ASSERT_EQ(Value::LIST, result.type()); |
165 ASSERT_EQ(1u, result.list_value().size()); | 182 ASSERT_EQ(1u, result.list_value().size()); |
166 ASSERT_EQ(Value::LIST, result.list_value()[0].type()); | 183 ASSERT_EQ(Value::LIST, result.list_value()[0].type()); |
167 ASSERT_EQ(1u, result.list_value()[0].list_value().size()); | 184 ASSERT_EQ(1u, result.list_value()[0].list_value().size()); |
168 ASSERT_EQ(Value::INTEGER, result.list_value()[0].list_value()[0].type()); | 185 ASSERT_EQ(Value::INTEGER, result.list_value()[0].list_value()[0].type()); |
169 ASSERT_EQ(12, result.list_value()[0].list_value()[0].int_value()); | 186 ASSERT_EQ(12, result.list_value()[0].list_value()[0].int_value()); |
170 | 187 |
171 // Try to append an integer and a string directly (e.g. foo += "hi"). | 188 // Try to append an integer and a string directly (e.g. foo += "hi"). |
172 // This should fail. | 189 // This should fail. |
173 const char str_str[] = "\"hi\""; | 190 const char str_str[] = "\"hi\""; |
174 Token str(Location(), Token::STRING, str_str); | 191 Token str(Location(), Token::STRING, str_str); |
175 node.set_right(std::unique_ptr<ParseNode>(new LiteralNode(str))); | 192 node.set_right(std::unique_ptr<ParseNode>(new LiteralNode(str))); |
176 ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err); | 193 ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err); |
177 EXPECT_TRUE(err.has_error()); | 194 EXPECT_TRUE(err.has_error()); |
178 err = Err(); | 195 err = Err(); |
179 | 196 |
180 node.set_right(std::unique_ptr<ParseNode>(new LiteralNode(twelve))); | 197 node.SetRightToValue(Value(nullptr, static_cast<int64_t>(12))); |
181 ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err); | 198 ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err); |
182 EXPECT_TRUE(err.has_error()); | 199 EXPECT_TRUE(err.has_error()); |
183 } | 200 } |
184 | 201 |
185 TEST(Operators, ListRemove) { | 202 TEST(Operators, ListRemove) { |
186 Err err; | 203 Err err; |
187 TestWithScope setup; | 204 TestWithScope setup; |
188 | 205 |
189 const char foo_str[] = "foo"; | 206 const char foo_str[] = "foo"; |
190 const char bar_str[] = "bar"; | 207 const char bar_str[] = "bar"; |
191 Value test_list(nullptr, Value::LIST); | 208 Value test_list(nullptr, Value::LIST); |
192 test_list.list_value().push_back(Value(nullptr, foo_str)); | 209 test_list.list_value().push_back(Value(nullptr, foo_str)); |
193 test_list.list_value().push_back(Value(nullptr, bar_str)); | 210 test_list.list_value().push_back(Value(nullptr, bar_str)); |
194 test_list.list_value().push_back(Value(nullptr, foo_str)); | 211 test_list.list_value().push_back(Value(nullptr, foo_str)); |
195 | 212 |
196 // Set up "var" with an the test list. | 213 // Set up "var" with an the test list. |
197 const char var_str[] = "var"; | 214 const char var[] = "var"; |
198 setup.scope()->SetValue(var_str, test_list, nullptr); | 215 setup.scope()->SetValue(var, test_list, nullptr); |
199 | 216 |
200 // Set up the operator. | 217 TestBinaryOpNode node(Token::MINUS_EQUALS, "-="); |
201 BinaryOpNode node; | 218 node.SetLeftToIdentifier(var); |
202 const char token_value[] = "-="; | |
203 Token op(Location(), Token::MINUS_EQUALS, token_value); | |
204 node.set_op(op); | |
205 | |
206 // Do -= on the var. | |
207 Token identifier_token(Location(), Token::IDENTIFIER, var_str); | |
208 node.set_left( | |
209 std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token))); | |
210 | 219 |
211 // Subtract a list consisting of "foo". | 220 // Subtract a list consisting of "foo". |
212 Value foo_list(nullptr, Value::LIST); | 221 node.SetRightToListOfValue(Value(nullptr, foo_str)); |
213 foo_list.list_value().push_back(Value(nullptr, foo_str)); | |
214 std::unique_ptr<ParseNode> outer_list(new TestParseNode(foo_list)); | |
215 node.set_right(std::move(outer_list)); | |
216 | |
217 Value result = ExecuteBinaryOperator( | 222 Value result = ExecuteBinaryOperator( |
218 setup.scope(), &node, node.left(), node.right(), &err); | 223 setup.scope(), &node, node.left(), node.right(), &err); |
219 EXPECT_FALSE(err.has_error()); | 224 EXPECT_FALSE(err.has_error()); |
220 | 225 |
221 // -= returns an empty value to reduce the possibility of writing confusing | 226 // -= returns an empty value to reduce the possibility of writing confusing |
222 // cases like foo = bar += 1. | 227 // cases like foo = bar += 1. |
223 EXPECT_EQ(Value::NONE, result.type()); | 228 EXPECT_EQ(Value::NONE, result.type()); |
224 | 229 |
225 // The "var" variable should have been updated. Both instances of "foo" are | 230 // The "var" variable should have been updated. Both instances of "foo" are |
226 // deleted. | 231 // deleted. |
227 const Value* new_value = setup.scope()->GetValue(var_str); | 232 const Value* new_value = setup.scope()->GetValue(var); |
228 ASSERT_TRUE(new_value); | 233 ASSERT_TRUE(new_value); |
229 ASSERT_EQ(Value::LIST, new_value->type()); | 234 ASSERT_EQ(Value::LIST, new_value->type()); |
230 ASSERT_EQ(1u, new_value->list_value().size()); | 235 ASSERT_EQ(1u, new_value->list_value().size()); |
231 ASSERT_EQ(Value::STRING, new_value->list_value()[0].type()); | 236 ASSERT_EQ(Value::STRING, new_value->list_value()[0].type()); |
232 EXPECT_EQ("bar", new_value->list_value()[0].string_value()); | 237 EXPECT_EQ("bar", new_value->list_value()[0].string_value()); |
233 } | 238 } |
234 | 239 |
235 TEST(Operators, ShortCircuitAnd) { | 240 TEST(Operators, ShortCircuitAnd) { |
236 Err err; | 241 Err err; |
237 TestWithScope setup; | 242 TestWithScope setup; |
238 | 243 |
239 // Set up the operator. | 244 // Set a && operator with the left to false. |
240 BinaryOpNode node; | 245 TestBinaryOpNode node(Token::BOOLEAN_AND, "&&"); |
241 const char token_value[] = "&&"; | 246 node.SetLeftToValue(Value(nullptr, false)); |
242 Token op(Location(), Token::BOOLEAN_AND, token_value); | |
243 node.set_op(op); | |
244 | |
245 // Set the left to false. | |
246 const char false_str[] = "false"; | |
247 Token false_tok(Location(), Token::FALSE_TOKEN, false_str); | |
248 node.set_left(std::unique_ptr<ParseNode>(new LiteralNode(false_tok))); | |
249 | 247 |
250 // Set right as foo, but don't define a value for it. | 248 // Set right as foo, but don't define a value for it. |
251 const char foo[] = "foo"; | 249 const char foo[] = "foo"; |
252 Token identifier_token(Location(), Token::IDENTIFIER, foo); | 250 Token identifier_token(Location(), Token::IDENTIFIER, foo); |
253 node.set_right( | 251 node.set_right( |
254 std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token))); | 252 std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token))); |
255 | 253 |
256 Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(), | 254 Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(), |
257 node.right(), &err); | 255 node.right(), &err); |
258 EXPECT_FALSE(err.has_error()); | 256 EXPECT_FALSE(err.has_error()); |
259 } | 257 } |
260 | 258 |
261 TEST(Operators, ShortCircuitOr) { | 259 TEST(Operators, ShortCircuitOr) { |
262 Err err; | 260 Err err; |
263 TestWithScope setup; | 261 TestWithScope setup; |
264 | 262 |
265 // Set up the operator. | 263 // Set a || operator with the left to true. |
266 BinaryOpNode node; | 264 TestBinaryOpNode node(Token::BOOLEAN_OR, "||"); |
267 const char token_value[] = "||"; | 265 node.SetLeftToValue(Value(nullptr, true)); |
268 Token op(Location(), Token::BOOLEAN_OR, token_value); | |
269 node.set_op(op); | |
270 | |
271 // Set the left to false. | |
272 const char false_str[] = "true"; | |
273 Token false_tok(Location(), Token::TRUE_TOKEN, false_str); | |
274 node.set_left(std::unique_ptr<ParseNode>(new LiteralNode(false_tok))); | |
275 | 266 |
276 // Set right as foo, but don't define a value for it. | 267 // Set right as foo, but don't define a value for it. |
277 const char foo[] = "foo"; | 268 const char foo[] = "foo"; |
278 Token identifier_token(Location(), Token::IDENTIFIER, foo); | 269 Token identifier_token(Location(), Token::IDENTIFIER, foo); |
279 node.set_right( | 270 node.set_right( |
280 std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token))); | 271 std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token))); |
281 | 272 |
282 Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(), | 273 Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(), |
283 node.right(), &err); | 274 node.right(), &err); |
284 EXPECT_FALSE(err.has_error()); | 275 EXPECT_FALSE(err.has_error()); |
285 } | 276 } |
| 277 |
| 278 // Overwriting nonempty lists and scopes with other nonempty lists and scopes |
| 279 // should be disallowed. |
| 280 TEST(Operators, NonemptyOverwriting) { |
| 281 Err err; |
| 282 TestWithScope setup; |
| 283 |
| 284 // Set up "foo" with a nonempty list. |
| 285 const char foo[] = "foo"; |
| 286 Value old_value(nullptr, Value::LIST); |
| 287 old_value.list_value().push_back(Value(nullptr, "string")); |
| 288 setup.scope()->SetValue(foo, old_value, nullptr); |
| 289 |
| 290 TestBinaryOpNode node(Token::EQUAL, "="); |
| 291 node.SetLeftToIdentifier(foo); |
| 292 |
| 293 // Assigning a nonempty list should fail. |
| 294 node.SetRightToListOfValue(Value(nullptr, "string")); |
| 295 node.Execute(setup.scope(), &err); |
| 296 ASSERT_TRUE(err.has_error()); |
| 297 EXPECT_EQ("Replacing nonempty list.", err.message()); |
| 298 err = Err(); |
| 299 |
| 300 // Assigning an empty list should succeed. |
| 301 node.SetRightToValue(Value(nullptr, Value::LIST)); |
| 302 node.Execute(setup.scope(), &err); |
| 303 ASSERT_FALSE(err.has_error()); |
| 304 const Value* new_value = setup.scope()->GetValue(foo); |
| 305 ASSERT_TRUE(new_value); |
| 306 ASSERT_EQ(Value::LIST, new_value->type()); |
| 307 ASSERT_TRUE(new_value->list_value().empty()); |
| 308 |
| 309 // Set up "foo" with a nonempty scope. |
| 310 const char bar[] = "bar"; |
| 311 old_value = Value( |
| 312 nullptr, std::unique_ptr<Scope>(new Scope(setup.settings()))); |
| 313 old_value.scope_value()->SetValue(bar, Value(nullptr, "bar"), nullptr); |
| 314 setup.scope()->SetValue(foo, old_value, nullptr); |
| 315 |
| 316 // Assigning a nonempty scope should fail (re-use old_value copy). |
| 317 node.SetRightToValue(old_value); |
| 318 node.Execute(setup.scope(), &err); |
| 319 ASSERT_TRUE(err.has_error()); |
| 320 EXPECT_EQ("Replacing nonempty scope.", err.message()); |
| 321 err = Err(); |
| 322 |
| 323 // Assigning an empty list should succeed. |
| 324 node.SetRightToValue( |
| 325 Value(nullptr, std::unique_ptr<Scope>(new Scope(setup.settings())))); |
| 326 node.Execute(setup.scope(), &err); |
| 327 ASSERT_FALSE(err.has_error()); |
| 328 new_value = setup.scope()->GetValue(foo); |
| 329 ASSERT_TRUE(new_value); |
| 330 ASSERT_EQ(Value::SCOPE, new_value->type()); |
| 331 ASSERT_FALSE(new_value->scope_value()->HasValues(Scope::SEARCH_CURRENT)); |
| 332 } |
OLD | NEW |