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 ++++++ tests/test_gdscript.cpp | 250 ------------------------------- tests/test_gdscript.h | 48 ------ 4 files changed, 298 insertions(+), 298 deletions(-) create mode 100644 modules/gdscript/tests/test_gdscript.cpp create mode 100644 modules/gdscript/tests/test_gdscript.h delete mode 100644 tests/test_gdscript.cpp delete mode 100644 tests/test_gdscript.h 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 diff --git a/tests/test_gdscript.cpp b/tests/test_gdscript.cpp deleted file mode 100644 index a909f216ee..0000000000 --- a/tests/test_gdscript.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/*************************************************************************/ -/* 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/tests/test_gdscript.h b/tests/test_gdscript.h deleted file mode 100644 index 6595da1430..0000000000 --- a/tests/test_gdscript.h +++ /dev/null @@ -1,48 +0,0 @@ -/*************************************************************************/ -/* 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 --- core/os/os.h | 4 ++- modules/gdscript/SCsub | 4 +++ modules/gdscript/register_types.cpp | 28 ++++++++++++++++++ modules/gdscript/tests/test_gdscript.cpp | 27 +++-------------- modules/gdscript/tests/test_gdscript.h | 5 ++-- tests/SCsub | 3 -- tests/test_macros.cpp | 42 +++++++++++++++++++++++++++ tests/test_macros.h | 16 ++++++++++ tests/test_main.cpp | 50 +++++++++++++++++++++++--------- 9 files changed, 135 insertions(+), 44 deletions(-) create mode 100644 tests/test_macros.cpp diff --git a/core/os/os.h b/core/os/os.h index 48dae99188..8e5c0c26e0 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -86,11 +86,13 @@ public: protected: friend class Main; + // Needed by tests to setup command-line args. + friend int test_main(int argc, char *argv[]); HasServerFeatureCallback has_server_feature_callback = nullptr; RenderThreadMode _render_thread_mode = RENDER_THREAD_SAFE; - // functions used by main to initialize/deinitialize the OS + // Functions used by Main to initialize/deinitialize the OS. void add_logger(Logger *p_logger); virtual void initialize() = 0; 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 diff --git a/tests/SCsub b/tests/SCsub index 84c9fc1ffe..a2864d805b 100644 --- a/tests/SCsub +++ b/tests/SCsub @@ -6,9 +6,6 @@ env.tests_sources = [] env_tests = env.Clone() -# Enable test framework and inform it of configuration method. -env_tests.Append(CPPDEFINES=["DOCTEST_CONFIG_IMPLEMENT"]) - # We must disable the THREAD_LOCAL entirely in doctest to prevent crashes on debugging # Since we link with /MT thread_local is always expired when the header is used # So the debugger crashes the engine and it causes weird errors diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp new file mode 100644 index 0000000000..2317223b23 --- /dev/null +++ b/tests/test_macros.cpp @@ -0,0 +1,42 @@ +/*************************************************************************/ +/* test_macros.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. */ +/*************************************************************************/ + +#define DOCTEST_CONFIG_IMPLEMENT +#include "test_macros.h" + +Map *test_commands = nullptr; + +int register_test_command(String p_command, TestFunc p_function) { + if (!test_commands) { + test_commands = new Map; + } + test_commands->insert(p_command, p_function); + return 0; +} diff --git a/tests/test_macros.h b/tests/test_macros.h index 45ba8581dd..3486c68bb7 100644 --- a/tests/test_macros.h +++ b/tests/test_macros.h @@ -31,6 +31,9 @@ #ifndef TEST_MACROS_H #define TEST_MACROS_H +#include "core/map.h" +#include "core/variant.h" + // See documentation for doctest at: // https://github.com/onqtam/doctest/blob/master/doc/markdown/readme.md#reference #include "thirdparty/doctest/doctest.h" @@ -104,4 +107,17 @@ DOCTEST_STRINGIFY_VARIANT(PackedVector2Array); DOCTEST_STRINGIFY_VARIANT(PackedVector3Array); DOCTEST_STRINGIFY_VARIANT(PackedColorArray); +// Register test commands to be launched from the command-line. +// For instance: REGISTER_TEST_COMMAND("gdscript-parser" &test_parser_func). +// Example usage: `godot --test gdscript-parser`. + +typedef void (*TestFunc)(); +extern Map *test_commands; +int register_test_command(String p_command, TestFunc p_function); + +#define REGISTER_TEST_COMMAND(m_command, m_function) \ + DOCTEST_GLOBAL_NO_WARNINGS(DOCTEST_ANONYMOUS(_DOCTEST_ANON_VAR_)) = \ + register_test_command(m_command, m_function); \ + DOCTEST_GLOBAL_NO_WARNINGS_END() + #endif // TEST_MACROS_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 43c1ef331f..1c9535174b 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -37,7 +37,6 @@ #include "test_class_db.h" #include "test_color.h" #include "test_expression.h" -#include "test_gdscript.h" #include "test_gradient.h" #include "test_gui.h" #include "test_math.h" @@ -56,40 +55,63 @@ #include "tests/test_macros.h" int test_main(int argc, char *argv[]) { + bool run_tests = true; + + // Convert arguments to Godot's command-line. + List args; + + for (int i = 0; i < argc; i++) { + args.push_back(String::utf8(argv[i])); + } + OS::get_singleton()->set_cmdline("", args); + + // Run custom test tools. + if (test_commands) { + for (Map::Element *E = test_commands->front(); E; E = E->next()) { + if (args.find(E->key())) { + const TestFunc &test_func = E->get(); + test_func(); + run_tests = false; + break; + } + } + if (!run_tests) { + delete test_commands; + return 0; + } + } // Doctest runner. doctest::Context test_context; - List valid_arguments; + List test_args; // Clean arguments of "--test" from the args. - int argument_count = 0; for (int x = 0; x < argc; x++) { if (strncmp(argv[x], "--test", 6) != 0) { - valid_arguments.push_back(String(argv[x])); - argument_count++; + test_args.push_back(String(argv[x])); } } // Convert Godot command line arguments back to standard arguments. - char **args = new char *[valid_arguments.size()]; - for (int x = 0; x < valid_arguments.size(); x++) { + char **doctest_args = new char *[test_args.size()]; + for (int x = 0; x < test_args.size(); x++) { // Operation to convert Godot string to non wchar string. - CharString cs = valid_arguments[x].utf8(); + CharString cs = test_args[x].utf8(); const char *str = cs.get_data(); // Allocate the string copy. - args[x] = new char[strlen(str) + 1]; + doctest_args[x] = new char[strlen(str) + 1]; // Copy this into memory. - std::memcpy(args[x], str, strlen(str) + 1); + memcpy(doctest_args[x], str, strlen(str) + 1); } - test_context.applyCommandLine(valid_arguments.size(), args); + test_context.applyCommandLine(test_args.size(), doctest_args); test_context.setOption("order-by", "name"); test_context.setOption("abort-after", 5); test_context.setOption("no-breaks", true); - for (int x = 0; x < valid_arguments.size(); x++) { - delete[] args[x]; + for (int x = 0; x < test_args.size(); x++) { + delete[] doctest_args[x]; } - delete[] args; + delete[] doctest_args; return test_context.run(); } -- cgit v1.2.3