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

Unified Diff: tools/gn/operators_unittest.cc

Issue 2224343003: GN: Throw an error overwriting a nonempty scope. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Comment Created 4 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 side-by-side diff with in-line comments
Download patch
Index: tools/gn/operators_unittest.cc
diff --git a/tools/gn/operators_unittest.cc b/tools/gn/operators_unittest.cc
index e2c396c4b684c23ed0326f749d167b3dc2418dc9..4fa3d114017e88883fdbd7910a6e0d855403bc38 100644
--- a/tools/gn/operators_unittest.cc
+++ b/tools/gn/operators_unittest.cc
@@ -26,15 +26,6 @@ bool IsValueStringEqualing(const Value& v, const char* s) {
return v.string_value() == s;
}
-// Returns a list populated with a single literal Value corresponding to the
-// given token. The token must outlive the list (since the list will just
-// copy the reference).
-std::unique_ptr<ListNode> ListWithLiteral(const Token& token) {
- std::unique_ptr<ListNode> list(new ListNode);
- list->append_item(std::unique_ptr<ParseNode>(new LiteralNode(token)));
- return list;
-}
-
// This parse node is for passing to tests. It returns a canned value for
// Execute().
class TestParseNode : public ParseNode {
@@ -59,6 +50,54 @@ class TestParseNode : public ParseNode {
Value value_;
};
+// Sets up a BinaryOpNode for testing.
+class TestBinaryOpNode : public BinaryOpNode {
+ public:
+ // Input token value string must outlive class.
+ TestBinaryOpNode(Token::Type op_token_type,
+ const char* op_token_value)
+ : BinaryOpNode(),
+ op_token_ownership_(Location(), op_token_type, op_token_value) {
+ set_op(op_token_ownership_);
+ }
+
+ void SetLeftToValue(const Value& value) {
+ set_left(std::unique_ptr<ParseNode>(new TestParseNode(value)));
+ }
+
+ // Sets the left-hand side of the operator to an identifier node, this is
+ // used for testing assignments. Input string must outlive class.
+ void SetLeftToIdentifier(const char* identifier) {
+ left_identifier_token_ownership_ =
+ Token(Location(), Token::IDENTIFIER, identifier);
+ set_left(std::unique_ptr<ParseNode>(
+ new IdentifierNode(left_identifier_token_ownership_)));
+ }
+
+ void SetRightToValue(const Value& value) {
+ set_right(std::unique_ptr<ParseNode>(new TestParseNode(value)));
+ }
+ void SetRightToListOfValue(const Value& value) {
+ Value list(nullptr, Value::LIST);
+ list.list_value().push_back(value);
+ set_right(std::unique_ptr<ParseNode>(new TestParseNode(list)));
+ }
+ void SetRightToListOfValue(const Value& value1, const Value& value2) {
+ Value list(nullptr, Value::LIST);
+ list.list_value().push_back(value1);
+ list.list_value().push_back(value2);
+ set_right(std::unique_ptr<ParseNode>(new TestParseNode(list)));
+ }
+
+ private:
+ // The base class takes the Token by reference, this manages the lifetime.
+ Token op_token_ownership_;
+
+ // When setting the left to an identifier, this manages the lifetime of
+ // the identifier token.
+ Token left_identifier_token_ownership_;
+};
+
} // namespace
TEST(Operators, SourcesAppend) {
@@ -70,15 +109,8 @@ TEST(Operators, SourcesAppend) {
setup.scope()->SetValue(sources, Value(nullptr, Value::LIST), nullptr);
// Set up the operator.
- BinaryOpNode node;
- const char token_value[] = "+=";
- Token op(Location(), Token::PLUS_EQUALS, token_value);
- node.set_op(op);
-
- // Append to the sources variable.
- Token identifier_token(Location(), Token::IDENTIFIER, sources);
- node.set_left(
- std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token)));
+ TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
+ node.SetLeftToIdentifier(sources);
// Set up the filter on the scope to remove everything ending with "rm"
std::unique_ptr<PatternList> pattern_list(new PatternList);
@@ -86,31 +118,25 @@ TEST(Operators, SourcesAppend) {
setup.scope()->set_sources_assignment_filter(std::move(pattern_list));
// Append an integer.
- const char integer_value[] = "5";
- Token integer(Location(), Token::INTEGER, integer_value);
- node.set_right(ListWithLiteral(integer));
+ node.SetRightToListOfValue(Value(nullptr, static_cast<int64_t>(5)));
node.Execute(setup.scope(), &err);
EXPECT_FALSE(err.has_error());
// Append a string that doesn't match the pattern, it should get appended.
- const char string_1_value[] = "\"good\"";
- Token string_1(Location(), Token::STRING, string_1_value);
- node.set_right(ListWithLiteral(string_1));
+ const char string1[] = "good";
+ node.SetRightToListOfValue(Value(nullptr, string1));
node.Execute(setup.scope(), &err);
EXPECT_FALSE(err.has_error());
// Append a string that does match the pattern, it should be a no-op.
- const char string_2_value[] = "\"foo-rm\"";
- Token string_2(Location(), Token::STRING, string_2_value);
- node.set_right(ListWithLiteral(string_2));
+ const char string2[] = "foo-rm";
+ node.SetRightToListOfValue(Value(nullptr, string2));
node.Execute(setup.scope(), &err);
EXPECT_FALSE(err.has_error());
// Append a list with the two strings from above.
- ListNode list;
- list.append_item(std::unique_ptr<ParseNode>(new LiteralNode(string_1)));
- list.append_item(std::unique_ptr<ParseNode>(new LiteralNode(string_2)));
- ExecuteBinaryOperator(setup.scope(), &node, node.left(), &list, &err);
+ node.SetRightToListOfValue(Value(nullptr, string1), Value(nullptr, string2));
+ node.Execute(setup.scope(), &err);
EXPECT_FALSE(err.has_error());
// The sources variable in the scope should now have: [ 5, "good", "good" ]
@@ -133,23 +159,14 @@ TEST(Operators, ListAppend) {
const char foo[] = "foo";
setup.scope()->SetValue(foo, Value(nullptr, Value::LIST), nullptr);
- // Set up the operator.
- BinaryOpNode node;
- const char token_value[] = "+=";
- Token op(Location(), Token::PLUS_EQUALS, token_value);
- node.set_op(op);
-
- // Append to the foo variable.
- Token identifier_token(Location(), Token::IDENTIFIER, foo);
- node.set_left(
- std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token)));
+ // Set up the operator to append to "foo".
+ TestBinaryOpNode node(Token::PLUS_EQUALS, "+=");
+ node.SetLeftToIdentifier(foo);
// Append a list with a list, the result should be a nested list.
- std::unique_ptr<ListNode> outer_list(new ListNode);
- const char twelve_str[] = "12";
- Token twelve(Location(), Token::INTEGER, twelve_str);
- outer_list->append_item(ListWithLiteral(twelve));
- node.set_right(std::move(outer_list));
+ Value inner_list(nullptr, Value::LIST);
+ inner_list.list_value().push_back(Value(nullptr, static_cast<int64_t>(12)));
+ node.SetRightToListOfValue(inner_list);
Value ret = ExecuteBinaryOperator(setup.scope(), &node, node.left(),
node.right(), &err);
@@ -177,7 +194,7 @@ TEST(Operators, ListAppend) {
EXPECT_TRUE(err.has_error());
err = Err();
- node.set_right(std::unique_ptr<ParseNode>(new LiteralNode(twelve)));
+ node.SetRightToValue(Value(nullptr, static_cast<int64_t>(12)));
ExecuteBinaryOperator(setup.scope(), &node, node.left(), node.right(), &err);
EXPECT_TRUE(err.has_error());
}
@@ -194,26 +211,14 @@ TEST(Operators, ListRemove) {
test_list.list_value().push_back(Value(nullptr, foo_str));
// Set up "var" with an the test list.
- const char var_str[] = "var";
- setup.scope()->SetValue(var_str, test_list, nullptr);
+ const char var[] = "var";
+ setup.scope()->SetValue(var, test_list, nullptr);
- // Set up the operator.
- BinaryOpNode node;
- const char token_value[] = "-=";
- Token op(Location(), Token::MINUS_EQUALS, token_value);
- node.set_op(op);
-
- // Do -= on the var.
- Token identifier_token(Location(), Token::IDENTIFIER, var_str);
- node.set_left(
- std::unique_ptr<ParseNode>(new IdentifierNode(identifier_token)));
+ TestBinaryOpNode node(Token::MINUS_EQUALS, "-=");
+ node.SetLeftToIdentifier(var);
// Subtract a list consisting of "foo".
- Value foo_list(nullptr, Value::LIST);
- foo_list.list_value().push_back(Value(nullptr, foo_str));
- std::unique_ptr<ParseNode> outer_list(new TestParseNode(foo_list));
- node.set_right(std::move(outer_list));
-
+ node.SetRightToListOfValue(Value(nullptr, foo_str));
Value result = ExecuteBinaryOperator(
setup.scope(), &node, node.left(), node.right(), &err);
EXPECT_FALSE(err.has_error());
@@ -224,7 +229,7 @@ TEST(Operators, ListRemove) {
// The "var" variable should have been updated. Both instances of "foo" are
// deleted.
- const Value* new_value = setup.scope()->GetValue(var_str);
+ const Value* new_value = setup.scope()->GetValue(var);
ASSERT_TRUE(new_value);
ASSERT_EQ(Value::LIST, new_value->type());
ASSERT_EQ(1u, new_value->list_value().size());
@@ -236,16 +241,9 @@ TEST(Operators, ShortCircuitAnd) {
Err err;
TestWithScope setup;
- // Set up the operator.
- BinaryOpNode node;
- const char token_value[] = "&&";
- Token op(Location(), Token::BOOLEAN_AND, token_value);
- node.set_op(op);
-
- // Set the left to false.
- const char false_str[] = "false";
- Token false_tok(Location(), Token::FALSE_TOKEN, false_str);
- node.set_left(std::unique_ptr<ParseNode>(new LiteralNode(false_tok)));
+ // Set a && operator with the left to false.
+ TestBinaryOpNode node(Token::BOOLEAN_AND, "&&");
+ node.SetLeftToValue(Value(nullptr, false));
// Set right as foo, but don't define a value for it.
const char foo[] = "foo";
@@ -262,16 +260,9 @@ TEST(Operators, ShortCircuitOr) {
Err err;
TestWithScope setup;
- // Set up the operator.
- BinaryOpNode node;
- const char token_value[] = "||";
- Token op(Location(), Token::BOOLEAN_OR, token_value);
- node.set_op(op);
-
- // Set the left to false.
- const char false_str[] = "true";
- Token false_tok(Location(), Token::TRUE_TOKEN, false_str);
- node.set_left(std::unique_ptr<ParseNode>(new LiteralNode(false_tok)));
+ // Set a || operator with the left to true.
+ TestBinaryOpNode node(Token::BOOLEAN_OR, "||");
+ node.SetLeftToValue(Value(nullptr, true));
// Set right as foo, but don't define a value for it.
const char foo[] = "foo";
@@ -283,3 +274,59 @@ TEST(Operators, ShortCircuitOr) {
node.right(), &err);
EXPECT_FALSE(err.has_error());
}
+
+// Overwriting nonempty lists and scopes with other nonempty lists and scopes
+// should be disallowed.
+TEST(Operators, NonemptyOverwriting) {
+ Err err;
+ TestWithScope setup;
+
+ // Set up "foo" with a nonempty list.
+ const char foo[] = "foo";
+ Value old_value(nullptr, Value::LIST);
+ old_value.list_value().push_back(Value(nullptr, "string"));
+ setup.scope()->SetValue(foo, old_value, nullptr);
+
+ TestBinaryOpNode node(Token::EQUAL, "=");
+ node.SetLeftToIdentifier(foo);
+
+ // Assigning a nonempty list should fail.
+ node.SetRightToListOfValue(Value(nullptr, "string"));
+ node.Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Replacing nonempty list.", err.message());
+ err = Err();
+
+ // Assigning an empty list should succeed.
+ node.SetRightToValue(Value(nullptr, Value::LIST));
+ node.Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error());
+ const Value* new_value = setup.scope()->GetValue(foo);
+ ASSERT_TRUE(new_value);
+ ASSERT_EQ(Value::LIST, new_value->type());
+ ASSERT_TRUE(new_value->list_value().empty());
+
+ // Set up "foo" with a nonempty scope.
+ const char bar[] = "bar";
+ old_value = Value(
+ nullptr, std::unique_ptr<Scope>(new Scope(setup.settings())));
+ old_value.scope_value()->SetValue(bar, Value(nullptr, "bar"), nullptr);
+ setup.scope()->SetValue(foo, old_value, nullptr);
+
+ // Assigning a nonempty scope should fail (re-use old_value copy).
+ node.SetRightToValue(old_value);
+ node.Execute(setup.scope(), &err);
+ ASSERT_TRUE(err.has_error());
+ EXPECT_EQ("Replacing nonempty scope.", err.message());
+ err = Err();
+
+ // Assigning an empty list should succeed.
+ node.SetRightToValue(
+ Value(nullptr, std::unique_ptr<Scope>(new Scope(setup.settings()))));
+ node.Execute(setup.scope(), &err);
+ ASSERT_FALSE(err.has_error());
+ new_value = setup.scope()->GetValue(foo);
+ ASSERT_TRUE(new_value);
+ ASSERT_EQ(Value::SCOPE, new_value->type());
+ ASSERT_FALSE(new_value->scope_value()->HasValues(Scope::SEARCH_CURRENT));
+}
« tools/gn/operators.cc ('K') | « tools/gn/operators.cc ('k') | tools/gn/scope.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698