gdharness 1.3.46 → 1.3.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +186 -49
- package/build/godot/addons/gdharness_editor/animation_reads.gd +20 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd +6 -0
- package/build/godot/addons/gdharness_editor/scene_uids.gd +25 -13
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +20 -6
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +19 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +23 -4
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +9 -7
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +7 -0
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +90 -30
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +1 -1
- package/build/godot/operations/animation_reads.gd +20 -0
- package/build/godot/operations/dependencies.gd +65 -9
- package/build/godot/operations/export_presets.gd +32 -5
- package/build/godot/operations/file_reads.gd +6 -0
- package/build/godot/operations/import_pipeline.gd +10 -2
- package/build/godot/operations/project_config.gd +35 -0
- package/build/godot/operations/resource_files.gd +1 -2
- package/build/godot/tests/listed.gd +25 -0
- package/build/keeper.js +1308 -1026
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -11027,9 +11027,10 @@ function rowOf(name) {
|
|
|
11027
11027
|
function rowRuns(share, row) {
|
|
11028
11028
|
return "only" in share ? row !== null && share.only.includes(row) : row === null || !share.besides.includes(row);
|
|
11029
11029
|
}
|
|
11030
|
-
function
|
|
11030
|
+
function skippedAsDeclaredIn(printed) {
|
|
11031
11031
|
const lines = printed.flatMap((text) => stripVTControlCharacters(text).split(/\r?\n/));
|
|
11032
11032
|
const found = [];
|
|
11033
|
+
const unrunnable = [];
|
|
11033
11034
|
let suite = null;
|
|
11034
11035
|
let lastCase = null;
|
|
11035
11036
|
for (let at = 0;at < lines.length; at += 1) {
|
|
@@ -11045,7 +11046,15 @@ function mismatchedRowsIn(printed) {
|
|
|
11045
11046
|
lastCase = caseLine[3] === "STARTED" ? lastCase : caseLine[2] ?? null;
|
|
11046
11047
|
continue;
|
|
11047
11048
|
}
|
|
11048
|
-
if (suite === null || lastCase === null
|
|
11049
|
+
if (suite === null || lastCase === null) {
|
|
11050
|
+
continue;
|
|
11051
|
+
}
|
|
11052
|
+
const declared = UNRUNNABLE_CASE.exec(line)?.[1];
|
|
11053
|
+
if (declared !== undefined) {
|
|
11054
|
+
unrunnable.push({ suite, name: lastCase, reason: declared });
|
|
11055
|
+
continue;
|
|
11056
|
+
}
|
|
11057
|
+
if (!MISMATCHED_ROW.test(line)) {
|
|
11049
11058
|
continue;
|
|
11050
11059
|
}
|
|
11051
11060
|
const parts = [line.trim()];
|
|
@@ -11055,7 +11064,7 @@ function mismatchedRowsIn(printed) {
|
|
|
11055
11064
|
}
|
|
11056
11065
|
found.push({ suite, name: lastCase, reason: parts.join(" ").replaceAll(/\[\/?color[^\]]*\]/g, "") });
|
|
11057
11066
|
}
|
|
11058
|
-
return found;
|
|
11067
|
+
return { mismatched: found, unrunnable };
|
|
11059
11068
|
}
|
|
11060
11069
|
function readRun(printed) {
|
|
11061
11070
|
const lines = stripVTControlCharacters(printed).replaceAll(`\r
|
|
@@ -11189,7 +11198,7 @@ function readRun(printed) {
|
|
|
11189
11198
|
leftOut,
|
|
11190
11199
|
engineVersion,
|
|
11191
11200
|
orphansLeft,
|
|
11192
|
-
|
|
11201
|
+
...skippedAsDeclaredIn(lines)
|
|
11193
11202
|
};
|
|
11194
11203
|
}
|
|
11195
11204
|
function withNestedErrors(printed, piped) {
|
|
@@ -11314,7 +11323,7 @@ function judgeEngine(printed, handed, end) {
|
|
|
11314
11323
|
if (printed.executed === null || printed.executed === 0) {
|
|
11315
11324
|
faults.push(printed.executed === null ? "the run never printed its summary" : "the run executed no test case at all, whatever it exited with");
|
|
11316
11325
|
}
|
|
11317
|
-
for (const row of printed.mismatched) {
|
|
11326
|
+
for (const row of [...printed.mismatched, ...printed.unrunnable]) {
|
|
11318
11327
|
faults.push(`${row.suite} > ${row.name} never ran: gdUnit4 skipped it, saying "${row.reason}"`);
|
|
11319
11328
|
failing.add(row.suite);
|
|
11320
11329
|
}
|
|
@@ -11481,7 +11490,7 @@ ${String(error.warning)}`);
|
|
|
11481
11490
|
const neverPrinted = [...owed.values()].flatMap((debt) => debt.texts.map((text) => ({ suite: debt.suite, during: debt.during, text, warning: debt.warning })));
|
|
11482
11491
|
return { printed: unexpected, neverPrinted };
|
|
11483
11492
|
}
|
|
11484
|
-
var SUITE_OPENED, TIME, CASE_LINE, STATISTICS, ERROR_LINE, WARNING_LINE, EXECUTED, MISMATCHED_ROW, BANNER, LEFT_OUT, DRIVER_FALLBACK, MACHINE_WARNED, ORPHANS_REPORTED, STARVED, NESTED_ERROR = "While attempting to print an error, another error was printed:", WINDOWS_EXITS, MEMORY_EXITS, GDUNIT_FAILED = 100, GDUNIT_ORPHANS = 101, GDUNIT_EXITS, GODOT_EXITS, TAIL = 60, MOST_NAMED = 8;
|
|
11493
|
+
var SUITE_OPENED, TIME, CASE_LINE, STATISTICS, ERROR_LINE, WARNING_LINE, EXECUTED, MISMATCHED_ROW, UNRUNNABLE_CASE, BANNER, LEFT_OUT, DRIVER_FALLBACK, MACHINE_WARNED, ORPHANS_REPORTED, STARVED, NESTED_ERROR = "While attempting to print an error, another error was printed:", WINDOWS_EXITS, MEMORY_EXITS, GDUNIT_FAILED = 100, GDUNIT_ORPHANS = 101, GDUNIT_EXITS, GODOT_EXITS, TAIL = 60, MOST_NAMED = 8;
|
|
11485
11494
|
var init_suite_judge = __esm(() => {
|
|
11486
11495
|
init_engine_leaks();
|
|
11487
11496
|
init_junit();
|
|
@@ -11493,6 +11502,7 @@ var init_suite_judge = __esm(() => {
|
|
|
11493
11502
|
WARNING_LINE = /^\s*(?:SCRIPT |USER )?WARNING: (.*?)\s*$/;
|
|
11494
11503
|
EXECUTED = /^Executed test cases\s*:\s*\((\d+)\//;
|
|
11495
11504
|
MISMATCHED_ROW = /^\s*The test data set at index \(\d+\) does not match the expected test parameters/;
|
|
11505
|
+
UNRUNNABLE_CASE = /^\s*Reason: '((?:Unknown test case argument's \[.*\] found|Fuzzer argument '.+' must be declared before the test config argument\(s\) \[.*\])\.)'\s*$/;
|
|
11496
11506
|
BANNER = /^\s*Godot Engine v(\S+?)(?:\s+-\s+\S+)?\s*$/;
|
|
11497
11507
|
LEFT_OUT = /^gdharness left out (res:\/\/.+?\.gd) > ([^:\s]+):(-?\d+)\s*$/;
|
|
11498
11508
|
DRIVER_FALLBACK = /^\s*WARNING: Your video card drivers .*, switching to /;
|
|
@@ -12101,7 +12111,8 @@ class EngineSlots {
|
|
|
12101
12111
|
changes = new FolderChanges(this.directory);
|
|
12102
12112
|
continue;
|
|
12103
12113
|
}
|
|
12104
|
-
|
|
12114
|
+
const sayAt = waiting !== undefined ? said + WAIT_SAID_MS : this.say !== undefined && this.held.size === 0 ? Math.max(began + COMMAND_WAIT_SAID_MS, this.saidAt + WAIT_SAID_MS) : Number.POSITIVE_INFINITY;
|
|
12115
|
+
await changes.next(Math.max(0, Math.min(SLOT_LOOK_MS, sayAt - Date.now())), SLOT_SETTLE_MS, signal);
|
|
12105
12116
|
}
|
|
12106
12117
|
} finally {
|
|
12107
12118
|
changes?.close();
|
|
@@ -12159,11 +12170,17 @@ class EngineSlots {
|
|
|
12159
12170
|
}
|
|
12160
12171
|
release(lock) {
|
|
12161
12172
|
this.held.delete(lock);
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12173
|
+
giveBackSlot(lock, this.holder.pid);
|
|
12174
|
+
}
|
|
12175
|
+
forget(lock) {
|
|
12176
|
+
this.held.delete(lock);
|
|
12177
|
+
}
|
|
12178
|
+
hand(lock, to) {
|
|
12179
|
+
const handed = handSlot(lock, this.holder.pid, to);
|
|
12180
|
+
if (handed) {
|
|
12181
|
+
this.held.delete(lock);
|
|
12182
|
+
}
|
|
12183
|
+
return handed;
|
|
12167
12184
|
}
|
|
12168
12185
|
made(path) {
|
|
12169
12186
|
let made;
|
|
@@ -12230,6 +12247,22 @@ class EngineSlots {
|
|
|
12230
12247
|
this.starts.set(pid, { started, readAt: Date.now() });
|
|
12231
12248
|
}
|
|
12232
12249
|
}
|
|
12250
|
+
function handSlot(lock, from, to) {
|
|
12251
|
+
let content;
|
|
12252
|
+
try {
|
|
12253
|
+
content = readWhole(lock);
|
|
12254
|
+
} catch {
|
|
12255
|
+
return false;
|
|
12256
|
+
}
|
|
12257
|
+
return content !== null && holderOf(content)?.pid === from && writeWhole(lock, JSON.stringify(to));
|
|
12258
|
+
}
|
|
12259
|
+
function giveBackSlot(lock, pid) {
|
|
12260
|
+
try {
|
|
12261
|
+
if (holderOf(readFileSync8(lock, "utf8"))?.pid === pid) {
|
|
12262
|
+
removeWhole(lock);
|
|
12263
|
+
}
|
|
12264
|
+
} catch {}
|
|
12265
|
+
}
|
|
12233
12266
|
function sayWaitsTo(say) {
|
|
12234
12267
|
saidTo = say;
|
|
12235
12268
|
}
|
|
@@ -12298,8 +12331,14 @@ async function slotForCall() {
|
|
|
12298
12331
|
waits.said ??= (waitedMs) => slotWaitedSaid(waitedMs, slots.count, slots.directory, heldBy);
|
|
12299
12332
|
}
|
|
12300
12333
|
return {
|
|
12334
|
+
lock,
|
|
12335
|
+
from: slots.holder.pid,
|
|
12301
12336
|
release: () => {
|
|
12302
12337
|
slots.release(lock);
|
|
12338
|
+
},
|
|
12339
|
+
handTo: (holder) => slots.hand(lock, holder),
|
|
12340
|
+
leave: () => {
|
|
12341
|
+
slots.forget(lock);
|
|
12303
12342
|
}
|
|
12304
12343
|
};
|
|
12305
12344
|
}
|
|
@@ -12838,7 +12877,7 @@ class GameLog {
|
|
|
12838
12877
|
return;
|
|
12839
12878
|
}
|
|
12840
12879
|
const owner = this.lastHeadline[source];
|
|
12841
|
-
if (owner !== null && /^\s/.test(line)) {
|
|
12880
|
+
if (owner !== null && /^\s/.test(line) && !GDUNIT_CASE.test(line)) {
|
|
12842
12881
|
owner.detail.push(line.trim());
|
|
12843
12882
|
owner.continues = false;
|
|
12844
12883
|
return;
|
|
@@ -12915,10 +12954,11 @@ function answersTo(entry, severity, contains) {
|
|
|
12915
12954
|
const needle = contains.toLowerCase();
|
|
12916
12955
|
return [entry.text, ...entry.detail].some((line) => line.toLowerCase().includes(needle));
|
|
12917
12956
|
}
|
|
12918
|
-
var HEADLINE, CRASH, CRASH_FILLER, COLOUR_CODE, STREAMS, ADMITTED, LOCATION2, FRAME, LONGEST_CYCLE = 64;
|
|
12957
|
+
var HEADLINE, CRASH, GDUNIT_CASE, CRASH_FILLER, COLOUR_CODE, STREAMS, ADMITTED, LOCATION2, FRAME, LONGEST_CYCLE = 64;
|
|
12919
12958
|
var init_game_log = __esm(() => {
|
|
12920
12959
|
HEADLINE = /^(USER )?(SCRIPT ERROR|SHADER ERROR|ERROR|WARNING):\s?(.*)$/;
|
|
12921
12960
|
CRASH = /^(?:CrashHandlerException|handle_crash): Program crashed with signal \d+/;
|
|
12961
|
+
GDUNIT_CASE = /^\s+res:\/\/.+?\.(?:gd|cs) > .+? (?:STARTED|PASSED|FAILED|ERROR|SKIPPED|FLAKY)(?:\s|$)/;
|
|
12922
12962
|
CRASH_FILLER = /^(?:=+|-- END OF .* BACKTRACE --|\[\d+\] .* - no debug info in .*)$/;
|
|
12923
12963
|
COLOUR_CODE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
12924
12964
|
STREAMS = ["stdout", "stderr", "transcript"];
|
|
@@ -14215,7 +14255,7 @@ async function operationOutcome(engine, operation, params, projectPath) {
|
|
|
14215
14255
|
let ran;
|
|
14216
14256
|
let answer;
|
|
14217
14257
|
try {
|
|
14218
|
-
ran = await runEngine(engine.godotPath, args);
|
|
14258
|
+
ran = await runEngine(engine.godotPath, args, { timeout: engine.ceiling ?? OPERATION_CEILING });
|
|
14219
14259
|
answer = answerIn(answerFile);
|
|
14220
14260
|
} finally {
|
|
14221
14261
|
discard(paramsDir);
|
|
@@ -14305,7 +14345,7 @@ function answerIn(file) {
|
|
|
14305
14345
|
};
|
|
14306
14346
|
}
|
|
14307
14347
|
}
|
|
14308
|
-
var AFTER_ANSWER_NOTE = "Printed after this operation had written its answer, while the engine ran the project's autoloads and shut down: Godot starts a project's autoloads in every script run and cannot be told not to. An error raised in a project script here is that autoload's own, and none of these changes the answer.", BEFORE_OPERATION_NOTE = "Printed while the engine started the project's autoloads, before this operation ran: an operation that compiles the project's scripts waits for them, because the engine knows an autoload's name only once it is in the tree. An error raised in a project script here is that autoload's own.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", WAITING = "[gdharness] waiting for the autoloads", BEGUN = "[gdharness] begun", ASIDE = "[gdharness] aside", RESUMED = "[gdharness] resumed", MESSAGES_KEPT = 200, ERRORS_NAMED = 3, NO_BLENDER = "Blender path is invalid or not set", BLENDER_STOPPED = "the import pass stopped at a .blend file and imported nothing: the engine imports .blend files through Blender, which the editor setting filesystem/import/blender/blender_path names, and here it names none. Set it in an editor, or set the project setting filesystem/import/blender/enabled to false with project_settings set, which has the engine leave .blend files unimported.";
|
|
14348
|
+
var AFTER_ANSWER_NOTE = "Printed after this operation had written its answer, while the engine ran the project's autoloads and shut down: Godot starts a project's autoloads in every script run and cannot be told not to. An error raised in a project script here is that autoload's own, and none of these changes the answer.", BEFORE_OPERATION_NOTE = "Printed while the engine started the project's autoloads, before this operation ran: an operation that compiles the project's scripts waits for them, because the engine knows an autoload's name only once it is in the tree. An error raised in a project script here is that autoload's own.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", WAITING = "[gdharness] waiting for the autoloads", BEGUN = "[gdharness] begun", ASIDE = "[gdharness] aside", RESUMED = "[gdharness] resumed", MESSAGES_KEPT = 200, OPERATION_CEILING, ERRORS_NAMED = 3, NO_BLENDER = "Blender path is invalid or not set", BLENDER_STOPPED = "the import pass stopped at a .blend file and imported nothing: the engine imports .blend files through Blender, which the editor setting filesystem/import/blender/blender_path names, and here it names none. Set it in an editor, or set the project setting filesystem/import/blender/enabled to false with project_settings set, which has the engine leave .blend files unimported.";
|
|
14309
14349
|
var init_headless = __esm(() => {
|
|
14310
14350
|
init_doc_cache();
|
|
14311
14351
|
init_engine_leaks();
|
|
@@ -14316,6 +14356,7 @@ var init_headless = __esm(() => {
|
|
|
14316
14356
|
init_reimport();
|
|
14317
14357
|
init_scratch();
|
|
14318
14358
|
init_script_origins();
|
|
14359
|
+
OPERATION_CEILING = { ms: 600000, said: "ten minutes" };
|
|
14319
14360
|
});
|
|
14320
14361
|
|
|
14321
14362
|
// src/update-check.ts
|
|
@@ -16583,13 +16624,25 @@ var init_tool_definitions = __esm(() => {
|
|
|
16583
16624
|
],
|
|
16584
16625
|
description: "add_track makes property and method tracks; remove_track takes any type read answers."
|
|
16585
16626
|
},
|
|
16586
|
-
nodePath: {
|
|
16627
|
+
nodePath: {
|
|
16628
|
+
type: "string",
|
|
16629
|
+
description: "Target node, relative to the AnimationPlayer's root_node, which is the player's parent unless the scene sets it."
|
|
16630
|
+
},
|
|
16587
16631
|
property: { type: "string", description: "For property tracks: the property to animate." },
|
|
16588
16632
|
update: {
|
|
16589
16633
|
type: "string",
|
|
16590
16634
|
enum: ["continuous", "discrete", "capture"],
|
|
16591
16635
|
description: "For property tracks: how the value moves between keys. continuous blends toward the next key, so a bool or an int changes halfway to it; discrete holds each key's value until the next; capture blends from the value the property holds when playing starts. Without it a new track is made as the editor makes one: as the RESET animation's track for the same property, otherwise continuous for a float, vector, colour or transform and discrete for anything else. The answer gives the track's update."
|
|
16592
16636
|
},
|
|
16637
|
+
interpolation: {
|
|
16638
|
+
type: "string",
|
|
16639
|
+
enum: ["nearest", "linear", "cubic", "linear_angle", "cubic_angle"],
|
|
16640
|
+
description: "For property tracks: how a continuous track blends between keys. Without it a new track is made linear, or as the RESET track for the same property is, and an existing track keeps its own."
|
|
16641
|
+
},
|
|
16642
|
+
loopWrap: {
|
|
16643
|
+
type: "boolean",
|
|
16644
|
+
description: "For property tracks: whether a looping animation blends its last key into its first. Without it a new track wraps, or as the RESET track for the same property does, and an existing track keeps its own."
|
|
16645
|
+
},
|
|
16593
16646
|
method: {
|
|
16594
16647
|
type: "string",
|
|
16595
16648
|
description: "For method tracks: the method to call. A method track holds every method key for its node, so a key calling another method joins the node's track, with existingTrack true. The answer's methodFound says whether the target node has it; a method it lacks is still added, while a nodePath that reaches no node is refused, since it may be written next, and until it exists each call the track makes fails. A key whose args the method cannot take (too few or too many, or an object where it takes a value or the reverse) is written too, and answers argumentsFit false with misfit saying why. remove_track: only the keys calling this method."
|
|
@@ -16947,7 +17000,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16947
17000
|
direction: {
|
|
16948
17001
|
type: "string",
|
|
16949
17002
|
enum: ["forward", "reverse"],
|
|
16950
|
-
description: "forward: what this resource loads,
|
|
17003
|
+
description: "forward: what this resource loads or names. Each file named by its path carries kind, as reverse words it: ext_resource, extends, preload, load, include, or path for a path only written in a string, which the file itself may never load. Each reference closing a cycle is marked circular, with the cycle under circular_references. A script's dependencies include every script whose class_name it uses, marked by_class with the name, and the script of every autoload it names, marked by_autoload with the autoload's name. An ext_resource whose uid names another file than its path is followed to that file, as the engine loads it, with the path written under stale_path; one whose uid names no file the project has is followed by its path, as the engine does after warning, with the uid under unknown_uid; and a load or preload of a uid the project does not know is listed under that uid as missing. A shader's #include is followed as the preprocessor reads it, relative to the shader. A file the depth stops at carries beyond_depth, how many files it names that were not walked. A file's dependencies are listed at its first entry, and again further down only where the depth cut that list short and more is left; every other entry for a file naming any carries dependencies_listed_above. summary.total_resources is the file asked about, so 1; total_dependencies counts each file reached, directly or through another, once however many places load it; total_references counts the entries in the tree, and missing names every file referenced that does not exist, with the files referring to it. reverse: every file that refers to it and how, for a file that is gone as well while anything still refers to it, answered with exists: false (a missing file nothing refers to is refused, naming the nearest files), a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name, and for an autoload's script every use of the autoload's name, with autoload naming it. Each reference carries a kind: ext_resource, extends, preload, load, include or path for a reference by path, with stale_path when an ext_resource reaches it by its uid under another path, and unknown_uid when one names it by path beside a uid naming no file; setting, plugin, export_preset and import for one in project.godot, a plugin.cfg, export_presets.cfg or an import sidecar; class_name for a use of the declared name, the declaration itself being the script asked about; autoload for code naming the autoload. Three kinds are not uses: doc is a name inside a ## documentation comment, which renaming would break in the generated docs; comment is one in ordinary prose, which it would only make stale; and stale_path is an ext_resource naming it by a path the engine does not load, since its uid leads to the file under loads. summary.in_code is the count with those left out, which is the number to read when the question is whether anything still uses this. Default forward."
|
|
16951
17004
|
},
|
|
16952
17005
|
depth: {
|
|
16953
17006
|
type: "integer",
|
|
@@ -16982,7 +17035,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16982
17035
|
options: {
|
|
16983
17036
|
type: "object",
|
|
16984
17037
|
ops: ["set_options"],
|
|
16985
|
-
description: `set_options: import options keyed as the .import file spells them, {"compress/mode": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {"opentype_features": {"liga": 0}} turns ligatures off. Both are written as the numbers the engine reads. One already holding the value given is answered under unchanged_options rather than updated_options, and when none changed nothing is written or reimported. changed gives each option written as {was, now}
|
|
17038
|
+
description: `set_options: import options keyed as the .import file spells them, {"compress/mode": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {"opentype_features": {"liga": 0}} turns ligatures off. Both are written as the numbers the engine reads. One already holding the value given is answered under unchanged_options rather than updated_options, and when none changed nothing is written or reimported. changed gives each option written as {was, now}.`,
|
|
16986
17039
|
additionalProperties: true
|
|
16987
17040
|
},
|
|
16988
17041
|
reimport: {
|
|
@@ -17022,7 +17075,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17022
17075
|
},
|
|
17023
17076
|
uid: {
|
|
17024
17077
|
effect: "read",
|
|
17025
|
-
summary: "the UID of one file and where it was read: a script's or shader's .uid file, an imported file's .import, or a scene's or resource's own header",
|
|
17078
|
+
summary: "the UID of one file and where it was read: a script's or shader's .uid file, an imported file's .import, or a scene's or resource's own header. A file with none is answered with uid null and what would give it one",
|
|
17026
17079
|
requires: ["resourcePath"]
|
|
17027
17080
|
},
|
|
17028
17081
|
refresh_uids: {
|
|
@@ -17069,7 +17122,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17069
17122
|
type: "string",
|
|
17070
17123
|
ops: ["set_preset"],
|
|
17071
17124
|
enum: ["all_resources", "scenes", "resources", "exclude"],
|
|
17072
|
-
description: "set_preset: what the preset exports: every resource, the scenes listed in files and what they use, the resources listed, or every resource but those listed."
|
|
17125
|
+
description: "set_preset: what the preset exports: every resource, the scenes listed in files and what they use, the resources listed, or every resource but those listed. What a scene or a resource uses is what the engine counts as its dependencies, so a file named only in a string, such as a scene path held in an exported property, or a script reached only by its class_name, is left out of the export, though project_dependencies lists both."
|
|
17073
17126
|
},
|
|
17074
17127
|
files: {
|
|
17075
17128
|
type: "array",
|
|
@@ -17130,7 +17183,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17130
17183
|
path: {
|
|
17131
17184
|
type: ["string", "array"],
|
|
17132
17185
|
items: { type: "string" },
|
|
17133
|
-
description: 'A test directory or suite file inside the project, or a list of them, all run on one engine: a named set of suites boots once rather than once per suite. An entry given twice, or a suite inside a directory also listed, runs once. An entry "suite.gd:test_name" runs that case alone, as gdharness test reads it and as tests runs it, so it cannot share the list with a whole suite or a directory. An entry that is not there is left out and named under missing, the rest run, and such a run is never called passed; when no entry is there the run is refused before the engine starts, naming each one. A script named that declares no test_ function at the top level is refused the same way, and so is a directory holding no suite. A script that declares test_ functions and does not extend GdUnitTestSuite, which gdUnit4 never runs and never mentions, is refused when named, and under a directory run whole it is named under notSuites and the run is not called passed. A case declared there that gdUnit4 never ran, in a suite that passed, comes back under testsNotRun,
|
|
17186
|
+
description: 'A test directory or suite file inside the project, or a list of them, all run on one engine: a named set of suites boots once rather than once per suite. An entry given twice, or a suite inside a directory also listed, runs once. An entry "suite.gd:test_name" runs that case alone, as gdharness test reads it and as tests runs it, so it cannot share the list with a whole suite or a directory. An entry that is not there is left out and named under missing, the rest run, and such a run is never called passed; when no entry is there the run is refused before the engine starts, naming each one. A script named that declares no test_ function at the top level is refused the same way, and so is a directory holding no suite. A script that declares test_ functions and does not extend GdUnitTestSuite, which gdUnit4 never runs and never mentions, is refused when named, and under a directory run whole it is named under notSuites and the run is not called passed. A case declared there that gdUnit4 never ran, in a suite that passed, comes back under testsNotRun, a row it skipped for data that does not fit the case under rowsMismatched, and a case it skipped because it cannot run it as declared (a parameter nothing fills, or a fuzzer after a setting) under casesUnrunnable, each with the reason gdUnit4 gave; none of those runs is called passed. Default tests and test, those the project has, which is where gdharness test looks.'
|
|
17134
17187
|
},
|
|
17135
17188
|
ignore: {
|
|
17136
17189
|
type: "array",
|
|
@@ -17204,7 +17257,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17204
17257
|
name: "scene_tree",
|
|
17205
17258
|
title: "Scene tree",
|
|
17206
17259
|
effect: "read",
|
|
17207
|
-
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one; its editableChildren says whether this scene, or a scene it inherits, keeps values of its own for them (scene_node set editableChildren). The root of a scene inheriting another names the scene it inherits under inherits, and every node that scene or one further down makes names its file under inheritedFrom; this file holds only the values it changes on them. A node in any group answers groups, sorted, from this file and every scene it comes from. Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.",
|
|
17260
|
+
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. type is the engine class, and a node whose script declares a class_name names it under scriptClass, as scene_node get does. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one; its editableChildren says whether this scene, or a scene it inherits, keeps values of its own for them (scene_node set editableChildren). The root of a scene inheriting another names the scene it inherits under inherits, and every node that scene or one further down makes names its file under inheritedFrom; this file holds only the values it changes on them. A node in any group answers groups, sorted, from this file and every scene it comes from. Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.",
|
|
17208
17261
|
parameters: {
|
|
17209
17262
|
projectPath: PROJECT_PATH,
|
|
17210
17263
|
scenePath: SCENE_PATH,
|
|
@@ -18215,7 +18268,11 @@ var init_tool_definitions = __esm(() => {
|
|
|
18215
18268
|
},
|
|
18216
18269
|
requires: [],
|
|
18217
18270
|
operations: {
|
|
18218
|
-
tree: {
|
|
18271
|
+
tree: {
|
|
18272
|
+
effect: "read",
|
|
18273
|
+
summary: "the live scene tree, with current_scene giving the path of the scene the game is showing and the file it was loaded from, or null when it shows none",
|
|
18274
|
+
requires: []
|
|
18275
|
+
},
|
|
18219
18276
|
text: {
|
|
18220
18277
|
effect: "read",
|
|
18221
18278
|
summary: "every line of text under nodePath, the values in its fields included, in reading order, leaving out what is hidden and everything under it, and a control drawn wholly outside the viewport or scrolled out of a ScrollContainer, which the player cannot read. Every line reads as drawn: rich text without tags, text only as far as it is typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each. A line drawn as a copy under a node saying the same words, as an outline or a glow is, reads once, counted under copies",
|
|
@@ -18620,12 +18677,12 @@ var init_tool_definitions = __esm(() => {
|
|
|
18620
18677
|
says: {
|
|
18621
18678
|
type: "string",
|
|
18622
18679
|
ops: ["until"],
|
|
18623
|
-
description: 'until: wait for these words to appear anywhere under nodePath, or anywhere in the game when no nodePath is given, instead of for a property, which is how a panel that rebuilds its labels is waited on at all: the labels are named afresh each redraw and the panel is what stays put. Matched the way runtime_inspect find matches says: case-insensitive, a plain word anywhere in the text, a pattern with * or ? a glob over the whole of it, and alternatives separated by | met by whichever appears first ("*won*|*lost*"); on a node the player can see unless includeHidden says otherwise; a label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Instead of, not as well as: a call carrying this and a property is refused, because they ask about different things and answering one of them silently is how a caller watches a screen believing they are watching a property. It looks every frame while a look is cheap and less often on a screen large enough for a look to cost a real share of a frame, so the game keeps its speed; the answer counts the frames the wait spanned and the looks it took, and says under looking_ms how long the looks took between them.'
|
|
18680
|
+
description: 'until: wait for these words to appear anywhere under nodePath, or anywhere in the game when no nodePath is given, instead of for a property, which is how a panel that rebuilds its labels is waited on at all: the labels are named afresh each redraw and the panel is what stays put. Matched the way runtime_inspect find matches says: case-insensitive, a plain word anywhere in the text, a pattern with * or ? a glob over the whole of it, and alternatives separated by | met by whichever appears first ("*won*|*lost*"); on a node the player can see unless includeHidden says otherwise; a label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Instead of, not as well as: a call carrying this and a property is refused, because they ask about different things and answering one of them silently is how a caller watches a screen believing they are watching a property. It looks every frame while a look is cheap and less often on a screen large enough for a look to cost a real share of a frame, so the game keeps its speed; the answer counts the frames the wait spanned and the looks it took, and says under looking_ms how long the looks took between them. A look on a large screen is read about 2ms to a frame, so no frame grows by more, except the last look as the time runs out, which is read whole; longest_hold_ms is the longest the looks held one frame, which tells a long frame the game logs apart from one the wait caused.'
|
|
18624
18681
|
},
|
|
18625
18682
|
includeHidden: {
|
|
18626
18683
|
type: "boolean",
|
|
18627
18684
|
ops: ["until"],
|
|
18628
|
-
description: "until, with says: count words on a hidden node too, and on one drawn outside the viewport or scrolled out of a ScrollContainer. Default false, since a wait for words is a wait for them to be shown, and a wait that runs out while such a node holds them says so under unseen_note: a button that exists hidden through a whole animation carries its words the whole time and satisfied the wait at once, and every wait on that screen fell back to counting frames."
|
|
18685
|
+
description: "until, with says: count words on a hidden node too, and on one drawn outside the viewport or scrolled out of a ScrollContainer. Default false, since a wait for words is a wait for them to be shown, and a wait that runs out while such a node holds them says so under unseen_note: a button that exists hidden through a whole animation carries its words the whole time and satisfied the wait at once, and every wait on that screen fell back to counting frames. What is drawn over a node is not looked at, so words under a dialog or a panel count either way."
|
|
18629
18686
|
},
|
|
18630
18687
|
timeoutMs: {
|
|
18631
18688
|
type: "integer",
|
|
@@ -18701,7 +18758,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18701
18758
|
},
|
|
18702
18759
|
step_out: {
|
|
18703
18760
|
effect: "additive",
|
|
18704
|
-
summary: "run to the end of the current function and stop at the next line run at its caller's depth: in its caller, or, when the call was the last thing its caller did, wherever script runs next, such as the caller's next call; a game this server started
|
|
18761
|
+
summary: "run to the end of the current function and stop at the next line run at its caller's depth: in its caller, or, when the call was the last thing its caller did, wherever script runs next, such as the caller's next call; only for a game this server started",
|
|
18705
18762
|
requires: []
|
|
18706
18763
|
}
|
|
18707
18764
|
}
|
|
@@ -18719,7 +18776,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18719
18776
|
expression: {
|
|
18720
18777
|
type: "string",
|
|
18721
18778
|
ops: ["evaluate"],
|
|
18722
|
-
description: "evaluate: a GDScript expression, as Godot's Expression class parses it: members of self by name, locals, autoloads and global classes, operators, indexing and calls, such as wave.spawned[0] or Ledger.entries.size(). An expression that does not parse, or fails as it runs, comes to null, and the game
|
|
18779
|
+
description: "evaluate: a GDScript expression, as Godot's Expression class parses it: members of self by name, locals, autoloads and global classes, operators, indexing and calls, such as wave.spawned[0] or Ledger.entries.size(). An expression that does not parse, or fails as it runs, comes to null with failed saying why, as the engine words it, and a null with no failed is the value; for a game the editor plays, the reason reaches only the editor's Debugger panel, so a null there carries a note saying it may be either. The game reports what an expression raises while it is held inside a runtime_invoke call as raised by that call, and the call's answer says so under errorsNote."
|
|
18723
18780
|
}
|
|
18724
18781
|
},
|
|
18725
18782
|
requires: [],
|
|
@@ -42552,6 +42609,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
42552
42609
|
const frameId = typeof safeArgs.frameId === "number" ? safeArgs.frameId : undefined;
|
|
42553
42610
|
const expression = typeof safeArgs.expression === "string" ? safeArgs.expression : "";
|
|
42554
42611
|
const evaluated = await client.evaluate(expression, frameId);
|
|
42612
|
+
const nil = evaluated.type === "Nil" || evaluated.value === "null" || evaluated.value === "<null>";
|
|
42555
42613
|
return {
|
|
42556
42614
|
content: [
|
|
42557
42615
|
{
|
|
@@ -42560,7 +42618,10 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
42560
42618
|
frame: evaluated.frame,
|
|
42561
42619
|
expression,
|
|
42562
42620
|
value: evaluated.value,
|
|
42563
|
-
...evaluated.type === undefined ? {} : { type: evaluated.type }
|
|
42621
|
+
...evaluated.type === undefined ? {} : { type: evaluated.type },
|
|
42622
|
+
...nil ? {
|
|
42623
|
+
note: "null is also what an expression comes to when it does not parse or fails as it runs. For a game the editor plays, why reaches only the editor's Debugger panel, so this cannot say which it was."
|
|
42624
|
+
} : {}
|
|
42564
42625
|
})
|
|
42565
42626
|
}
|
|
42566
42627
|
]
|
|
@@ -49508,6 +49569,13 @@ function restartsOf(run) {
|
|
|
49508
49569
|
endedAt: new Date(one.endedAt).toISOString()
|
|
49509
49570
|
}));
|
|
49510
49571
|
}
|
|
49572
|
+
async function handToTheGame(slot, pid, projectPath) {
|
|
49573
|
+
const started = (await startTimesOf([pid])).get(pid);
|
|
49574
|
+
const by = `a game${projectPath === null ? "" : ` of ${projectPath}`} the editor played for editor_run`;
|
|
49575
|
+
if (started === undefined || !slot.handTo({ pid, started, by })) {
|
|
49576
|
+
slot.release();
|
|
49577
|
+
}
|
|
49578
|
+
}
|
|
49511
49579
|
function followTheRestart(run) {
|
|
49512
49580
|
if (run.throughEditor || run.pid === null || run.projectPath === null) {
|
|
49513
49581
|
return false;
|
|
@@ -50034,6 +50102,13 @@ function presetsIn(projectPath) {
|
|
|
50034
50102
|
function presetPlatform(projectPath, preset) {
|
|
50035
50103
|
return presetsIn(projectPath)?.find((held) => held.name === preset)?.platform ?? null;
|
|
50036
50104
|
}
|
|
50105
|
+
function engineEndSaid(ending) {
|
|
50106
|
+
if (ending.failure === null && ending.exitSignal === null) {
|
|
50107
|
+
return `the engine ended with ${exitCodeText(ending.exitCode)}`;
|
|
50108
|
+
}
|
|
50109
|
+
const how = howItEnded(ending);
|
|
50110
|
+
return how.startsWith("ended by") ? `the engine was ${how}` : how;
|
|
50111
|
+
}
|
|
50037
50112
|
function exportPresetRefusal(projectPath, preset) {
|
|
50038
50113
|
const presets = presetsIn(projectPath);
|
|
50039
50114
|
if (presets === null) {
|
|
@@ -50068,7 +50143,24 @@ function exportAnswer(ending, asked, before, after, beside = [], docCacheNote =
|
|
|
50068
50143
|
if (verdict.exported) {
|
|
50069
50144
|
return { content: [{ type: "text", text: answerJson(verdict) }] };
|
|
50070
50145
|
}
|
|
50071
|
-
|
|
50146
|
+
if (written) {
|
|
50147
|
+
const errors = log.count("error");
|
|
50148
|
+
const because = [
|
|
50149
|
+
...ending.exitCode === 0 && ending.failure === null && ending.exitSignal === null ? [] : [engineEndSaid(ending)],
|
|
50150
|
+
...errors === 0 ? [] : [`it printed ${String(errors)} ${errors === 1 ? "error" : "errors"}, under entries`]
|
|
50151
|
+
];
|
|
50152
|
+
return {
|
|
50153
|
+
content: [
|
|
50154
|
+
{
|
|
50155
|
+
type: "text",
|
|
50156
|
+
text: `Export with preset '${asked.preset}' wrote ${asked.outputPath}, but it is not counted as exported, as ${because.join(" and ")}.`
|
|
50157
|
+
},
|
|
50158
|
+
{ type: "text", text: answerJson(verdict) }
|
|
50159
|
+
],
|
|
50160
|
+
isError: true
|
|
50161
|
+
};
|
|
50162
|
+
}
|
|
50163
|
+
const stale = after === null ? "" : ` The ${asked.outputPath} there is from before this export, which did not write it.`;
|
|
50072
50164
|
const why = ending.exitSignal === null ? stale : ` The engine was ${howItEnded(ending)}, so it has no exit code.${stale}`;
|
|
50073
50165
|
return {
|
|
50074
50166
|
content: [
|
|
@@ -50823,6 +50915,17 @@ function ownArguments(params) {
|
|
|
50823
50915
|
function needsItsOwnProcess(asked) {
|
|
50824
50916
|
return asked.savesIn !== undefined || asked.env !== undefined;
|
|
50825
50917
|
}
|
|
50918
|
+
function unheldNote(changed) {
|
|
50919
|
+
if (!isPlainObject3(changed)) {
|
|
50920
|
+
return "";
|
|
50921
|
+
}
|
|
50922
|
+
const unheld = Object.entries(changed).filter(([, change]) => isPlainObject3(change) && change["was"] === null).map(([name]) => name);
|
|
50923
|
+
if (unheld.length === 0) {
|
|
50924
|
+
return "";
|
|
50925
|
+
}
|
|
50926
|
+
const one = unheld.length === 1;
|
|
50927
|
+
return ` ${unheld.join(", ")} ${one ? "was" : "were"} not in the import file before, so only that reimport can tell whether the importer has ${one ? "it" : "them"}: one it lacks is dropped from the file then, and the reimport through set_options says which.`;
|
|
50928
|
+
}
|
|
50826
50929
|
|
|
50827
50930
|
class GodotServer {
|
|
50828
50931
|
stdio = null;
|
|
@@ -52454,7 +52557,7 @@ class GodotServer {
|
|
|
52454
52557
|
...written,
|
|
52455
52558
|
payload: {
|
|
52456
52559
|
...written.payload,
|
|
52457
|
-
note:
|
|
52560
|
+
note: `Not reimported: the editor imports it with these options on its next scan, or project_import reimport does now.${unheldNote(written.payload["changed"])}`
|
|
52458
52561
|
}
|
|
52459
52562
|
} : written);
|
|
52460
52563
|
}
|
|
@@ -53509,10 +53612,14 @@ ${error.text}`;
|
|
|
53509
53612
|
test: `${row.suite} > ${row.name}`,
|
|
53510
53613
|
reason: row.reason
|
|
53511
53614
|
}));
|
|
53615
|
+
const casesUnrunnable = casePrinted.unrunnable.map((one) => ({
|
|
53616
|
+
test: `${one.suite} > ${one.name}`,
|
|
53617
|
+
reason: one.reason
|
|
53618
|
+
}));
|
|
53512
53619
|
return this.jsonTextResponse({
|
|
53513
|
-
passed: !timedOut && exitCode === 0 && nothingRan === null && report.tests > 0 && report.failures === 0 && report.errors === 0 && hookFailures.length === 0 && uncaught.length === 0 && undeclaredPrinted.length === 0 && declaredNotPrinted.length === 0 && countedLeaks.length === 0 && missing.length === 0 && testsNotRun.length === 0 && casesNotRun.length === 0 && rowsMismatched.length === 0 && notSuites.length === 0,
|
|
53620
|
+
passed: !timedOut && exitCode === 0 && nothingRan === null && report.tests > 0 && report.failures === 0 && report.errors === 0 && hookFailures.length === 0 && uncaught.length === 0 && undeclaredPrinted.length === 0 && declaredNotPrinted.length === 0 && countedLeaks.length === 0 && missing.length === 0 && testsNotRun.length === 0 && casesNotRun.length === 0 && rowsMismatched.length === 0 && casesUnrunnable.length === 0 && notSuites.length === 0,
|
|
53514
53621
|
verdict: [
|
|
53515
|
-
verdict === "passed" && undeclaredPrinted.length + declaredNotPrinted.length + countedLeaks.length + testsNotRun.length + casesNotRun.length + rowsMismatched.length + notSuites.length > 0 ? undefined : verdict,
|
|
53622
|
+
verdict === "passed" && undeclaredPrinted.length + declaredNotPrinted.length + countedLeaks.length + testsNotRun.length + casesNotRun.length + rowsMismatched.length + casesUnrunnable.length + notSuites.length > 0 ? undefined : verdict,
|
|
53516
53623
|
...[false, true].map((warning) => {
|
|
53517
53624
|
const count = undeclaredPrinted.filter((error) => error.warning === warning).length;
|
|
53518
53625
|
const word = warning ? "warning" : "error";
|
|
@@ -53526,12 +53633,14 @@ ${error.text}`;
|
|
|
53526
53633
|
countedLeaks.length > 0 ? "the engine exited holding what it should have freed" : undefined,
|
|
53527
53634
|
testsNotRun.length + casesNotRun.length > 0 ? `not run: ${[...testsNotRun, ...casesNotRun].join(", ")}` : undefined,
|
|
53528
53635
|
rowsMismatched.length > 0 ? `${rowsMismatched.length === 1 ? "one row" : `${String(rowsMismatched.length)} rows`} skipped for data that does not fit the case` : undefined,
|
|
53636
|
+
casesUnrunnable.length > 0 ? `${casesUnrunnable.length === 1 ? "one case" : `${String(casesUnrunnable.length)} cases`} skipped as gdUnit4 cannot run ${casesUnrunnable.length === 1 ? "it" : "them"} as declared` : undefined,
|
|
53529
53637
|
notSuites.length > 0 ? `${notSuites.length === 1 ? "one script declares" : `${String(notSuites.length)} scripts declare`} cases gdUnit4 never runs` : undefined
|
|
53530
53638
|
].filter((part) => part !== undefined).join("; "),
|
|
53531
53639
|
...missing.length > 0 ? { missing } : {},
|
|
53532
53640
|
...notSuites.length > 0 ? { notSuites } : {},
|
|
53533
53641
|
...testsNotRun.length + casesNotRun.length > 0 ? { testsNotRun: [...testsNotRun, ...casesNotRun] } : {},
|
|
53534
53642
|
...rowsMismatched.length > 0 ? { rowsMismatched } : {},
|
|
53643
|
+
...casesUnrunnable.length > 0 ? { casesUnrunnable } : {},
|
|
53535
53644
|
...timedOut ? { timedOut, hung, silentForMs } : {},
|
|
53536
53645
|
exitCode,
|
|
53537
53646
|
exitSignal: run.exitSignal ?? undefined,
|
|
@@ -53565,6 +53674,7 @@ ${error.text}`;
|
|
|
53565
53674
|
testsNotRun.length > 0 ? `${testsNotRun.join(", ")} ${testsNotRun.length === 1 ? "was" : "were"} asked for and not run, so the run is not called passed: gdUnit4 runs a test_ function only in a script that extends GdUnitTestSuite, and leaves out a suite whose script does not load.` : undefined,
|
|
53566
53675
|
casesNotRun.length > 0 ? `${casesNotRun.join(", ")} ${casesNotRun.length === 1 ? "is a case" : "are cases"} of a suite that ran and passed, and gdUnit4 never ran ${casesNotRun.length === 1 ? "it" : "them"} and said nothing of it, so the run is not called passed, as gdharness test judges it.` : undefined,
|
|
53567
53676
|
rowsMismatched.length > 0 ? `gdUnit4 skipped what rowsMismatched names because the row's data does not fit the case's parameters, though it printed ${rowsMismatched.length === 1 ? "the row" : "each row"} passed first; each entry carries gdUnit4's reason. The run is not called passed, as gdharness test judges it.` : undefined,
|
|
53677
|
+
casesUnrunnable.length > 0 ? `gdUnit4 skipped what casesUnrunnable names because it cannot run ${casesUnrunnable.length === 1 ? "that case" : "those cases"} as declared, though it printed ${casesUnrunnable.length === 1 ? "it" : "each"} passed first; each entry carries gdUnit4's reason. A parameter is filled only by _test_parameters, a fuzzer or a setting gdUnit4 names (timeout, do_skip, skip_reason, fuzzer_iterations, fuzzer_seed), and fuzzers come before the settings. The run is not called passed, as gdharness test judges it.` : undefined,
|
|
53568
53678
|
notSuites.length > 0 ? "What notSuites names declares cases gdUnit4 never runs, so the run is not called passed, as gdharness test judges it." : undefined,
|
|
53569
53679
|
briefed > 0 ? `${briefed} of the ${failed.length} failing cases, those with the largest reports, are named with their first message only, since their reports would have made the answer too large to deliver; pass one case's suite path to see its reports.` : undefined,
|
|
53570
53680
|
notApplied.length > 0 ? "An ignore entry named a case that ran all the same: gdUnit4 leaves a case out only as suite_name:test_name, and reads an entry with no colon as a suite name or a piece of a suite path. ignoreNotApplied gives the form for each case that ran." : undefined,
|
|
@@ -55114,17 +55224,20 @@ ${error.text}`;
|
|
|
55114
55224
|
const sendingFrom = Date.now();
|
|
55115
55225
|
const breakpoints = await this.dapHoldingBreakpointsOf(projectPath).reapplyBreakpoints();
|
|
55116
55226
|
this.logDebug(`Breakpoints were sent again after ${Date.now() - sendingFrom}ms`);
|
|
55227
|
+
const slot = await slotForCall();
|
|
55117
55228
|
if (profiled) {
|
|
55118
55229
|
const armed = await this.handleViaBridge("profile_start", {
|
|
55119
55230
|
arm: true,
|
|
55120
55231
|
frameFunctions: profileFrameFunctions(projectPath)
|
|
55121
55232
|
});
|
|
55122
55233
|
if (armed.isError === true) {
|
|
55234
|
+
slot.release();
|
|
55123
55235
|
return armed;
|
|
55124
55236
|
}
|
|
55125
55237
|
}
|
|
55126
55238
|
const answer = await this.handleViaBridge("play_scene", scene === null ? {} : { scenePath: `res://${scene}` });
|
|
55127
55239
|
if (answer.isError === true) {
|
|
55240
|
+
slot.release();
|
|
55128
55241
|
return answer;
|
|
55129
55242
|
}
|
|
55130
55243
|
const startedAt = Date.now();
|
|
@@ -55154,7 +55267,8 @@ ${error.text}`;
|
|
|
55154
55267
|
announcedBefore: alreadyPlaying,
|
|
55155
55268
|
seenPlaying: readBoolean(playAnswer, "playing") === true,
|
|
55156
55269
|
...which === null ? {} : { play: which },
|
|
55157
|
-
bootScene: runtimeWaitMs.scene
|
|
55270
|
+
bootScene: runtimeWaitMs.scene,
|
|
55271
|
+
slot
|
|
55158
55272
|
};
|
|
55159
55273
|
this.activeProcess = played;
|
|
55160
55274
|
this.noteTheLatestRun({ projectPath, pid: null, startedAt });
|
|
@@ -55170,6 +55284,11 @@ ${error.text}`;
|
|
|
55170
55284
|
sweepTranscripts();
|
|
55171
55285
|
step("sweep");
|
|
55172
55286
|
this.logDebug(`After the play: ${steps.join(", ")}`);
|
|
55287
|
+
const runtime = await this.runtimeUp(projectPath, alreadyPlaying, runtimeWaitMs, false);
|
|
55288
|
+
if (played.slot !== undefined) {
|
|
55289
|
+
played.slot.release();
|
|
55290
|
+
delete played.slot;
|
|
55291
|
+
}
|
|
55173
55292
|
return this.jsonTextResponse({
|
|
55174
55293
|
started: true,
|
|
55175
55294
|
through: "editor",
|
|
@@ -55186,7 +55305,7 @@ ${error.text}`;
|
|
|
55186
55305
|
scriptPath: projectSpelling(projectPath, one.scriptPath),
|
|
55187
55306
|
reason: one.reason
|
|
55188
55307
|
})),
|
|
55189
|
-
runtime
|
|
55308
|
+
runtime,
|
|
55190
55309
|
message: "The editor is playing it, so its debugger holds it: the debug_* tools can reach it, " + "editor_output reads its console through the debug adapter and the runtime's report of it, and editor_run stop ends it." + endedToStartThis(ended)
|
|
55191
55310
|
});
|
|
55192
55311
|
}
|
|
@@ -55567,20 +55686,33 @@ ${error.text}`;
|
|
|
55567
55686
|
closeSync5(transcript.fd);
|
|
55568
55687
|
const gameEnv = profiled ? { ...env ?? process.env, [PROFILE_FROM_START]: "1" } : env;
|
|
55569
55688
|
const profile = profiled ? profilePath(startedAt) : undefined;
|
|
55570
|
-
const
|
|
55571
|
-
|
|
55572
|
-
|
|
55573
|
-
|
|
55574
|
-
|
|
55575
|
-
|
|
55576
|
-
|
|
55577
|
-
|
|
55578
|
-
|
|
55579
|
-
|
|
55580
|
-
|
|
55581
|
-
|
|
55582
|
-
|
|
55583
|
-
|
|
55689
|
+
const slot = await slotForCall();
|
|
55690
|
+
let launched;
|
|
55691
|
+
try {
|
|
55692
|
+
launched = await launchOutsideTheTree({
|
|
55693
|
+
command: godotPath,
|
|
55694
|
+
args: cmdArgs,
|
|
55695
|
+
...gameEnv === undefined ? {} : { env: gameEnv },
|
|
55696
|
+
run: {
|
|
55697
|
+
transcript: transcript.path,
|
|
55698
|
+
startedAt,
|
|
55699
|
+
projectPath,
|
|
55700
|
+
...this.servedBy(),
|
|
55701
|
+
...profile === undefined ? {} : { profile },
|
|
55702
|
+
...debugged ? { breakpoints: this.breakpointsForAGame(projectPath) } : { debugged: false },
|
|
55703
|
+
slot: { lock: slot.lock, from: slot.from, by: `a game of ${projectPath} editor_run started` }
|
|
55704
|
+
},
|
|
55705
|
+
...desktop === undefined ? {} : { desktop }
|
|
55706
|
+
});
|
|
55707
|
+
} catch (error) {
|
|
55708
|
+
slot.release();
|
|
55709
|
+
throw error;
|
|
55710
|
+
}
|
|
55711
|
+
if ("error" in launched) {
|
|
55712
|
+
slot.release();
|
|
55713
|
+
} else {
|
|
55714
|
+
slot.leave();
|
|
55715
|
+
}
|
|
55584
55716
|
if ("error" in launched) {
|
|
55585
55717
|
return launched;
|
|
55586
55718
|
}
|
|
@@ -55802,6 +55934,11 @@ ${error.text}`;
|
|
|
55802
55934
|
tie(run, endpoint) {
|
|
55803
55935
|
run.announcedPid = endpoint.pid;
|
|
55804
55936
|
this.noteBootOf(run, endpoint);
|
|
55937
|
+
const slot = run.slot;
|
|
55938
|
+
if (slot !== undefined) {
|
|
55939
|
+
delete run.slot;
|
|
55940
|
+
handToTheGame(slot, endpoint.pid, run.projectPath);
|
|
55941
|
+
}
|
|
55805
55942
|
}
|
|
55806
55943
|
playedByOurEditor(endpoint) {
|
|
55807
55944
|
const editor = this.godotBridge.getStatus().editorPid;
|
|
@@ -16,6 +16,22 @@ const UPDATE_MODE_NAMES: Dictionary[int, String] = {
|
|
|
16
16
|
Animation.UPDATE_DISCRETE: "discrete",
|
|
17
17
|
Animation.UPDATE_CAPTURE: "capture",
|
|
18
18
|
}
|
|
19
|
+
const INTERPOLATION_NAMES: Dictionary[int, String] = {
|
|
20
|
+
Animation.INTERPOLATION_NEAREST: "nearest",
|
|
21
|
+
Animation.INTERPOLATION_LINEAR: "linear",
|
|
22
|
+
Animation.INTERPOLATION_CUBIC: "cubic",
|
|
23
|
+
Animation.INTERPOLATION_LINEAR_ANGLE: "linear_angle",
|
|
24
|
+
Animation.INTERPOLATION_CUBIC_ANGLE: "cubic_angle",
|
|
25
|
+
}
|
|
26
|
+
## The track types whose keys are blended between, so how they are and whether a looping animation
|
|
27
|
+
## blends its last key into its first say how the track moves.
|
|
28
|
+
const INTERPOLATED_TYPES: Array[int] = [
|
|
29
|
+
Animation.TYPE_VALUE,
|
|
30
|
+
Animation.TYPE_POSITION_3D,
|
|
31
|
+
Animation.TYPE_ROTATION_3D,
|
|
32
|
+
Animation.TYPE_SCALE_3D,
|
|
33
|
+
Animation.TYPE_BLEND_SHAPE,
|
|
34
|
+
]
|
|
19
35
|
const TRACK_TYPE_NAMES: Dictionary[int, String] = {
|
|
20
36
|
Animation.TYPE_VALUE: "property",
|
|
21
37
|
Animation.TYPE_POSITION_3D: "position_3d",
|
|
@@ -174,6 +190,10 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
|
|
|
174
190
|
if kind == Animation.TYPE_VALUE:
|
|
175
191
|
var mode: int = anim.value_track_get_update_mode(track)
|
|
176
192
|
shaped["update"] = UPDATE_MODE_NAMES.get(mode, str(mode))
|
|
193
|
+
if kind in INTERPOLATED_TYPES:
|
|
194
|
+
var interpolation: int = anim.track_get_interpolation_type(track)
|
|
195
|
+
shaped["interpolation"] = INTERPOLATION_NAMES.get(interpolation, str(interpolation))
|
|
196
|
+
shaped["loopWrap"] = anim.track_get_interpolation_loop_wrap(track)
|
|
177
197
|
var keyframes: Array[Dictionary] = []
|
|
178
198
|
var methods: Dictionary = {}
|
|
179
199
|
for key: int in anim.track_get_key_count(track):
|
|
@@ -356,6 +356,12 @@ static func _entry(
|
|
|
356
356
|
var data: Dictionary = {
|
|
357
357
|
"name": node["name"], "type": node["type"], "path": path, "children": children,
|
|
358
358
|
}
|
|
359
|
+
var given_values: Dictionary = node["properties"]
|
|
360
|
+
var attached: Variant = given_values.get("script")
|
|
361
|
+
if attached is Script:
|
|
362
|
+
var script: Script = attached
|
|
363
|
+
if not str(script.get_global_name()).is_empty():
|
|
364
|
+
data["scriptClass"] = str(script.get_global_name())
|
|
359
365
|
# The root's file is the scene being read, so only another node's names a second scene.
|
|
360
366
|
if path != "." and node.has("instanceOf"):
|
|
361
367
|
data["instanceOf"] = node["instanceOf"]
|