Completion Tests: Add script to owner

This commit is contained in:
HolonProduction 2024-03-25 16:11:44 +01:00
parent fe01776f05
commit d2c2194937
39 changed files with 100 additions and 11 deletions

View file

@ -13,6 +13,12 @@ The `script/completion` folder contains test for the GDScript autocompletion.
Each test case consists of at least one `.gd` file, which contains the code, and one `.cfg` file, which contains expected results and configuration. Inside of the GDScript file the character `➡` represents the cursor position, at which autocompletion is invoked.
The script files won't be parsable GDScript since it contains an invalid char and and often the code is not complete during autocompletion. To allow for a valid base when used with a scene, the
runner will remove the line which contains `➡`. Therefore the scripts need to be valid if this line is removed, otherwise the test might behave in unexpected ways. This may for example require
adding an additional `pass` statement.
This also means, that the runner will add the script to its owner node, so the script should not be loaded through the scene file.
The config file contains two section:
`[input]` contains keys that configure the test environment. The following keys are possible:
@ -20,6 +26,7 @@ The config file contains two section:
- `cs: boolean = false`: If `true`, the test will be skipped when running a non C# build.
- `use_single_quotes: boolean = false`: Configures the corresponding editor setting for the test.
- `scene: String`: Allows to specify a scene which is opened while autocompletion is performed. If this is not set the test runner will search for a `.tscn` file with the same basename as the GDScript file. If that isn't found either, autocompletion will behave as if no scene was opened.
- `node_path: String`: The node path of the node which holds the current script inside of the scene. Defaults to the scene root node.
`[output]` specifies the expected results for the test. The following key are supported:

View file

@ -0,0 +1,3 @@
[gd_scene load_steps=1 format=3 uid="uid://dl28pdkxcjvym"]
[node name="GetNode" type="Node"]

View file

@ -0,0 +1,8 @@
[input]
scene="res://completion/argument_options/argument_options.tscn"
[output]
include=[
; Node
{"display": "\"signal_a\""},
{"display": "\"child_entered_tree\""},
]

View file

@ -0,0 +1,7 @@
extends Node
signal signal_a()
func _ready():
connect()
pass

View file

@ -2,3 +2,4 @@ extends Node
func a():
%AnimationPlayer.
pass

View file

@ -2,3 +2,4 @@ extends Node
func a():
$UniqueAnimationPlayer.
pass

View file

@ -2,3 +2,4 @@ extends Node
func a():
$A.
pass

View file

@ -2,3 +2,4 @@ extends Node
func a():
$AnimationPlayer.
pass

View file

@ -2,3 +2,4 @@ extends Node
func a():
%UniqueA.
pass

View file

@ -2,3 +2,4 @@ extends Node
func a():
%UniqueAnimationPlayer.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test = $AnimationPlayer
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test := $AnimationPlayer
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test := $A
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test := $AnimationPlayer
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test = $A
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test = $AnimationPlayer
test.
pass

View file

@ -5,3 +5,4 @@ const A := preload("res://completion/class_a.notest.gd")
func a():
var test: A = $A
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test: AnimationPlayer = $AnimationPlayer
test.
pass

View file

@ -5,3 +5,4 @@ const A := preload("res://completion/class_a.notest.gd")
func a():
var test: A = $A
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test: AnimationPlayer = $AnimationPlayer
test.
pass

View file

@ -4,3 +4,4 @@ extends Node
func a():
var test: Node = $A
test.
pass

View file

@ -4,3 +4,4 @@ extends Node
func a():
var test: Node = $AnimationPlayer
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test: Area2D = $A
test.
pass

View file

@ -3,3 +3,4 @@ extends Node
func a():
var test: Area2D = $AnimationPlayer
test.
pass

View file

@ -4,3 +4,4 @@ var test = $AnimationPlayer
func a():
test.
pass

View file

@ -4,3 +4,4 @@ var test := $AnimationPlayer
func a():
test.
pass

View file

@ -4,3 +4,4 @@ var test := $AnimationPlayer
func a():
test.
pass

View file

@ -4,3 +4,4 @@ var test = $A
func a():
test.
pass

View file

@ -4,3 +4,4 @@ var test = $AnimationPlayer
func a():
test.
pass

View file

@ -6,3 +6,4 @@ var test: A = $A
func a():
test.
pass

View file

@ -4,3 +4,4 @@ var test: AnimationPlayer = $AnimationPlayer
func a():
test.
pass

View file

@ -6,3 +6,4 @@ const A := preload("res://completion/class_a.notest.gd")
func a():
test.
pass

View file

@ -4,3 +4,4 @@ var test: AnimationPlayer = $AnimationPlayer
func a():
test.
pass

View file

@ -4,3 +4,4 @@ var test: Node = $AnimationPlayer
func a():
test.
pass

View file

@ -4,3 +4,4 @@ var test: Area2D = $AnimationPlayer
func a():
test.
pass

View file

@ -33,6 +33,7 @@
#ifdef TOOLS_ENABLED
#include "core/config/project_settings.h"
#include "core/io/config_file.h"
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
@ -111,7 +112,10 @@ static void test_directory(const String &p_dir) {
// For ease of reading ➡ (0x27A1) acts as sentinel char instead of 0xFFFF in the files.
code = code.replace_first(String::chr(0x27A1), String::chr(0xFFFF));
// Require pointer sentinel char in scripts.
CHECK(code.find_char(0xFFFF) != -1);
int location = code.find_char(0xFFFF);
CHECK(location != -1);
String res_path = ProjectSettings::get_singleton()->localize_path(path.path_join(next));
ConfigFile conf;
if (conf.load(path.path_join(next.get_basename() + ".cfg")) != OK) {
@ -137,20 +141,46 @@ static void test_directory(const String &p_dir) {
String call_hint;
bool forced;
Node *owner = nullptr;
Node *scene = nullptr;
if (conf.has_section_key("input", "scene")) {
Ref<PackedScene> scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP);
if (scene.is_valid()) {
owner = scene->instantiate();
Ref<PackedScene> packed_scene = ResourceLoader::load(conf.get_value("input", "scene"), "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP);
if (packed_scene.is_valid()) {
scene = packed_scene->instantiate();
}
} else if (dir->file_exists(next.get_basename() + ".tscn")) {
Ref<PackedScene> scene = ResourceLoader::load(path.path_join(next.get_basename() + ".tscn"), "PackedScene");
if (scene.is_valid()) {
owner = scene->instantiate();
Ref<PackedScene> packed_scene = ResourceLoader::load(path.path_join(next.get_basename() + ".tscn"), "PackedScene");
if (packed_scene.is_valid()) {
scene = packed_scene->instantiate();
}
}
Node *owner = nullptr;
if (scene != nullptr) {
owner = scene->get_node(conf.get_value("input", "node_path", "."));
}
GDScriptLanguage::get_singleton()->complete_code(code, path.path_join(next), owner, &options, forced, call_hint);
if (owner != nullptr) {
// Remove the line which contains the sentinel char, to get a valid script.
Ref<GDScript> scr;
scr.instantiate();
int start = location;
int end = location;
for (; start >= 0; --start) {
if (code.get(start) == '\n') {
break;
}
}
for (; end < code.size(); ++end) {
if (code.get(end) == '\n') {
break;
}
}
scr->set_source_code(code.erase(start, end - start));
scr->reload();
scr->set_path(res_path);
owner->set_script(scr);
}
GDScriptLanguage::get_singleton()->complete_code(code, res_path, owner, &options, forced, call_hint);
String contains_excluded;
for (ScriptLanguage::CodeCompletionOption &option : options) {
for (const Dictionary &E : exclude) {
@ -179,8 +209,8 @@ static void test_directory(const String &p_dir) {
CHECK(expected_call_hint == call_hint);
CHECK(expected_forced == forced);
if (owner) {
memdelete(owner);
if (scene) {
memdelete(scene);
}
}
next = dir->get_next();