From 6b7e50ab3410542d341bd1dd1ec70123fc218ecc Mon Sep 17 00:00:00 2001 From: "Andrii Doroshenko (Xrayez)" Date: Tue, 18 Aug 2020 15:29:22 +0300 Subject: Move GDScript tests to respective folder under modules --- modules/gdscript/tests/test_gdscript.cpp | 250 +++++++++++++++++++++++++++++++ modules/gdscript/tests/test_gdscript.h | 48 ++++++ 2 files changed, 298 insertions(+) create mode 100644 modules/gdscript/tests/test_gdscript.cpp create mode 100644 modules/gdscript/tests/test_gdscript.h (limited to 'modules/gdscript') diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp new file mode 100644 index 0000000000..a909f216ee --- /dev/null +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -0,0 +1,250 @@ +/*************************************************************************/ +/* test_gdscript.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "test_gdscript.h" + +#include "core/os/file_access.h" +#include "core/os/main_loop.h" +#include "core/os/os.h" +#include "core/string_builder.h" + +#include "modules/modules_enabled.gen.h" + +#ifdef MODULE_GDSCRIPT_ENABLED + +#include "modules/gdscript/gdscript_analyzer.h" +#include "modules/gdscript/gdscript_compiler.h" +#include "modules/gdscript/gdscript_parser.h" +#include "modules/gdscript/gdscript_tokenizer.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif + +namespace TestGDScript { + +static void test_tokenizer(const String &p_code, const Vector &p_lines) { + GDScriptTokenizer tokenizer; + tokenizer.set_source_code(p_code); + + int tab_size = 4; +#ifdef TOOLS_ENABLED + if (EditorSettings::get_singleton()) { + tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size"); + } +#endif // TOOLS_ENABLED + String tab = String(" ").repeat(tab_size); + + GDScriptTokenizer::Token current = tokenizer.scan(); + while (current.type != GDScriptTokenizer::Token::TK_EOF) { + StringBuilder token; + token += " --> "; // Padding for line number. + + for (int l = current.start_line; l <= current.end_line; l++) { + print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab)); + } + + { + // Print carets to point at the token. + StringBuilder pointer; + pointer += " "; // Padding for line number. + int rightmost_column = current.rightmost_column; + if (current.end_line > current.start_line) { + rightmost_column--; // Don't point to the newline as a column. + } + for (int col = 1; col < rightmost_column; col++) { + if (col < current.leftmost_column) { + pointer += " "; + } else { + pointer += "^"; + } + } + print_line(pointer.as_string()); + } + + token += current.get_name(); + + if (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::LITERAL || current.type == GDScriptTokenizer::Token::IDENTIFIER || current.type == GDScriptTokenizer::Token::ANNOTATION) { + token += "("; + token += Variant::get_type_name(current.literal.get_type()); + token += ") "; + token += current.literal; + } + + print_line(token.as_string()); + + print_line("-------------------------------------------------------"); + + current = tokenizer.scan(); + } + + print_line(current.get_name()); // Should be EOF +} + +static void test_parser(const String &p_code, const String &p_script_path, const Vector &p_lines) { + GDScriptParser parser; + Error err = parser.parse(p_code, p_script_path, false); + + if (err != OK) { + const List &errors = parser.get_errors(); + for (const List::Element *E = errors.front(); E != nullptr; E = E->next()) { + const GDScriptParser::ParserError &error = E->get(); + print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message)); + } + } + + GDScriptParser::TreePrinter printer; + + printer.print_tree(parser); +} + +static void test_compiler(const String &p_code, const String &p_script_path, const Vector &p_lines) { + GDScriptParser parser; + Error err = parser.parse(p_code, p_script_path, false); + + if (err != OK) { + print_line("Error in parser:"); + const List &errors = parser.get_errors(); + for (const List::Element *E = errors.front(); E != nullptr; E = E->next()) { + const GDScriptParser::ParserError &error = E->get(); + print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message)); + } + return; + } + + GDScriptAnalyzer analyzer(&parser); + err = analyzer.analyze(); + + if (err != OK) { + print_line("Error in analyzer:"); + const List &errors = parser.get_errors(); + for (const List::Element *E = errors.front(); E != nullptr; E = E->next()) { + const GDScriptParser::ParserError &error = E->get(); + print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message)); + } + return; + } + + GDScriptCompiler compiler; + Ref script; + script.instance(); + script->set_path(p_script_path); + + err = compiler.compile(&parser, script.ptr(), false); + + if (err) { + print_line("Error in compiler:"); + print_line(vformat("%02d:%02d: %s", compiler.get_error_line(), compiler.get_error_column(), compiler.get_error())); + return; + } + + for (const Map::Element *E = script->get_member_functions().front(); E; E = E->next()) { + const GDScriptFunction *func = E->value(); + + String signature = "Disassembling " + func->get_name().operator String() + "("; + for (int i = 0; i < func->get_argument_count(); i++) { + if (i > 0) { + signature += ", "; + } + signature += func->get_argument_name(i); + } + print_line(signature + ")"); + + func->disassemble(p_lines); + print_line(""); + print_line(""); + } +} + +MainLoop *test(TestType p_type) { + List cmdlargs = OS::get_singleton()->get_cmdline_args(); + + if (cmdlargs.empty()) { + return nullptr; + } + + String test = cmdlargs.back()->get(); + if (!test.ends_with(".gd")) { + print_line("This test expects a path to a GDScript file as its last parameter. Got: " + test); + return nullptr; + } + + FileAccessRef fa = FileAccess::open(test, FileAccess::READ); + ERR_FAIL_COND_V_MSG(!fa, nullptr, "Could not open file: " + test); + + Vector buf; + int flen = fa->get_len(); + buf.resize(fa->get_len() + 1); + fa->get_buffer(buf.ptrw(), flen); + buf.write[flen] = 0; + + String code; + code.parse_utf8((const char *)&buf[0]); + + Vector lines; + int last = 0; + for (int i = 0; i <= code.length(); i++) { + if (code[i] == '\n' || code[i] == 0) { + lines.push_back(code.substr(last, i - last)); + last = i + 1; + } + } + + switch (p_type) { + case TEST_TOKENIZER: + test_tokenizer(code, lines); + break; + case TEST_PARSER: + test_parser(code, test, lines); + break; + case TEST_COMPILER: + test_compiler(code, test, lines); + break; + case TEST_BYTECODE: + print_line("Not implemented."); + } + + return nullptr; +} + +} // namespace TestGDScript + +#else + +namespace TestGDScript { + +MainLoop *test(TestType p_type) { + ERR_PRINT("The GDScript module is disabled, therefore GDScript tests cannot be used."); + return nullptr; +} + +} // namespace TestGDScript + +#endif diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h new file mode 100644 index 0000000000..6595da1430 --- /dev/null +++ b/modules/gdscript/tests/test_gdscript.h @@ -0,0 +1,48 @@ +/*************************************************************************/ +/* test_gdscript.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef TEST_GDSCRIPT_H +#define TEST_GDSCRIPT_H + +#include "core/os/main_loop.h" + +namespace TestGDScript { + +enum TestType { + TEST_TOKENIZER, + TEST_PARSER, + TEST_COMPILER, + TEST_BYTECODE, +}; + +MainLoop *test(TestType p_type); +} // namespace TestGDScript + +#endif // TEST_GDSCRIPT_H -- cgit v1.2.3 From 0cc05c5a31b6c6d3db76b5982f3f36919e137500 Mon Sep 17 00:00:00 2001 From: "Andrii Doroshenko (Xrayez)" Date: Tue, 18 Aug 2020 17:24:45 +0300 Subject: Register GDScript test tools as test commands to run via command-line --- modules/gdscript/SCsub | 4 ++++ modules/gdscript/register_types.cpp | 28 ++++++++++++++++++++++++++++ modules/gdscript/tests/test_gdscript.cpp | 27 ++++----------------------- modules/gdscript/tests/test_gdscript.h | 5 ++--- 4 files changed, 38 insertions(+), 26 deletions(-) (limited to 'modules/gdscript') diff --git a/modules/gdscript/SCsub b/modules/gdscript/SCsub index e58a1d8edc..5c8cbdf869 100644 --- a/modules/gdscript/SCsub +++ b/modules/gdscript/SCsub @@ -17,3 +17,7 @@ if env["tools"]: # Using a define in the disabled case, to avoid having an extra define # in regular builds where all modules are enabled. env_gdscript.Append(CPPDEFINES=["GDSCRIPT_NO_LSP"]) + +if env["tests"]: + env_gdscript.Append(CPPDEFINES=["TESTS_ENABLED"]) + env_gdscript.add_source_files(env.modules_sources, "./tests/*.cpp") diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index c554cbac05..7dad878eb1 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -39,6 +39,11 @@ #include "gdscript_cache.h" #include "gdscript_tokenizer.h" +#ifdef TESTS_ENABLED +#include "tests/test_gdscript.h" +#include "tests/test_macros.h" +#endif + GDScriptLanguage *script_language_gd = nullptr; Ref resource_loader_gd; Ref resource_saver_gd; @@ -153,3 +158,26 @@ void unregister_gdscript_types() { GDScriptParser::cleanup(); GDScriptAnalyzer::cleanup(); } + +#ifdef TESTS_ENABLED +void test_tokenizer() { + TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER); +} + +void test_parser() { + TestGDScript::test(TestGDScript::TestType::TEST_PARSER); +} + +void test_compiler() { + TestGDScript::test(TestGDScript::TestType::TEST_COMPILER); +} + +void test_bytecode() { + TestGDScript::test(TestGDScript::TestType::TEST_BYTECODE); +} + +REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer); +REGISTER_TEST_COMMAND("gdscript-parser", &test_parser); +REGISTER_TEST_COMMAND("gdscript-compiler", &test_compiler); +REGISTER_TEST_COMMAND("gdscript-bytecode", &test_bytecode); +#endif diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index a909f216ee..68d9984b43 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -35,10 +35,6 @@ #include "core/os/os.h" #include "core/string_builder.h" -#include "modules/modules_enabled.gen.h" - -#ifdef MODULE_GDSCRIPT_ENABLED - #include "modules/gdscript/gdscript_analyzer.h" #include "modules/gdscript/gdscript_compiler.h" #include "modules/gdscript/gdscript_parser.h" @@ -183,21 +179,21 @@ static void test_compiler(const String &p_code, const String &p_script_path, con } } -MainLoop *test(TestType p_type) { +void test(TestType p_type) { List cmdlargs = OS::get_singleton()->get_cmdline_args(); if (cmdlargs.empty()) { - return nullptr; + return; } String test = cmdlargs.back()->get(); if (!test.ends_with(".gd")) { print_line("This test expects a path to a GDScript file as its last parameter. Got: " + test); - return nullptr; + return; } FileAccessRef fa = FileAccess::open(test, FileAccess::READ); - ERR_FAIL_COND_V_MSG(!fa, nullptr, "Could not open file: " + test); + ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test); Vector buf; int flen = fa->get_len(); @@ -230,21 +226,6 @@ MainLoop *test(TestType p_type) { case TEST_BYTECODE: print_line("Not implemented."); } - - return nullptr; -} - -} // namespace TestGDScript - -#else - -namespace TestGDScript { - -MainLoop *test(TestType p_type) { - ERR_PRINT("The GDScript module is disabled, therefore GDScript tests cannot be used."); - return nullptr; } } // namespace TestGDScript - -#endif diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h index 6595da1430..5aa962dcf8 100644 --- a/modules/gdscript/tests/test_gdscript.h +++ b/modules/gdscript/tests/test_gdscript.h @@ -31,8 +31,6 @@ #ifndef TEST_GDSCRIPT_H #define TEST_GDSCRIPT_H -#include "core/os/main_loop.h" - namespace TestGDScript { enum TestType { @@ -42,7 +40,8 @@ enum TestType { TEST_BYTECODE, }; -MainLoop *test(TestType p_type); +void test(TestType p_type); + } // namespace TestGDScript #endif // TEST_GDSCRIPT_H -- cgit v1.2.3