| 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));
|
| +}
|
|
|