#include #include #include #include #include #include "antlr4-runtime.h" #include "dbms/dbms.hpp" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "query/context.hpp" #include "query/frontend/ast/ast.hpp" #include "query/frontend/ast/cypher_main_visitor.hpp" #include "query/frontend/opencypher/parser.hpp" namespace { using namespace query; using namespace query::frontend; using testing::UnorderedElementsAre; using testing::Pair; class AstGenerator { public: AstGenerator(const std::string &query) : dbms_(), db_accessor_(dbms_.active()), context_(Config{}, *db_accessor_), query_string_(query), parser_(query), visitor_(context_), query_([&]() { visitor_.visit(parser_.tree()); return visitor_.query(); }()) {} Dbms dbms_; std::unique_ptr db_accessor_; Context context_; std::string query_string_; ::frontend::opencypher::Parser parser_; CypherMainVisitor visitor_; Query *query_; }; TEST(CypherMainVisitorTest, SyntaxException) { ASSERT_THROW(AstGenerator("CREATE ()-[*1...2]-()"), SyntaxException); } TEST(CypherMainVisitorTest, PropertyLookup) { AstGenerator ast_generator("RETURN n.x"); auto *query = ast_generator.query_; ASSERT_EQ(query->clauses_.size(), 1U); auto *return_clause = dynamic_cast(query->clauses_[0]); auto *property_lookup = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(property_lookup->expression_); auto identifier = dynamic_cast(property_lookup->expression_); ASSERT_TRUE(identifier); ASSERT_EQ(identifier->name_, "n"); ASSERT_EQ(property_lookup->property_, ast_generator.db_accessor_->property("x")); } TEST(CypherMainVisitorTest, ReturnNamedIdentifier) { AstGenerator ast_generator("RETURN var AS var5"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *named_expr = return_clause->named_expressions_[0]; ASSERT_EQ(named_expr->name_, "var5"); auto *identifier = dynamic_cast(named_expr->expression_); ASSERT_EQ(identifier->name_, "var"); } TEST(CypherMainVisitorTest, IntegerLiteral) { AstGenerator ast_generator("RETURN 42"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 42); } TEST(CypherMainVisitorTest, IntegerLiteralTooLarge) { ASSERT_THROW(AstGenerator("RETURN 10000000000000000000000000"), SemanticException); } TEST(CypherMainVisitorTest, BooleanLiteralTrue) { AstGenerator ast_generator("RETURN TrUe"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), true); } TEST(CypherMainVisitorTest, BooleanLiteralFalse) { AstGenerator ast_generator("RETURN faLSE"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), false); } TEST(CypherMainVisitorTest, NullLiteral) { AstGenerator ast_generator("RETURN nULl"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.type(), TypedValue::Type::Null); } TEST(CypherMainVisitorTest, OrOperator) { AstGenerator ast_generator("RETURN true Or false oR n"); auto *query = ast_generator.query_; ASSERT_EQ(query->clauses_.size(), 1U); auto *return_clause = dynamic_cast(query->clauses_[0]); auto *or_operator2 = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(or_operator2); auto *or_operator1 = dynamic_cast(or_operator2->expression1_); ASSERT_TRUE(or_operator1); auto *operand1 = dynamic_cast(or_operator1->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), true); auto *operand2 = dynamic_cast(or_operator1->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), false); auto *operand3 = dynamic_cast(or_operator2->expression2_); ASSERT_TRUE(operand3); ASSERT_EQ(operand3->name_, "n"); } TEST(CypherMainVisitorTest, XorOperator) { AstGenerator ast_generator("RETURN true xOr false"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *xor_operator = dynamic_cast( return_clause->named_expressions_[0]->expression_); auto *operand1 = dynamic_cast(xor_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), true); auto *operand2 = dynamic_cast(xor_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), false); } TEST(CypherMainVisitorTest, AndOperator) { AstGenerator ast_generator("RETURN true and false"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *and_operator = dynamic_cast( return_clause->named_expressions_[0]->expression_); auto *operand1 = dynamic_cast(and_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), true); auto *operand2 = dynamic_cast(and_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), false); } TEST(CypherMainVisitorTest, AdditionSubtractionOperators) { AstGenerator ast_generator("RETURN 1 - 2 + 3"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *addition_operator = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(addition_operator); auto *subtraction_operator = dynamic_cast(addition_operator->expression1_); ASSERT_TRUE(subtraction_operator); auto *operand1 = dynamic_cast(subtraction_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 1); auto *operand2 = dynamic_cast(subtraction_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), 2); auto *operand3 = dynamic_cast(addition_operator->expression2_); ASSERT_TRUE(operand3); ASSERT_EQ(operand3->value_.Value(), 3); } TEST(CypherMainVisitorTest, MulitplicationOperator) { AstGenerator ast_generator("RETURN 2 * 3"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *mult_operator = dynamic_cast( return_clause->named_expressions_[0]->expression_); auto *operand1 = dynamic_cast(mult_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 2); auto *operand2 = dynamic_cast(mult_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), 3); } TEST(CypherMainVisitorTest, DivisionOperator) { AstGenerator ast_generator("RETURN 2 / 3"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *div_operator = dynamic_cast( return_clause->named_expressions_[0]->expression_); auto *operand1 = dynamic_cast(div_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 2); auto *operand2 = dynamic_cast(div_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), 3); } TEST(CypherMainVisitorTest, ModOperator) { AstGenerator ast_generator("RETURN 2 % 3"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *mod_operator = dynamic_cast( return_clause->named_expressions_[0]->expression_); auto *operand1 = dynamic_cast(mod_operator->expression1_); ASSERT_TRUE(operand1); ASSERT_EQ(operand1->value_.Value(), 2); auto *operand2 = dynamic_cast(mod_operator->expression2_); ASSERT_TRUE(operand2); ASSERT_EQ(operand2->value_.Value(), 3); } #define CHECK_COMPARISON(TYPE, VALUE1, VALUE2) \ do { \ auto *and_operator = dynamic_cast(_operator); \ ASSERT_TRUE(and_operator); \ _operator = and_operator->expression1_; \ auto *cmp_operator = dynamic_cast(and_operator->expression2_); \ ASSERT_TRUE(cmp_operator); \ auto *operand1 = dynamic_cast(cmp_operator->expression1_); \ ASSERT_EQ(operand1->value_.Value(), VALUE1); \ auto *operand2 = dynamic_cast(cmp_operator->expression2_); \ ASSERT_EQ(operand2->value_.Value(), VALUE2); \ } while (0) TEST(CypherMainVisitorTest, ComparisonOperators) { AstGenerator ast_generator("RETURN 2 = 3 != 4 <> 5 < 6 > 7 <= 8 >= 9"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); Expression *_operator = return_clause->named_expressions_[0]->expression_; CHECK_COMPARISON(GreaterEqualOperator, 8, 9); CHECK_COMPARISON(LessEqualOperator, 7, 8); CHECK_COMPARISON(GreaterOperator, 6, 7); CHECK_COMPARISON(LessOperator, 5, 6); CHECK_COMPARISON(NotEqualOperator, 4, 5); CHECK_COMPARISON(NotEqualOperator, 3, 4); auto *cmp_operator = dynamic_cast(_operator); ASSERT_TRUE(cmp_operator); auto *operand1 = dynamic_cast(cmp_operator->expression1_); ASSERT_EQ(operand1->value_.Value(), 2); auto *operand2 = dynamic_cast(cmp_operator->expression2_); ASSERT_EQ(operand2->value_.Value(), 3); } #undef CHECK_COMPARISON TEST(CypherMainVisitorTest, NotOperator) { AstGenerator ast_generator("RETURN not true"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *not_operator = dynamic_cast( return_clause->named_expressions_[0]->expression_); auto *operand = dynamic_cast(not_operator->expression_); ASSERT_TRUE(operand); ASSERT_EQ(operand->value_.Value(), true); } TEST(CypherMainVisitorTest, UnaryMinusPlusOperators) { AstGenerator ast_generator("RETURN -+5"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *unary_minus_operator = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(unary_minus_operator); auto *unary_plus_operator = dynamic_cast(unary_minus_operator->expression_); ASSERT_TRUE(unary_plus_operator); auto *operand = dynamic_cast(unary_plus_operator->expression_); ASSERT_TRUE(operand); ASSERT_EQ(operand->value_.Value(), 5); } TEST(CypherMainVisitorTest, StringLiteralDoubleQuotes) { AstGenerator ast_generator("RETURN \"mi'rko\""); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), "mi'rko"); } TEST(CypherMainVisitorTest, StringLiteralSingleQuotes) { AstGenerator ast_generator("RETURN 'mi\"rko'"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), "mi\"rko"); } TEST(CypherMainVisitorTest, StringLiteralEscapedChars) { AstGenerator ast_generator( "RETURN '\\\\\\'\\\"\\b\\B\\f\\F\\n\\N\\r\\R\\t\\T'"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), "\\'\"\b\b\f\f\n\n\r\r\t\t"); } TEST(CypherMainVisitorTest, StringLiteralEscapedUtf16) { AstGenerator ast_generator("RETURN '\\u221daaa\\U221daaa'"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), u8"\u221daaa\u221daaa"); } TEST(CypherMainVisitorTest, StringLiteralEscapedUtf32) { AstGenerator ast_generator("RETURN '\\u0001F600aaaa\\U0001F600aaaaaaaa'"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), u8"\U0001F600aaaa\U0001F600aaaaaaaa"); } TEST(CypherMainVisitorTest, DoubleLiteral) { AstGenerator ast_generator("RETURN 3.5"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 3.5); } TEST(CypherMainVisitorTest, DoubleLiteralExponent) { AstGenerator ast_generator("RETURN 5e-1"); auto *query = ast_generator.query_; auto *return_clause = dynamic_cast(query->clauses_[0]); auto *literal = dynamic_cast( return_clause->named_expressions_[0]->expression_); ASSERT_TRUE(literal); ASSERT_EQ(literal->value_.Value(), 0.5); } TEST(CypherMainVisitorTest, NodePattern) { AstGenerator ast_generator("MATCH (:label1:label2:label3 {a : 5, b : 10})"); auto *query = ast_generator.query_; ASSERT_EQ(query->clauses_.size(), 1U); auto *match = dynamic_cast(query->clauses_[0]); ASSERT_TRUE(match); ASSERT_EQ(match->patterns_.size(), 1U); ASSERT_TRUE(match->patterns_[0]); ASSERT_EQ(match->patterns_[0]->atoms_.size(), 1U); auto node = dynamic_cast(match->patterns_[0]->atoms_[0]); ASSERT_TRUE(node); ASSERT_TRUE(node->identifier_); ASSERT_EQ(node->identifier_->name_, CypherMainVisitor::kAnonPrefix + std::to_string(1)); ASSERT_THAT(node->labels_, UnorderedElementsAre( ast_generator.db_accessor_->label("label1"), ast_generator.db_accessor_->label("label2"), ast_generator.db_accessor_->label("label3"))); std::unordered_map properties; for (auto x : node->properties_) { auto *literal = dynamic_cast(x.second); ASSERT_TRUE(literal); ASSERT_TRUE(literal->value_.type() == TypedValue::Type::Int); properties[x.first] = literal->value_.Value(); } ASSERT_THAT(properties, UnorderedElementsAre( Pair(ast_generator.db_accessor_->property("a"), 5), Pair(ast_generator.db_accessor_->property("b"), 10))); } TEST(CypherMainVisitorTest, NodePatternIdentifier) { AstGenerator ast_generator("MATCH (var)"); auto *query = ast_generator.query_; auto *match = dynamic_cast(query->clauses_[0]); auto node = dynamic_cast(match->patterns_[0]->atoms_[0]); ASSERT_TRUE(node->identifier_); ASSERT_EQ(node->identifier_->name_, "var"); ASSERT_THAT(node->labels_, UnorderedElementsAre()); ASSERT_THAT(node->properties_, UnorderedElementsAre()); } TEST(CypherMainVisitorTest, RelationshipPatternNoDetails) { AstGenerator ast_generator("MATCH ()--()"); auto *query = ast_generator.query_; auto *match = dynamic_cast(query->clauses_[0]); ASSERT_EQ(match->patterns_.size(), 1U); ASSERT_TRUE(match->patterns_[0]); ASSERT_EQ(match->patterns_[0]->atoms_.size(), 3U); auto *node1 = dynamic_cast(match->patterns_[0]->atoms_[0]); ASSERT_TRUE(node1); auto *edge = dynamic_cast(match->patterns_[0]->atoms_[1]); ASSERT_TRUE(edge); auto *node2 = dynamic_cast(match->patterns_[0]->atoms_[2]); ASSERT_TRUE(node2); ASSERT_EQ(edge->direction_, EdgeAtom::Direction::BOTH); ASSERT_TRUE(edge->identifier_); ASSERT_THAT(edge->identifier_->name_, CypherMainVisitor::kAnonPrefix + std::to_string(2)); } // PatternPart in braces. TEST(CypherMainVisitorTest, PatternPartBraces) { AstGenerator ast_generator("MATCH ((()--()))"); auto *query = ast_generator.query_; auto *match = dynamic_cast(query->clauses_[0]); ASSERT_EQ(match->patterns_.size(), 1U); ASSERT_TRUE(match->patterns_[0]); ASSERT_EQ(match->patterns_[0]->atoms_.size(), 3U); auto *node1 = dynamic_cast(match->patterns_[0]->atoms_[0]); ASSERT_TRUE(node1); auto *edge = dynamic_cast(match->patterns_[0]->atoms_[1]); ASSERT_TRUE(edge); auto *node2 = dynamic_cast(match->patterns_[0]->atoms_[2]); ASSERT_TRUE(node2); ASSERT_EQ(edge->direction_, EdgeAtom::Direction::BOTH); ASSERT_TRUE(edge->identifier_); ASSERT_THAT(edge->identifier_->name_, CypherMainVisitor::kAnonPrefix + std::to_string(2)); } TEST(CypherMainVisitorTest, RelationshipPatternDetails) { AstGenerator ast_generator("MATCH ()<-[:type1|type2 {a : 5, b : 10}]-()"); auto *query = ast_generator.query_; auto *match = dynamic_cast(query->clauses_[0]); auto *edge = dynamic_cast(match->patterns_[0]->atoms_[1]); ASSERT_EQ(edge->direction_, EdgeAtom::Direction::LEFT); ASSERT_THAT( edge->edge_types_, UnorderedElementsAre(ast_generator.db_accessor_->edge_type("type1"), ast_generator.db_accessor_->edge_type("type2"))); std::unordered_map properties; for (auto x : edge->properties_) { auto *literal = dynamic_cast(x.second); ASSERT_TRUE(literal); ASSERT_TRUE(literal->value_.type() == TypedValue::Type::Int); properties[x.first] = literal->value_.Value(); } ASSERT_THAT(properties, UnorderedElementsAre( Pair(ast_generator.db_accessor_->property("a"), 5), Pair(ast_generator.db_accessor_->property("b"), 10))); } TEST(CypherMainVisitorTest, RelationshipPatternVariable) { AstGenerator ast_generator("MATCH ()-[var]->()"); auto *query = ast_generator.query_; auto *match = dynamic_cast(query->clauses_[0]); auto *edge = dynamic_cast(match->patterns_[0]->atoms_[1]); ASSERT_EQ(edge->direction_, EdgeAtom::Direction::RIGHT); ASSERT_TRUE(edge->identifier_); ASSERT_THAT(edge->identifier_->name_, "var"); } // // Relationship with unbounded variable range. // TEST(CypherMainVisitorTest, RelationshipPatternUnbounded) { // ParserTables parser("CREATE ()-[*]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); // CompareRelationships(*parser.relationships_.begin(), // Relationship::Direction::BOTH, {}, {}, true, 1, // LLONG_MAX); // } // // // Relationship with lower bounded variable range. // TEST(CypherMainVisitorTest, RelationshipPatternLowerBounded) { // ParserTables parser("CREATE ()-[*5..]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); // CompareRelationships(*parser.relationships_.begin(), // Relationship::Direction::BOTH, {}, {}, true, 5, // LLONG_MAX); // } // // // Relationship with upper bounded variable range. // TEST(CypherMainVisitorTest, RelationshipPatternUpperBounded) { // ParserTables parser("CREATE ()-[*..10]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); // CompareRelationships(*parser.relationships_.begin(), // Relationship::Direction::BOTH, {}, {}, true, 1, 10); // } // // // Relationship with lower and upper bounded variable range. // TEST(CypherMainVisitorTest, RelationshipPatternLowerUpperBounded) { // ParserTables parser("CREATE ()-[*5..10]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); // CompareRelationships(*parser.relationships_.begin(), // Relationship::Direction::BOTH, {}, {}, true, 5, 10); // } // // // Relationship with fixed number of edges. // TEST(CypherMainVisitorTest, RelationshipPatternFixedRange) { // ParserTables parser("CREATE ()-[*10]-()"); // ASSERT_EQ(parser.identifiers_map_.size(), 0U); // ASSERT_EQ(parser.relationships_.size(), 1U); // CompareRelationships(*parser.relationships_.begin(), // Relationship::Direction::BOTH, {}, {}, true, 10, 10); // } // // // // PatternPart with variable. // TEST(CypherMainVisitorTest, PatternPartVariable) { // ParserTables parser("CREATE var=()--()"); // ASSERT_EQ(parser.identifiers_map_.size(), 1U); // ASSERT_EQ(parser.pattern_parts_.size(), 1U); // ASSERT_EQ(parser.relationships_.size(), 1U); // ASSERT_EQ(parser.nodes_.size(), 2U); // ASSERT_EQ(parser.pattern_parts_.begin()->second.nodes.size(), 2U); // ASSERT_EQ(parser.pattern_parts_.begin()->second.relationships.size(), 1U); // ASSERT_NE(parser.identifiers_map_.find("var"), // parser.identifiers_map_.end()); // auto output_identifier = parser.identifiers_map_["var"]; // ASSERT_NE(parser.pattern_parts_.find(output_identifier), // parser.pattern_parts_.end()); // } TEST(CypherMainVisitorTest, ReturnUnanemdIdentifier) { AstGenerator ast_generator("RETURN var"); auto *query = ast_generator.query_; ASSERT_EQ(query->clauses_.size(), 1U); auto *return_clause = dynamic_cast(query->clauses_[0]); ASSERT_TRUE(return_clause); ASSERT_EQ(return_clause->named_expressions_.size(), 1U); auto *named_expr = return_clause->named_expressions_[0]; ASSERT_TRUE(named_expr); ASSERT_EQ(named_expr->name_, "var"); auto *identifier = dynamic_cast(named_expr->expression_); ASSERT_TRUE(identifier); ASSERT_EQ(identifier->name_, "var"); } TEST(CypherMainVisitorTest, Create) { AstGenerator ast_generator("CREATE (n)"); auto *query = ast_generator.query_; ASSERT_EQ(query->clauses_.size(), 1U); auto *create = dynamic_cast(query->clauses_[0]); ASSERT_TRUE(create); ASSERT_EQ(create->patterns_.size(), 1U); ASSERT_TRUE(create->patterns_[0]); ASSERT_EQ(create->patterns_[0]->atoms_.size(), 1U); auto node = dynamic_cast(create->patterns_[0]->atoms_[0]); ASSERT_TRUE(node); ASSERT_TRUE(node->identifier_); ASSERT_EQ(node->identifier_->name_, "n"); } }