/* * GDevelop Core * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights * reserved. This project is released under the MIT License. */ #include "GDCore/IDE/Events/ExpressionNodeLocationFinder.h" #include "DummyPlatform.h" #include "GDCore/Events/Parsers/ExpressionParser2.h" #include "GDCore/Extensions/Platform.h" #include "GDCore/Extensions/PlatformExtension.h" #include "GDCore/Project/Layout.h" #include "GDCore/Project/Project.h" #include "catch.hpp" template bool CheckNodeAtLocationIs(gd::ExpressionParser2& parser, const gd::String& expression, size_t searchPosition) { auto node = parser.ParseExpression(expression); REQUIRE(node != nullptr); return dynamic_cast( gd::ExpressionNodeLocationFinder::GetNodeAtPosition( *node, searchPosition)) != nullptr; } template bool CheckParentNodeAtLocationIs(gd::ExpressionParser2& parser, const gd::String& expression, size_t searchPosition) { auto node = parser.ParseExpression(expression); REQUIRE(node != nullptr); return dynamic_cast( gd::ExpressionNodeLocationFinder::GetParentNodeAtPosition( *node, searchPosition)) != nullptr; } bool CheckNoNodeAtLocation(gd::ExpressionParser2& parser, const gd::String& expression, size_t searchPosition) { auto node = parser.ParseExpression(expression); REQUIRE(node != nullptr); return gd::ExpressionNodeLocationFinder::GetNodeAtPosition( *node, searchPosition) == nullptr; } bool CheckNoParentNodeAtLocation(gd::ExpressionParser2& parser, const gd::String& expression, size_t searchPosition) { auto node = parser.ParseExpression(expression); REQUIRE(node != nullptr); return gd::ExpressionNodeLocationFinder::GetParentNodeAtPosition( *node, searchPosition) == nullptr; } TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") { gd::Project project; gd::Platform platform; SetupProjectWithDummyPlatform(project, platform); auto& layout1 = project.InsertNewLayout("Layout1", 0); layout1.InsertNewObject(project, "MyExtension::Sprite", "MySpriteObject", 0); gd::ExpressionParser2 parser; SECTION("Empty expressions") { SECTION("Test 1") { REQUIRE(CheckNodeAtLocationIs( parser, "", 0) == true); REQUIRE(CheckNoNodeAtLocation(parser, "", 1) == true); } SECTION("Test 2") { REQUIRE(CheckNoNodeAtLocation(parser, " ", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, " ", 1) == true); REQUIRE(CheckNoNodeAtLocation(parser, " ", 2) == true); } } SECTION("Valid text") { SECTION("Test 1") { REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello world\"", 0) == true); } SECTION("Test 2") { REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello world\"", 1) == true); } SECTION("Test 3") { REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello world\"", 12) == true); } SECTION("Test 4") { REQUIRE(CheckNoNodeAtLocation(parser, "\"Hello world\"", 13) == true); } SECTION("Test 5") { REQUIRE(CheckNoNodeAtLocation(parser, "\"Hello world\"", 99) == true); } } SECTION("Valid text operators") { SECTION("Test 1") { REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello \" + \"World\"", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello \" + \"World\"", 8) == true); REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello \" + \"World\"", 15) == true); } } SECTION("Invalid texts") { REQUIRE(CheckNodeAtLocationIs(parser, "\"", 0) == true); REQUIRE(CheckNoNodeAtLocation(parser, "\"", 1) == true); REQUIRE(CheckNodeAtLocationIs(parser, "\"a", 1) == true); } SECTION("Invalid parenthesis") { REQUIRE(CheckNodeAtLocationIs( parser, "((\"hello\"", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "((\"hello\"", 2) == true); } SECTION("Invalid text operators") { SECTION("Test 1") { REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello \" - \"World\"", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello \" - \"World\"", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello \" / \"World\"", 8) == true); REQUIRE(CheckNodeAtLocationIs( parser, "\"Hello \" * \"World\"", 15) == true); } } SECTION("Valid unary operators") { SECTION("Test 1") { REQUIRE(CheckNodeAtLocationIs( parser, "-123", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "+123", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "-123", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "-123", 2) == true); REQUIRE(CheckNodeAtLocationIs( parser, "-123", 3) == true); REQUIRE(CheckNoNodeAtLocation(parser, "-123", 4) == true); } SECTION("Test 2") { REQUIRE(CheckNodeAtLocationIs( parser, "-+-123", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "-+-123", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "-+-123", 2) == true); REQUIRE(CheckNodeAtLocationIs( parser, "-+-123", 3) == true); } } SECTION("Invalid number operators") { SECTION("Test 1") { REQUIRE(CheckNodeAtLocationIs( parser, "12 ! 34", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 ! 34", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 ! 34", 2) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 ! 34", 3) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 ! 34", 4) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 ! 34", 5) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 ! 34", 6) == true); } SECTION("Test 2") { REQUIRE(CheckNodeAtLocationIs( parser, "1 / /2", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "1 / /2", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "1 / /2", 2) == true); REQUIRE(CheckNodeAtLocationIs( parser, "1 / /2", 3) == true); REQUIRE(CheckNodeAtLocationIs( parser, "1 / /2", 4) == true); REQUIRE(CheckNodeAtLocationIs( parser, "1 / /2", 5) == true); } } SECTION("Numbers and texts mismatchs") { REQUIRE(CheckNodeAtLocationIs(parser, "12+\"hello\"", 0) == true); REQUIRE(CheckNodeAtLocationIs(parser, "12+\"hello\"", 1) == true); REQUIRE(CheckNodeAtLocationIs(parser, "12+\"hello\"", 2) == true); REQUIRE(CheckNodeAtLocationIs(parser, "12+\"hello\"", 3) == true); } SECTION("Numbers and texts mismatchs (parent node)") { REQUIRE(CheckParentNodeAtLocationIs(parser, "12+\"hello\"", 0) == true); REQUIRE(CheckParentNodeAtLocationIs(parser, "12+\"hello\"", 1) == true); REQUIRE(CheckNoParentNodeAtLocation(parser, "12+\"hello\"", 2) == true); REQUIRE(CheckParentNodeAtLocationIs(parser, "12+\"hello\"", 3) == true); } SECTION("Valid objects") { REQUIRE(CheckNodeAtLocationIs( parser, "HelloWorld1", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "HelloWorld1", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "HelloWorld1", 10) == true); REQUIRE(CheckNoNodeAtLocation(parser, "HelloWorld1", 11) == true); } SECTION("Valid objects (parent node)") { REQUIRE(CheckNoParentNodeAtLocation( parser, "HelloWorld1", 0) == true); } SECTION("Invalid objects") { REQUIRE(CheckNodeAtLocationIs( parser, "a+b", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "a+b", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "a+b", 2) == true); REQUIRE(CheckNoNodeAtLocation(parser, "a+b", 3) == true); } SECTION("Valid function calls") { SECTION("Test 1") { REQUIRE(CheckNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 2) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 3) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 4) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 5) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 27) == true); REQUIRE(CheckNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 28) == true); REQUIRE(CheckNoNodeAtLocation( parser, "12 + MyExtension::GetNumber()", 29) == true); } SECTION("Test 2") { REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 33) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 34) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 35) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 36) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 37) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 38) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 39) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 50) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 51) == true); REQUIRE(CheckNoNodeAtLocation( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 52) == true); } SECTION("Parent node") { REQUIRE(CheckParentNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 0) == true); REQUIRE(CheckParentNodeAtLocationIs( parser, "12 + MyExtension::GetNumber()", 6) == true); REQUIRE(CheckNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 35) == true); REQUIRE(CheckParentNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 35) == true); REQUIRE(CheckParentNodeAtLocationIs( parser, "MyExtension::GetNumberWith2Params(12, \"hello world\")", 39) == true); } } SECTION("Invalid function calls") { REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(12)", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(12)", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(12)", 2) == true); REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(12)", 10) == true); REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(12)", 11) == true); REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(12)", 12) == true); REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(12)", 13) == true); REQUIRE(CheckNoNodeAtLocation(parser, "Idontexist(12)", 14) == true); } SECTION("Invalid function calls (parent node)") { REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(12)", 12) == true); REQUIRE(CheckParentNodeAtLocationIs( parser, "Idontexist(12)", 12) == true); } SECTION("Unterminated function calls") { REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(", 1) == true); REQUIRE(CheckNodeAtLocationIs( parser, "Idontexist(", 10) == true); REQUIRE(CheckNoNodeAtLocation(parser, "Idontexist(", 11) == true); } SECTION("Valid variables") { SECTION("Test 1") { REQUIRE(CheckNodeAtLocationIs( parser, "myVariable", 0) == true); REQUIRE(CheckNodeAtLocationIs( parser, "myVariable", 9) == true); REQUIRE(CheckNoNodeAtLocation(parser, "myVariable", 10) == true); } SECTION("Test 2") { auto node = parser.ParseExpression("Var1.Child1"); REQUIRE(node != nullptr); auto var1Node = gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 0); REQUIRE(dynamic_cast(var1Node) != nullptr); REQUIRE(dynamic_cast(*var1Node).identifierName == "Var1"); // It's actually the same node. auto child1Node = gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 4); REQUIRE(dynamic_cast(child1Node) != nullptr); REQUIRE(dynamic_cast(*child1Node).childIdentifierName == "Child1"); } SECTION("Test 3") { auto node = parser.ParseExpression( "myVariable[ \"My named children\" ].grandChild"); REQUIRE(node != nullptr); auto myVariableNode = gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 0); REQUIRE(dynamic_cast(myVariableNode) != nullptr); REQUIRE(dynamic_cast(*myVariableNode).name == "myVariable"); auto variableBracketAccessorNode = gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 10); REQUIRE(dynamic_cast( variableBracketAccessorNode) != nullptr); auto textNode = gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 15); REQUIRE(dynamic_cast(textNode) != nullptr); auto grandChildNode = gd::ExpressionNodeLocationFinder::GetNodeAtPosition(*node, 40); REQUIRE(dynamic_cast(grandChildNode) != nullptr); REQUIRE(dynamic_cast(*grandChildNode).name == "grandChild"); } } }