Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port GDScript test/debugging tools #41355

Merged
merged 2 commits into from
Sep 2, 2020
Merged

Port GDScript test/debugging tools #41355

merged 2 commits into from
Sep 2, 2020

Conversation

Xrayez
Copy link
Contributor

@Xrayez Xrayez commented Aug 18, 2020

Helps #41338, @vnen:

Also includes a disassembler for the current VM which I used to debug the code generation. It can't be accessed currently since the command for the old tests isn't available anymore, but this is useful to have for when we have a new way to trigger those.

You can now run those debug/test tools via command-line with this PR:

godot --test gdscript-tokenizer /path/to/test.gd
godot --test gdscript-parser /path/to/test.gd
godot --test gdscript-compiler /path/to/test.gd

You have to compile the engine with scons tests=yes in order to be able to use this (if you use dev=yes, you're already compiling the engine with tests).

Implementation details

I haven't modified the inner workings of those existing tools, except for the removed MainLoop since it doesn't require it for testing. This should work now as core functionality is initialized with #40980. It's also much faster to iterate because most non-core subsystems are not initialized (rendering, audio etc.), so it kind of works like --no-window option on Windows, visually.

doctest tests are skipped entirely if any such test tool is detected. I'm also reusing the dynamic initialization technique as seen in doctest, with a similar approach.

Example output so you can see it actually works:

PS D:\src\godot\4> .\bin\godot.windows.tools.64.exe --test gdscript-tokenizer D:/src/godot/4/test.gd
0001 extends Node
     ^^^^^^^
 --> extends
-------------------------------------------------------
0001 extends Node
             ^^^^
 --> Identifier(StringName) Node
-------------------------------------------------------
0001 extends Node
                 ^
 --> Newline
-------------------------------------------------------
0003 func _ready():
     ^^^^
 --> func

...and so on...

GDScript compiler will be provided by #41338. I moved those tools to modules/gdscript/tests so will need rebase in either case.

Comment on lines -9 to -11
# Enable test framework and inform it of configuration method.
env_tests.Append(CPPDEFINES=["DOCTEST_CONFIG_IMPLEMENT"])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this and define DOCTEST_CONFIG_IMPLEMENT in test_macros.cpp once. Including doctest header twice in source files doesn't protect the implementation to be included once, and leads to linker errors with duplicate symbols.

for (int i = 0; i < argc; i++) {
args.push_back(String::utf8(argv[i]));
}
OS::get_singleton()->set_cmdline("", args);
Copy link
Contributor Author

@Xrayez Xrayez Aug 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first argument is empty because there's no way to know the executable name with the existing testing interface, but I'm not sure whether that's essential in the first place.

@Calinou Calinou added this to the 4.0 milestone Aug 18, 2020
@akien-mga
Copy link
Member

Needs a rebase, then it can be merged.

@@ -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"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? It should already be inherited from the main env which was cloned for env_gdscript no?

Copy link
Contributor Author

@Xrayez Xrayez Sep 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because we're actually outside of the main/ env here. Recall #40726, this is what I meant when saying that some modules may eventually need this. Defining TESTS_ENABLED for all modules is also an option, but GDScript may be just an exception.

Technically, those are more like debug tools the way I see it, but they still qualify as something which facilitate manual testing process.

Comment on lines 31 to 34
#include "test_macros.h"

#define DOCTEST_CONFIG_IMPLEMENT
#include "thirdparty/doctest/doctest.h"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this include doctest.h first without the define (via test_macros.h), and then a second time with the define?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, removed doctest header.

@Xrayez
Copy link
Contributor Author

Xrayez commented Sep 1, 2020

Applied suggested changes and rebased.

I haven't reported this before (to test out compiler after rebase as well), but I've revealed some crashes by testing out a simple script, they all happen in GDScript code, so I'm not sure whether they are caused by running commands in the test environment or whether those crashes simply need a fix (CC @vnen):

# test.gd (at Godot source root)

extends Node

func _ready():
    pass

with the following commands:

--test gdscript-tokenizer test.gd

I suspect this may have something to do with off-by-one error.

godot.windows.tools.64.exe!CowData<String>::get(int p_index) Line 151 (d:\src\godot\4\core\cowdata.h:151)
godot.windows.tools.64.exe!Vector<String>::operator[](int p_index) Line 89 (d:\src\godot\4\core\vector.h:89)
godot.windows.tools.64.exe!TestGDScript::test_tokenizer(const String & p_code, const Vector<String> & p_lines) Line 67 (d:\src\godot\4\modules\gdscript\tests\test_gdscript.cpp:67)
godot.windows.tools.64.exe!TestGDScript::test(TestGDScript::TestType p_type) Line 219 (d:\src\godot\4\modules\gdscript\tests\test_gdscript.cpp:219)
godot.windows.tools.64.exe!test_tokenizer() Line 165 (d:\src\godot\4\modules\gdscript\register_types.cpp:165)
godot.windows.tools.64.exe!test_main(int argc, char * * argv) Line 74 (d:\src\godot\4\tests\test_main.cpp:74)
godot.windows.tools.64.exe!Main::test_entrypoint(int argc, char * * argv, bool & tests_need_run) Line 458 (d:\src\godot\4\main\main.cpp:458)
godot.windows.tools.64.exe!widechar_main(int argc, wchar_t * * argv) Line 149 (d:\src\godot\4\platform\windows\godot_windows.cpp:149)
godot.windows.tools.64.exe!_main() Line 185 (d:\src\godot\4\platform\windows\godot_windows.cpp:185)
godot.windows.tools.64.exe!main(int argc, char * * argv) Line 199 (d:\src\godot\4\platform\windows\godot_windows.cpp:199)
godot.windows.tools.64.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 213 (d:\src\godot\4\platform\windows\godot_windows.cpp:213)
[Inline Frame] godot.windows.tools.64.exe!invoke_main() Line 102 (d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:102)
godot.windows.tools.64.exe!__scrt_common_main_seh() Line 288 (d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
kernel32.dll!00007ff80cd87bd4() (Unknown Source:0)
ntdll.dll!00007ff80e72ce51() (Unknown Source:0)

--test gdscript-parser test.gd

Works fine. 🙂

--test gdscript-compiler test.gd

Uninitialized map data root. If you add the following snippet, it would just prevent the crash there:

			if (GDScriptLanguage::get_singleton()->get_global_map().empty()) {
				break;
			}
godot.windows.tools.64.exe!Map<StringName,int,Comparator<StringName>,DefaultAllocator>::operator[](const StringName & p_key) Line 575 (d:\src\godot\4\core\map.h:575)
godot.windows.tools.64.exe!GDScriptCompiler::_parse_class_level(GDScript * p_script, const GDScriptParser::ClassNode * p_class, bool p_keep_state) Line 1917 (d:\src\godot\4\modules\gdscript\gdscript_compiler.cpp:1917)
godot.windows.tools.64.exe!GDScriptCompiler::compile(const GDScriptParser * p_parser, GDScript * p_script, bool p_keep_state) Line 2305 (d:\src\godot\4\modules\gdscript\gdscript_compiler.cpp:2305)
godot.windows.tools.64.exe!TestGDScript::test_compiler(const String & p_code, const String & p_script_path, const Vector<String> & p_lines) Line 156 (d:\src\godot\4\modules\gdscript\tests\test_gdscript.cpp:156)
godot.windows.tools.64.exe!TestGDScript::test(TestGDScript::TestType p_type) Line 225 (d:\src\godot\4\modules\gdscript\tests\test_gdscript.cpp:225)
godot.windows.tools.64.exe!test_compiler() Line 173 (d:\src\godot\4\modules\gdscript\register_types.cpp:173)
godot.windows.tools.64.exe!test_main(int argc, char * * argv) Line 74 (d:\src\godot\4\tests\test_main.cpp:74)
godot.windows.tools.64.exe!Main::test_entrypoint(int argc, char * * argv, bool & tests_need_run) Line 458 (d:\src\godot\4\main\main.cpp:458)
godot.windows.tools.64.exe!widechar_main(int argc, wchar_t * * argv) Line 149 (d:\src\godot\4\platform\windows\godot_windows.cpp:149)
godot.windows.tools.64.exe!_main() Line 185 (d:\src\godot\4\platform\windows\godot_windows.cpp:185)
godot.windows.tools.64.exe!main(int argc, char * * argv) Line 199 (d:\src\godot\4\platform\windows\godot_windows.cpp:199)
godot.windows.tools.64.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 213 (d:\src\godot\4\platform\windows\godot_windows.cpp:213)
[Inline Frame] godot.windows.tools.64.exe!invoke_main() Line 102 (d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:102)
godot.windows.tools.64.exe!__scrt_common_main_seh() Line 288 (d:\agent\_work\4\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
kernel32.dll!00007ff80cd87bd4() (Unknown Source:0)
ntdll.dll!00007ff80e72ce51() (Unknown Source:0)

Otherwise my changes are complete.

@akien-mga
Copy link
Member

Let's merge as is and then work on fixing the identified issues, which don't seem to be introduced by this PR. Could you open issues for them?

@akien-mga akien-mga merged commit 3922883 into godotengine:master Sep 2, 2020
@akien-mga
Copy link
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants