gdharness 1.2.36 → 1.2.37
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 +314 -68
- package/build/godot/addons/gdharness_editor/headless_check.gd +374 -0
- package/build/godot/addons/gdharness_editor/headless_check.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +3 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +345 -10
- package/build/godot/addons/gdharness_editor/serialisation.gd +103 -5
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +68 -18
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +24 -13
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +9 -0
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +5 -0
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +3 -0
- package/build/godot/addons/gdharness_runtime/serialisation.gd +103 -5
- package/build/godot/operations/editor_tools.gd +57 -0
- package/build/godot/operations/gdscript_analysis.gd +26 -2
- package/build/godot/operations/gdscript_authoring.gd +5 -2
- package/build/godot/operations/godot_operations.gd +51 -1
- package/build/godot/operations/property_values.gd +3 -0
- package/build/godot/operations/serialisation.gd +103 -5
- package/build/index.js +304 -58
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -8487,6 +8487,7 @@ var init_scratch = __esm(() => {
|
|
|
8487
8487
|
waiting = new Set;
|
|
8488
8488
|
atExit = new Set;
|
|
8489
8489
|
KINDS = [
|
|
8490
|
+
"answers",
|
|
8490
8491
|
"desktop",
|
|
8491
8492
|
"export",
|
|
8492
8493
|
"import",
|
|
@@ -8509,8 +8510,14 @@ function snakeCased(params) {
|
|
|
8509
8510
|
}
|
|
8510
8511
|
function engineExtras(outcome) {
|
|
8511
8512
|
const after = outcome.afterAnswerMessages ?? [];
|
|
8513
|
+
const before = outcome.beforeOperationMessages ?? [];
|
|
8512
8514
|
return {
|
|
8513
8515
|
...outcome.messages.length > 0 ? { engine_messages: outcome.messages } : {},
|
|
8516
|
+
...before.length > 0 ? {
|
|
8517
|
+
engine_messages_before_operation: before,
|
|
8518
|
+
engine_messages_before_operation_note: BEFORE_OPERATION_NOTE
|
|
8519
|
+
} : {},
|
|
8520
|
+
...outcome.beforeOperationOmitted === undefined ? {} : { engine_messages_before_operation_omitted: outcome.beforeOperationOmitted },
|
|
8514
8521
|
...outcome.messagesOmitted === undefined ? {} : { engine_messages_omitted: outcome.messagesOmitted },
|
|
8515
8522
|
...after.length > 0 ? { engine_messages_after_answer: after, engine_messages_after_answer_note: AFTER_ANSWER_NOTE } : {},
|
|
8516
8523
|
...outcome.afterAnswerOmitted === undefined ? {} : { engine_messages_after_answer_omitted: outcome.afterAnswerOmitted },
|
|
@@ -8655,12 +8662,22 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
8655
8662
|
discard(paramsDir);
|
|
8656
8663
|
}
|
|
8657
8664
|
const everything = ran.log.everything();
|
|
8658
|
-
const
|
|
8659
|
-
const
|
|
8665
|
+
const marked = (text) => everything.findIndex((entry) => entry.source === "stderr" && entry.text === text);
|
|
8666
|
+
const answered = marked(ANSWERED);
|
|
8667
|
+
const waiting = marked(WAITING);
|
|
8668
|
+
const begun = marked(BEGUN);
|
|
8669
|
+
const ended = answered === -1 ? everything.length : answered;
|
|
8670
|
+
const problems = waiting === -1 ? problemsAmong(everything.slice(0, ended)) : problemsAmong([
|
|
8671
|
+
...everything.slice(0, waiting),
|
|
8672
|
+
...begun === -1 ? [] : everything.slice(begun + 1, ended)
|
|
8673
|
+
]);
|
|
8674
|
+
const startup = waiting === -1 ? [] : problemsAmong(everything.slice(waiting + 1, begun === -1 ? ended : begun));
|
|
8660
8675
|
const after = answered === -1 ? [] : problemsAmong(everything.slice(answered + 1));
|
|
8661
8676
|
const afterAnswered = {
|
|
8662
8677
|
...after.length === 0 ? {} : { afterAnswerMessages: after.slice(-MESSAGES_KEPT) },
|
|
8663
|
-
...after.length > MESSAGES_KEPT ? { afterAnswerOmitted: after.length - MESSAGES_KEPT } : {}
|
|
8678
|
+
...after.length > MESSAGES_KEPT ? { afterAnswerOmitted: after.length - MESSAGES_KEPT } : {},
|
|
8679
|
+
...startup.length === 0 ? {} : { beforeOperationMessages: startup.slice(-MESSAGES_KEPT) },
|
|
8680
|
+
...startup.length > MESSAGES_KEPT ? { beforeOperationOmitted: startup.length - MESSAGES_KEPT } : {}
|
|
8664
8681
|
};
|
|
8665
8682
|
if (answer.kind === "unreadable") {
|
|
8666
8683
|
return {
|
|
@@ -8671,6 +8688,14 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
8671
8688
|
};
|
|
8672
8689
|
}
|
|
8673
8690
|
if (answer.kind === "none") {
|
|
8691
|
+
if (waiting !== -1 && begun === -1) {
|
|
8692
|
+
return {
|
|
8693
|
+
ok: false,
|
|
8694
|
+
message: `${operation} never ran: the engine ended (${howItEnded(ran)}) while it started the project's ` + `autoloads, which this operation waits for. The last it reported: ${reason(ran.log, startup)}`,
|
|
8695
|
+
...reported2(problems),
|
|
8696
|
+
...afterAnswered
|
|
8697
|
+
};
|
|
8698
|
+
}
|
|
8674
8699
|
const refusals = refusalsIn(ran.log);
|
|
8675
8700
|
if (refusals.length > 0) {
|
|
8676
8701
|
const said = new Set(refusals.map((entry) => entry.index));
|
|
@@ -8720,7 +8745,7 @@ function answerIn(file) {
|
|
|
8720
8745
|
};
|
|
8721
8746
|
}
|
|
8722
8747
|
}
|
|
8723
|
-
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.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", MESSAGES_KEPT = 200, ERRORS_NAMED = 3;
|
|
8748
|
+
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", MESSAGES_KEPT = 200, ERRORS_NAMED = 3;
|
|
8724
8749
|
var init_headless = __esm(() => {
|
|
8725
8750
|
init_engine_run();
|
|
8726
8751
|
init_library_copies();
|
|
@@ -11141,7 +11166,7 @@ function annotationsOf(spec) {
|
|
|
11141
11166
|
openWorldHint: false
|
|
11142
11167
|
};
|
|
11143
11168
|
}
|
|
11144
|
-
var PROJECT_INFO_SECTIONS, PROJECT_PATH, RUNNING_PROJECT_PATH, RUNNING_PID, SCENE_PATH, SCRIPT_PATH, RESOURCE_FILE = 'File inside the project, such as "sprites/hero.png" or "materials/steel.tres".', RESOURCE_PATH, NODE_PATH, PROPERTIES, XY, ANIMATION_TRACK, SCRIPT_MODIFICATIONS, INPUT_EVENTS, TILESET_SOURCES, TILEMAP_CELLS, COLOUR, TOOL_SPECS, SPECS_BY_NAME;
|
|
11169
|
+
var PROJECT_INFO_SECTIONS, PROJECT_PATH, RUNNING_PROJECT_PATH, RUNNING_PID, SCENE_PATH, WITHOUT_AN_EDITOR = "Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around.", WHOLE_NUMBERS = 'A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {"_type": "int", "value": "9007199254740993"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused.', SCRIPT_PATH, RESOURCE_FILE = 'File inside the project, such as "sprites/hero.png" or "materials/steel.tres".', RESOURCE_PATH, NODE_PATH, PROPERTIES, XY, ANIMATION_TRACK, SCRIPT_MODIFICATIONS, INPUT_EVENTS, TILESET_SOURCES, TILEMAP_CELLS, COLOUR, TOOL_SPECS, SPECS_BY_NAME;
|
|
11145
11170
|
var init_tool_definitions = __esm(() => {
|
|
11146
11171
|
init_launch();
|
|
11147
11172
|
PROJECT_INFO_SECTIONS = [
|
|
@@ -11179,7 +11204,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11179
11204
|
};
|
|
11180
11205
|
PROPERTIES = {
|
|
11181
11206
|
type: "object",
|
|
11182
|
-
description:
|
|
11207
|
+
description: `Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {"x": 1, "y": 2} or tagged {"_type": "Vector2", "x": 1, "y": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: "position:x", "modulate:a", "region_rect:size:y"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as "../Player". Each value is read back once set, and one the engine changed or refused is refused with what it holds. ${WHOLE_NUMBERS}`,
|
|
11183
11208
|
additionalProperties: true
|
|
11184
11209
|
};
|
|
11185
11210
|
XY = {
|
|
@@ -11223,7 +11248,10 @@ var init_tool_definitions = __esm(() => {
|
|
|
11223
11248
|
name: { type: "string" },
|
|
11224
11249
|
params: { type: "string", description: 'Functions and signals: the parameter list, "delta: float".' },
|
|
11225
11250
|
returnType: { type: "string", description: "Functions: the return type." },
|
|
11226
|
-
body: {
|
|
11251
|
+
body: {
|
|
11252
|
+
type: "string",
|
|
11253
|
+
description: "Functions: the body, its lines separated by \\n. Indent shared by every line is taken off and one tab put on, so it can be written indented or not."
|
|
11254
|
+
},
|
|
11227
11255
|
varType: {
|
|
11228
11256
|
type: "string",
|
|
11229
11257
|
description: "Variables and constants: the type annotation; without one, the type the value evaluates to."
|
|
@@ -11326,7 +11354,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11326
11354
|
{
|
|
11327
11355
|
name: "project_settings",
|
|
11328
11356
|
title: "Project settings",
|
|
11329
|
-
description: "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is. add_input_action on an action that exists, one of the engine's ui_ actions included, replaces its events and deadzone, and answers was_new false with what it held under old_events and old_deadzone. A set of the value the setting already holds, in the same type, writes nothing and answers unchanged true and saved false. Every write keeps the sections and keys of project.godot in the order the file had them, so a change
|
|
11357
|
+
description: "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is. add_input_action on an action that exists, one of the engine's ui_ actions included, replaces its events and deadzone, and answers was_new false with what it held under old_events and old_deadzone. A set of the value the setting already holds, in the same type, writes nothing and answers unchanged true and saved false. Every write keeps the sections and keys of project.godot in the order the file had them, so a change moves no other line; the save is the engine's, and like the editor's it writes Godot's header and config/features into a file that lacks them. get answers exists, true for any setting the engine knows, and at_default, true when the value is the engine's own default and so has no line in project.godot. Each setting under prefix carries at_default too, so a family read separates what the project sets from the engine's defaults; it is absent for a setting the engine registers no default for, such as an input action the project added, which only the project can have set.",
|
|
11330
11358
|
parameters: {
|
|
11331
11359
|
projectPath: PROJECT_PATH,
|
|
11332
11360
|
setting: {
|
|
@@ -11504,24 +11532,32 @@ var init_tool_definitions = __esm(() => {
|
|
|
11504
11532
|
description: "The import pipeline: what needs importing, how a resource is imported, reimports, UIDs, and the global class list the editor and the engine read.",
|
|
11505
11533
|
parameters: {
|
|
11506
11534
|
projectPath: PROJECT_PATH,
|
|
11507
|
-
resourcePath: RESOURCE_PATH,
|
|
11535
|
+
resourcePath: { ...RESOURCE_PATH, ops: ["status", "options", "set_options", "uid", "reimport"] },
|
|
11508
11536
|
includeUpToDate: {
|
|
11509
11537
|
type: "boolean",
|
|
11538
|
+
ops: ["status"],
|
|
11510
11539
|
description: "status: list resources that are current as well. Default false."
|
|
11511
11540
|
},
|
|
11512
11541
|
options: {
|
|
11513
11542
|
type: "object",
|
|
11543
|
+
ops: ["set_options"],
|
|
11514
11544
|
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.`,
|
|
11515
11545
|
additionalProperties: true
|
|
11516
11546
|
},
|
|
11517
11547
|
reimport: {
|
|
11518
11548
|
type: "boolean",
|
|
11549
|
+
ops: ["set_options"],
|
|
11519
11550
|
description: "set_options: reimport afterwards, answered under reimport as project_import reimport answers. Default true."
|
|
11520
11551
|
},
|
|
11521
|
-
force: {
|
|
11552
|
+
force: {
|
|
11553
|
+
type: "boolean",
|
|
11554
|
+
ops: ["reimport"],
|
|
11555
|
+
description: "reimport: reimport even what is current. Default false."
|
|
11556
|
+
},
|
|
11522
11557
|
timeoutMs: {
|
|
11523
11558
|
type: "integer",
|
|
11524
11559
|
minimum: 1,
|
|
11560
|
+
ops: ["reimport", "set_options"],
|
|
11525
11561
|
description: "reimport, set_options: how long to wait for the editor to finish reimporting. Default 300000."
|
|
11526
11562
|
}
|
|
11527
11563
|
},
|
|
@@ -11599,7 +11635,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11599
11635
|
name: "project_test",
|
|
11600
11636
|
title: "Run tests",
|
|
11601
11637
|
effect: "additive",
|
|
11602
|
-
description: "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one,
|
|
11638
|
+
description: "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one, its reports under reports in the order the case met them: gdUnit4 goes on past a failed assertion, and a case can fail several and then stop on a runtime error, which makes its status error. Identical reports fold into one with times, at most twenty different ones are kept (reportsLeftOut counts the rest), and past about a megabyte the largest cases keep only their first message (failedWithoutDetail). failures counts the cases that failed an assertion and errors the cases that hit an error, so such a case counts in both. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines; identical engine messages come back once under engineEntries, with times saying how often they were printed, and engineErrors counts every one. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and does not count, and none count when the project turns off gdUnit4's report/godot/script_error setting. Needs gdUnit4 under addons/gdUnit4.",
|
|
11603
11639
|
parameters: {
|
|
11604
11640
|
projectPath: PROJECT_PATH,
|
|
11605
11641
|
path: {
|
|
@@ -11636,7 +11672,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11636
11672
|
{
|
|
11637
11673
|
name: "scene_create",
|
|
11638
11674
|
title: "Create or save a scene",
|
|
11639
|
-
description:
|
|
11675
|
+
description: `Creates a scene file where none is, saves one, or saves a copy under a new path. A save of a scene open in the editor saves the editor's copy, unsaved changes included. ${WITHOUT_AN_EDITOR}`,
|
|
11640
11676
|
parameters: {
|
|
11641
11677
|
projectPath: PROJECT_PATH,
|
|
11642
11678
|
scenePath: SCENE_PATH,
|
|
@@ -11681,17 +11717,19 @@ var init_tool_definitions = __esm(() => {
|
|
|
11681
11717
|
{
|
|
11682
11718
|
name: "scene_node",
|
|
11683
11719
|
title: "Scene nodes",
|
|
11684
|
-
description:
|
|
11720
|
+
description: `One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {"name": "Boss"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. ${WITHOUT_AN_EDITOR}`,
|
|
11685
11721
|
parameters: {
|
|
11686
11722
|
projectPath: PROJECT_PATH,
|
|
11687
11723
|
scenePath: SCENE_PATH,
|
|
11688
|
-
nodePath: NODE_PATH,
|
|
11724
|
+
nodePath: { ...NODE_PATH, ops: ["get", "set", "duplicate", "reparent", "delete", "set_tilemap_cells"] },
|
|
11689
11725
|
parentNodePath: {
|
|
11690
11726
|
type: "string",
|
|
11727
|
+
ops: ["add", "duplicate"],
|
|
11691
11728
|
description: "add: where the node goes. Default the root. duplicate: where the copy goes."
|
|
11692
11729
|
},
|
|
11693
11730
|
nodeType: {
|
|
11694
11731
|
type: "string",
|
|
11732
|
+
ops: ["add"],
|
|
11695
11733
|
description: 'add: the node class, such as "CharacterBody2D". Give this or instancePath.'
|
|
11696
11734
|
},
|
|
11697
11735
|
instancePath: {
|
|
@@ -11699,11 +11737,12 @@ var init_tool_definitions = __esm(() => {
|
|
|
11699
11737
|
ops: ["add"],
|
|
11700
11738
|
description: `add: a scene to instance as the new node instead of a class, such as "ui/panel.tscn". It is saved as an instance of that file, the way the editor's Instantiate Child Scene saves one, so changes to that scene reach it; properties set its root. A scene that is the target or instances it is refused.`
|
|
11701
11739
|
},
|
|
11702
|
-
nodeName: { type: "string", description: "add: the new node's name." },
|
|
11703
|
-
properties: PROPERTIES,
|
|
11704
|
-
newName: { type: "string", description: "duplicate: the copy's name." },
|
|
11740
|
+
nodeName: { type: "string", ops: ["add"], description: "add: the new node's name." },
|
|
11741
|
+
properties: { ...PROPERTIES, ops: ["add", "set"] },
|
|
11742
|
+
newName: { type: "string", ops: ["duplicate"], description: "duplicate: the copy's name." },
|
|
11705
11743
|
newParentPath: {
|
|
11706
11744
|
type: "string",
|
|
11745
|
+
ops: ["reparent"],
|
|
11707
11746
|
description: "reparent: the new parent, which may be the one it has, to move it among its siblings."
|
|
11708
11747
|
},
|
|
11709
11748
|
index: {
|
|
@@ -11714,13 +11753,15 @@ var init_tool_definitions = __esm(() => {
|
|
|
11714
11753
|
},
|
|
11715
11754
|
includeDefaults: {
|
|
11716
11755
|
type: "boolean",
|
|
11756
|
+
ops: ["get"],
|
|
11717
11757
|
description: "get: include properties still at their default. Default false."
|
|
11718
11758
|
},
|
|
11719
11759
|
layer: {
|
|
11720
11760
|
type: "number",
|
|
11761
|
+
ops: ["set_tilemap_cells"],
|
|
11721
11762
|
description: "set_tilemap_cells: the layer of a TileMap. Default 0. A TileMapLayer is one layer."
|
|
11722
11763
|
},
|
|
11723
|
-
cells: TILEMAP_CELLS
|
|
11764
|
+
cells: { ...TILEMAP_CELLS, ops: ["set_tilemap_cells"] }
|
|
11724
11765
|
},
|
|
11725
11766
|
requires: ["projectPath", "scenePath"],
|
|
11726
11767
|
operations: {
|
|
@@ -11760,16 +11801,32 @@ var init_tool_definitions = __esm(() => {
|
|
|
11760
11801
|
{
|
|
11761
11802
|
name: "scene_signal",
|
|
11762
11803
|
title: "Scene signals",
|
|
11763
|
-
description:
|
|
11804
|
+
description: `Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. ${WITHOUT_AN_EDITOR}`,
|
|
11764
11805
|
parameters: {
|
|
11765
11806
|
projectPath: PROJECT_PATH,
|
|
11766
11807
|
scenePath: SCENE_PATH,
|
|
11767
|
-
sourceNodePath: { type: "string", description: "The node that emits." },
|
|
11768
|
-
signalName: {
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
11772
|
-
|
|
11808
|
+
sourceNodePath: { type: "string", ops: ["connect", "disconnect"], description: "The node that emits." },
|
|
11809
|
+
signalName: {
|
|
11810
|
+
type: "string",
|
|
11811
|
+
ops: ["connect", "disconnect"],
|
|
11812
|
+
description: 'The signal it emits, such as "pressed".'
|
|
11813
|
+
},
|
|
11814
|
+
targetNodePath: {
|
|
11815
|
+
type: "string",
|
|
11816
|
+
ops: ["connect", "disconnect"],
|
|
11817
|
+
description: "The node whose method is called."
|
|
11818
|
+
},
|
|
11819
|
+
methodName: {
|
|
11820
|
+
type: "string",
|
|
11821
|
+
ops: ["connect", "disconnect"],
|
|
11822
|
+
description: "The method on that node, called when it emits."
|
|
11823
|
+
},
|
|
11824
|
+
flags: {
|
|
11825
|
+
type: "number",
|
|
11826
|
+
ops: ["connect"],
|
|
11827
|
+
description: "connect: Object.ConnectFlags, such as 1 for deferred."
|
|
11828
|
+
},
|
|
11829
|
+
nodePath: { type: "string", ops: ["list"], description: "list: only connections involving this node." }
|
|
11773
11830
|
},
|
|
11774
11831
|
requires: ["projectPath", "scenePath"],
|
|
11775
11832
|
operations: {
|
|
@@ -11894,18 +11951,28 @@ var init_tool_definitions = __esm(() => {
|
|
|
11894
11951
|
parameters: {
|
|
11895
11952
|
projectPath: PROJECT_PATH,
|
|
11896
11953
|
scriptPath: SCRIPT_PATH,
|
|
11897
|
-
className: {
|
|
11898
|
-
|
|
11954
|
+
className: {
|
|
11955
|
+
type: "string",
|
|
11956
|
+
ops: ["create"],
|
|
11957
|
+
description: "create: a class_name for the script. Not with content."
|
|
11958
|
+
},
|
|
11959
|
+
extends: {
|
|
11960
|
+
type: "string",
|
|
11961
|
+
ops: ["create"],
|
|
11962
|
+
description: "create: the base class. Default Node. Not with content."
|
|
11963
|
+
},
|
|
11899
11964
|
content: {
|
|
11900
11965
|
type: "string",
|
|
11966
|
+
ops: ["create"],
|
|
11901
11967
|
description: "create: the whole file, written as given, instead of a template, className and extends."
|
|
11902
11968
|
},
|
|
11903
11969
|
template: {
|
|
11904
11970
|
type: "string",
|
|
11905
11971
|
enum: ["singleton", "state_machine", "component", "resource"],
|
|
11972
|
+
ops: ["create"],
|
|
11906
11973
|
description: "create: a starting shape."
|
|
11907
11974
|
},
|
|
11908
|
-
modifications: SCRIPT_MODIFICATIONS,
|
|
11975
|
+
modifications: { ...SCRIPT_MODIFICATIONS, ops: ["modify"] },
|
|
11909
11976
|
symbol: {
|
|
11910
11977
|
type: "string",
|
|
11911
11978
|
ops: ["rename"],
|
|
@@ -11943,16 +12010,19 @@ var init_tool_definitions = __esm(() => {
|
|
|
11943
12010
|
scriptPath: SCRIPT_PATH,
|
|
11944
12011
|
includeInherited: {
|
|
11945
12012
|
type: "boolean",
|
|
12013
|
+
ops: ["structure"],
|
|
11946
12014
|
description: "structure: also list what the script inherits, walking extends through the project's other scripts. Each such member carries inherited_from, the file declaring it, and the answer gains inherits_from, the chain that was walked. A name the script overrides is listed at both its lines rather than once. A native base is not walked, since it declares nothing in a file: editor_classes info answers for those. Default false."
|
|
11947
12015
|
},
|
|
11948
12016
|
line: {
|
|
11949
12017
|
type: "integer",
|
|
11950
12018
|
minimum: 1,
|
|
12019
|
+
ops: ["completion", "hover"],
|
|
11951
12020
|
description: "completion, hover: the line, counted from 1 as the editor shows it."
|
|
11952
12021
|
},
|
|
11953
12022
|
character: {
|
|
11954
12023
|
type: "integer",
|
|
11955
12024
|
minimum: 1,
|
|
12025
|
+
ops: ["completion", "hover"],
|
|
11956
12026
|
description: "completion, hover: the column, counted from 1 as the editor shows it, with a tab counted as one. symbols gives each name at the line and column hover is asked at."
|
|
11957
12027
|
}
|
|
11958
12028
|
},
|
|
@@ -11960,7 +12030,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11960
12030
|
operations: {
|
|
11961
12031
|
structure: {
|
|
11962
12032
|
effect: "read",
|
|
11963
|
-
summary: "functions, variables, signals, class_name and extends, read from the file, and dependencies: the paths it loads or preloads as written, then the scripts declaring each project class_name its code uses, which classes_used names with the class",
|
|
12033
|
+
summary: "functions, variables, signals, class_name and extends, read from the file, and dependencies: the paths it loads or preloads as written, then the scripts declaring each project class_name its code uses, which classes_used names with the class; autoloads_used names each autoload the code uses by its global name, with the autoload's script, which the project loads rather than this script, so it is not under dependencies",
|
|
11964
12034
|
requires: []
|
|
11965
12035
|
},
|
|
11966
12036
|
symbols: {
|
|
@@ -11995,7 +12065,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11995
12065
|
{
|
|
11996
12066
|
name: "resource_edit",
|
|
11997
12067
|
title: "Edit resources",
|
|
11998
|
-
description: 'Resource files: read one, create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. read answers with the properties the file sets, in the form modify takes, and a Theme lists its items there as "Button/colors/font_color". move takes any file of the project, a script or an image as much as a resource, or a directory, and works from the files, so it needs no editor; one serving the project is brought up to it as after a move in its own FileSystem dock, its open scenes and cached resources named for the new paths and the project settings the move rewrote taken from the file. read needs no editor either: with none serving the project, a headless engine reads the file. The ops that write a resource
|
|
12068
|
+
description: 'Resource files: read one, create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. read answers with the properties the file sets, in the form modify takes, and a Theme lists its items there as "Button/colors/font_color". move takes any file of the project, a script or an image as much as a resource, or a directory, and works from the files, so it needs no editor; one serving the project is brought up to it as after a move in its own FileSystem dock, its open scenes and cached resources named for the new paths and the project settings the move rewrote taken from the file. read needs no editor either: with none serving the project, a headless engine reads the file. The ops that write a resource go through the editor when one serves the project; with none, a headless engine runs the tool the editor would and the answer says so under writtenBy. That engine runs the script of the resource, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before the save, and a resource where that cannot be done is refused and the file left as it was.',
|
|
11999
12069
|
parameters: {
|
|
12000
12070
|
projectPath: PROJECT_PATH,
|
|
12001
12071
|
resourcePath: {
|
|
@@ -12346,9 +12416,10 @@ var init_tool_definitions = __esm(() => {
|
|
|
12346
12416
|
description: "The engine's ClassDB: find classes, read one in full, or walk an inheritance tree.",
|
|
12347
12417
|
parameters: {
|
|
12348
12418
|
projectPath: PROJECT_PATH,
|
|
12349
|
-
filter: { type: "string", description: "query: a substring of the class name." },
|
|
12419
|
+
filter: { type: "string", ops: ["query"], description: "query: a substring of the class name." },
|
|
12350
12420
|
category: {
|
|
12351
12421
|
type: "string",
|
|
12422
|
+
ops: ["query"],
|
|
12352
12423
|
enum: [
|
|
12353
12424
|
"node",
|
|
12354
12425
|
"node2d",
|
|
@@ -12365,9 +12436,21 @@ var init_tool_definitions = __esm(() => {
|
|
|
12365
12436
|
],
|
|
12366
12437
|
description: "query: limit to one family."
|
|
12367
12438
|
},
|
|
12368
|
-
instantiableOnly: {
|
|
12369
|
-
|
|
12370
|
-
|
|
12439
|
+
instantiableOnly: {
|
|
12440
|
+
type: "boolean",
|
|
12441
|
+
ops: ["query"],
|
|
12442
|
+
description: "query: leave out abstract classes. Default false."
|
|
12443
|
+
},
|
|
12444
|
+
className: {
|
|
12445
|
+
type: "string",
|
|
12446
|
+
ops: ["info", "inheritance"],
|
|
12447
|
+
description: "info, inheritance: the class."
|
|
12448
|
+
},
|
|
12449
|
+
includeInherited: {
|
|
12450
|
+
type: "boolean",
|
|
12451
|
+
ops: ["info"],
|
|
12452
|
+
description: "info: include inherited members. Default false."
|
|
12453
|
+
},
|
|
12371
12454
|
member: {
|
|
12372
12455
|
type: "string",
|
|
12373
12456
|
ops: ["info"],
|
|
@@ -12521,7 +12604,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12521
12604
|
value: {
|
|
12522
12605
|
ops: ["set"],
|
|
12523
12606
|
blank: true,
|
|
12524
|
-
description: `set: the value, fitted to the property's type. A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}, which is the form a read answers with. "" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name.`
|
|
12607
|
+
description: `set: the value, fitted to the property's type. ${WHOLE_NUMBERS} A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}, which is the form a read answers with. "" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name.`
|
|
12525
12608
|
},
|
|
12526
12609
|
method: {
|
|
12527
12610
|
type: "string",
|
|
@@ -12531,7 +12614,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12531
12614
|
args: {
|
|
12532
12615
|
type: "array",
|
|
12533
12616
|
ops: ["call"],
|
|
12534
|
-
description: `call: the arguments, fitted to the method's parameter types. A number a parameter would have to cut is refused rather than truncated: a fraction for an int, a number other than 0 or 1 for a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, "_game:run:wares:3", or one starting at a node, "/root/Main/Hud" or "/root/Main:_game:run". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused.`
|
|
12617
|
+
description: `call: the arguments, fitted to the method's parameter types. An RID is passed as the {"_type": "RID", "id": ...} a read answers it with, such as a viewport's for RenderingServer's render timing. ${WHOLE_NUMBERS} A number a parameter would have to cut is refused rather than truncated: a fraction for an int, a number other than 0 or 1 for a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, "_game:run:wares:3", or one starting at a node, "/root/Main/Hud" or "/root/Main:_game:run". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], is built from a JSON list or object element by element, objects named by their paths. The method itself takes its arguments here only: brackets on it holding arguments are refused.`
|
|
12535
12618
|
},
|
|
12536
12619
|
properties: {
|
|
12537
12620
|
type: ["boolean", "array"],
|
|
@@ -13035,8 +13118,11 @@ function headlessSection() {
|
|
|
13035
13118
|
"had none. The reading ops are the ones to gate on, and which is which is in each",
|
|
13036
13119
|
"op's own summary below rather than assumed from this list.",
|
|
13037
13120
|
"",
|
|
13038
|
-
"
|
|
13039
|
-
"
|
|
13121
|
+
"`scene_create`, `scene_node`, `scene_signal` and `resource_edit` go through the editor when one",
|
|
13122
|
+
"serves the project and through a headless engine when none does, so they need nothing open",
|
|
13123
|
+
"either; a gate can call them, and the reads among them are the ones to gate on. Everything else",
|
|
13124
|
+
"needs the editor (`editor_*`, `debug_*`, `scene_animation` but its reads) or a running game",
|
|
13125
|
+
"(`runtime_*`).",
|
|
13040
13126
|
"",
|
|
13041
13127
|
'One of these can be pointed the other way: `project_settings get` takes `from: "editor"` to ask',
|
|
13042
13128
|
"the open editor instead of the file, which is a different reading and costs no engine start. A",
|
|
@@ -43102,6 +43188,29 @@ ${note}`;
|
|
|
43102
43188
|
return detail === entry.detail ? entry : { ...entry, detail };
|
|
43103
43189
|
});
|
|
43104
43190
|
}
|
|
43191
|
+
function shortened(detail) {
|
|
43192
|
+
if (detail === null || detail.length <= LONGEST_DETAIL) {
|
|
43193
|
+
return detail;
|
|
43194
|
+
}
|
|
43195
|
+
return `${detail.slice(0, LONGEST_DETAIL)}
|
|
43196
|
+
[${detail.length - LONGEST_DETAIL} more characters left out]`;
|
|
43197
|
+
}
|
|
43198
|
+
function foldedReports(reports) {
|
|
43199
|
+
const first = new Map;
|
|
43200
|
+
for (const report of reports) {
|
|
43201
|
+
const key = JSON.stringify([report.kind, report.message, report.detail]);
|
|
43202
|
+
const seen = first.get(key);
|
|
43203
|
+
if (seen === undefined) {
|
|
43204
|
+
first.set(key, { report, times: 1 });
|
|
43205
|
+
} else {
|
|
43206
|
+
seen.times += 1;
|
|
43207
|
+
}
|
|
43208
|
+
}
|
|
43209
|
+
return [...first.values()].map(({ report, times }) => {
|
|
43210
|
+
const kept = { ...report, detail: shortened(report.detail) };
|
|
43211
|
+
return times === 1 ? kept : { ...kept, times };
|
|
43212
|
+
});
|
|
43213
|
+
}
|
|
43105
43214
|
function failedCases(report, printed) {
|
|
43106
43215
|
const cases = report.suites.flatMap((suite) => suite.cases.filter((entry) => entry.status === "failed" || entry.status === "error").map((entry) => ({ entry, path: suite.path })));
|
|
43107
43216
|
const read = withActualsPrinted(cases.flatMap(({ entry }) => entry.reports), printed);
|
|
@@ -43109,6 +43218,8 @@ function failedCases(report, printed) {
|
|
|
43109
43218
|
return cases.map(({ entry, path }) => {
|
|
43110
43219
|
const reports = read.slice(next, next + entry.reports.length);
|
|
43111
43220
|
next += entry.reports.length;
|
|
43221
|
+
const folded = foldedReports(reports);
|
|
43222
|
+
const shown = folded.slice(0, MOST_REPORTS_SHOWN);
|
|
43112
43223
|
return {
|
|
43113
43224
|
suite: entry.suite,
|
|
43114
43225
|
name: entry.name,
|
|
@@ -43116,11 +43227,34 @@ function failedCases(report, printed) {
|
|
|
43116
43227
|
time: entry.time,
|
|
43117
43228
|
path,
|
|
43118
43229
|
message: reports[0]?.message ?? null,
|
|
43119
|
-
detail: reports[0]?.detail ?? null,
|
|
43120
|
-
...reports.length > 1 ? { reports } : {}
|
|
43230
|
+
detail: shortened(reports[0]?.detail ?? null),
|
|
43231
|
+
...reports.length > 1 ? { reports: shown, reportCount: reports.length } : {},
|
|
43232
|
+
...folded.length > shown.length ? { reportsLeftOut: folded.length - shown.length } : {}
|
|
43121
43233
|
};
|
|
43122
43234
|
});
|
|
43123
43235
|
}
|
|
43236
|
+
function failedWithin(failed, budget) {
|
|
43237
|
+
const sizes = failed.map((entry) => JSON.stringify(entry).length);
|
|
43238
|
+
const smallestFirst = sizes.map((_, index) => index).sort((a, b) => (sizes[a] ?? 0) - (sizes[b] ?? 0));
|
|
43239
|
+
const whole = new Set;
|
|
43240
|
+
let spent = 0;
|
|
43241
|
+
for (const index of smallestFirst) {
|
|
43242
|
+
const size = sizes[index] ?? 0;
|
|
43243
|
+
if (spent + size > budget) {
|
|
43244
|
+
break;
|
|
43245
|
+
}
|
|
43246
|
+
spent += size;
|
|
43247
|
+
whole.add(index);
|
|
43248
|
+
}
|
|
43249
|
+
const kept = failed.map((entry, index) => {
|
|
43250
|
+
if (whole.has(index)) {
|
|
43251
|
+
return entry;
|
|
43252
|
+
}
|
|
43253
|
+
const { detail: _detail, reports: _reports, reportsLeftOut: _left, ...named } = entry;
|
|
43254
|
+
return { ...named, message: entry.message?.slice(0, 500) ?? null, detail: null };
|
|
43255
|
+
});
|
|
43256
|
+
return { failed: kept, briefed: failed.length - whole.size };
|
|
43257
|
+
}
|
|
43124
43258
|
function masked(expected, actual) {
|
|
43125
43259
|
const allSpelled = (value) => !PRINTED_DICTIONARY.test(value) && value.split("[").length === value.split(MASKED_BRACKET).length;
|
|
43126
43260
|
return `${expected}${actual}`.includes(MASKED_BRACKET) && allSpelled(expected) && allSpelled(actual);
|
|
@@ -43199,7 +43333,7 @@ function testProgressMessage(printed, elapsedMs) {
|
|
|
43199
43333
|
const done = finished.length === 0 ? "No suite has finished" : `${finished.length === 1 ? "One suite has" : `${finished.length} suites have`} finished${failing === 0 ? "" : `, ${failing} with failures`}`;
|
|
43200
43334
|
return `${done}${running === null ? "" : `; running ${running}`}; ${took} in.`;
|
|
43201
43335
|
}
|
|
43202
|
-
var MalformedReportError, ENTITIES, ATTRIBUTE, HOOK_FAILURE, NOTHING_RAN, NO_SUCH_PATH, DISCOVERY_FAILED = "Script errors were detected during test discovery!", ESCAPE, LOCATION = `'[^'\\n]*' in \\S+:\\d+`, IGNORING_CASE = " \\(ignoring case\\)", PRINTED_ACTUAL, QUOTED_EQUALITY, COLOUR2, SPELLED, BOTH_SIDES_ALIKE_NOTE = "[gdharness: the expected and the actual printed alike because gdUnit4 drops the marks that tell them apart when it writes its report, and the runner printed no copy of this one, so neither side above is the value the assertion found.]", VALUE_NOT_RECOVERED_NOTE = '[gdharness: the value after "but was" may not be the one the assertion found. gdUnit4 writes a failing string into its report as a character diff against the expected one with the marks dropped, which merges the two, and the runner printed no copy of this one to read the value back from.]', RENDERED_AWAY_NOTE = '[gdharness: the two sides print alike, so what tells them apart is something gdUnit4 does not print. The usual cause is BBCode in the expected string, which gdUnit4 renders as formatting instead of showing; the value after "but was" is the one the assertion found.]', MASKED_BRACKET = "[lb]", PRINTED_DICTIONARY, RUNNING_SUITE, ORPHANS, OVERALL = "Overall Summary", SUITE_COUNTS, CASE_FAILED;
|
|
43336
|
+
var MalformedReportError, ENTITIES, ATTRIBUTE, HOOK_FAILURE, NOTHING_RAN, NO_SUCH_PATH, DISCOVERY_FAILED = "Script errors were detected during test discovery!", ESCAPE, LOCATION = `'[^'\\n]*' in \\S+:\\d+`, IGNORING_CASE = " \\(ignoring case\\)", PRINTED_ACTUAL, QUOTED_EQUALITY, COLOUR2, SPELLED, BOTH_SIDES_ALIKE_NOTE = "[gdharness: the expected and the actual printed alike because gdUnit4 drops the marks that tell them apart when it writes its report, and the runner printed no copy of this one, so neither side above is the value the assertion found.]", VALUE_NOT_RECOVERED_NOTE = '[gdharness: the value after "but was" may not be the one the assertion found. gdUnit4 writes a failing string into its report as a character diff against the expected one with the marks dropped, which merges the two, and the runner printed no copy of this one to read the value back from.]', RENDERED_AWAY_NOTE = '[gdharness: the two sides print alike, so what tells them apart is something gdUnit4 does not print. The usual cause is BBCode in the expected string, which gdUnit4 renders as formatting instead of showing; the value after "but was" is the one the assertion found.]', MOST_REPORTS_SHOWN = 20, LONGEST_DETAIL = 20000, MASKED_BRACKET = "[lb]", PRINTED_DICTIONARY, RUNNING_SUITE, ORPHANS, OVERALL = "Overall Summary", SUITE_COUNTS, CASE_FAILED;
|
|
43203
43337
|
var init_junit = __esm(() => {
|
|
43204
43338
|
MalformedReportError = class MalformedReportError extends Error {
|
|
43205
43339
|
constructor(message) {
|
|
@@ -44345,7 +44479,8 @@ import {
|
|
|
44345
44479
|
readFileSync as readFileSync26,
|
|
44346
44480
|
readSync as readSync2,
|
|
44347
44481
|
rmdirSync as rmdirSync2,
|
|
44348
|
-
statSync as statSync13
|
|
44482
|
+
statSync as statSync13,
|
|
44483
|
+
writeFileSync as writeFileSync18
|
|
44349
44484
|
} from "node:fs";
|
|
44350
44485
|
import { basename as basename9, dirname as dirname22, extname as extname4, isAbsolute as isAbsolute4, join as join30, normalize as normalize2, relative as relative6 } from "node:path";
|
|
44351
44486
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
@@ -45631,6 +45766,7 @@ class GodotServer {
|
|
|
45631
45766
|
inFlight = new Map;
|
|
45632
45767
|
callsOnGames = new Set;
|
|
45633
45768
|
callsTaken = 0;
|
|
45769
|
+
answersDirectory;
|
|
45634
45770
|
clientNoted = null;
|
|
45635
45771
|
parentCommand = null;
|
|
45636
45772
|
endNoted = false;
|
|
@@ -46029,12 +46165,66 @@ class GodotServer {
|
|
|
46029
46165
|
this.inFlight.set(call, { tool: spec.name, op: checked.op ?? "", startedAt: Date.now() });
|
|
46030
46166
|
try {
|
|
46031
46167
|
const answer = await withCallSignal(ctx.mcpReq.signal, () => this.answered(spec.name, checked.op ?? "", args), progress);
|
|
46032
|
-
return this.withFeedbackNotice(this.withUpdateNotice(answer));
|
|
46168
|
+
return this.withinTheClientsLimit(spec.name, this.withFeedbackNotice(this.withUpdateNotice(answer)));
|
|
46033
46169
|
} finally {
|
|
46034
46170
|
this.inFlight.delete(call);
|
|
46035
46171
|
}
|
|
46036
46172
|
});
|
|
46037
46173
|
}
|
|
46174
|
+
withinTheClientsLimit(tool, answer) {
|
|
46175
|
+
const sizeOf = (item) => item.type === "text" ? Buffer.byteLength(item.text) : item.data.length;
|
|
46176
|
+
const bytes = answer.content.reduce((sum, item) => sum + sizeOf(item), 0);
|
|
46177
|
+
if (bytes <= ANSWER_BUDGET) {
|
|
46178
|
+
return answer;
|
|
46179
|
+
}
|
|
46180
|
+
this.answersDirectory ??= scratchDirectory("answers");
|
|
46181
|
+
const directory = this.answersDirectory;
|
|
46182
|
+
const stem = `${tool}-${Date.now()}`;
|
|
46183
|
+
let savedAt;
|
|
46184
|
+
let notSaved;
|
|
46185
|
+
try {
|
|
46186
|
+
savedAt = answer.content.map((item, index) => {
|
|
46187
|
+
const file = join30(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
46188
|
+
writeFileSync18(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
46189
|
+
return file;
|
|
46190
|
+
});
|
|
46191
|
+
} catch (error) {
|
|
46192
|
+
notSaved = errorMessage(error);
|
|
46193
|
+
}
|
|
46194
|
+
const first = answer.content[0];
|
|
46195
|
+
let parsed = null;
|
|
46196
|
+
try {
|
|
46197
|
+
parsed = first?.type === "text" ? JSON.parse(first.text) : null;
|
|
46198
|
+
} catch {}
|
|
46199
|
+
const small = {};
|
|
46200
|
+
const leftOut = [];
|
|
46201
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
46202
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
46203
|
+
if (JSON.stringify(value).length <= 2000) {
|
|
46204
|
+
small[key] = value;
|
|
46205
|
+
} else {
|
|
46206
|
+
leftOut.push(key);
|
|
46207
|
+
}
|
|
46208
|
+
}
|
|
46209
|
+
}
|
|
46210
|
+
return {
|
|
46211
|
+
...answer,
|
|
46212
|
+
content: [
|
|
46213
|
+
{
|
|
46214
|
+
type: "text",
|
|
46215
|
+
text: answerJson({
|
|
46216
|
+
...small,
|
|
46217
|
+
answerTooLarge: {
|
|
46218
|
+
bytes,
|
|
46219
|
+
savedAt,
|
|
46220
|
+
leftOut: leftOut.length > 0 ? leftOut : undefined,
|
|
46221
|
+
note: savedAt === undefined ? `The answer was ${bytes} bytes, past the ${ANSWER_BUDGET} this server sends in one message (Claude Code closes the connection past 16 MB), and writing it to a file failed: ${notSaved ?? "no reason given"}. The fields of its top level that are short are kept here.` : `The answer was ${bytes} bytes, past the ${ANSWER_BUDGET} this server sends in one message (Claude Code closes the connection past 16 MB), so it was written whole to savedAt; read it from there. The fields of its top level that are short are kept here. The files are removed once this server has stopped.`
|
|
46222
|
+
}
|
|
46223
|
+
})
|
|
46224
|
+
}
|
|
46225
|
+
]
|
|
46226
|
+
};
|
|
46227
|
+
}
|
|
46038
46228
|
async answered(tool, op, args) {
|
|
46039
46229
|
try {
|
|
46040
46230
|
return await this.dispatch(tool, op, args);
|
|
@@ -46286,6 +46476,7 @@ class GodotServer {
|
|
|
46286
46476
|
const contained = this.containProjectFiles({ ...arguments_, ...extra });
|
|
46287
46477
|
return contained.ok ? await this.handleViaBridge(command, contained.value, op === "" ? tool : `${tool} ${op}`) : contained.response;
|
|
46288
46478
|
};
|
|
46479
|
+
const editorOrEngine = async (command, effect, extra = {}) => await this.editorOrEngine(command, { ...arguments_, ...extra }, `${tool} ${op}`, effect);
|
|
46289
46480
|
switch (tool) {
|
|
46290
46481
|
case "project_info":
|
|
46291
46482
|
return await this.handleProjectInfo(args);
|
|
@@ -46298,7 +46489,7 @@ class GodotServer {
|
|
|
46298
46489
|
case "project_test":
|
|
46299
46490
|
return await this.handleRunTests(args);
|
|
46300
46491
|
case "scene_create":
|
|
46301
|
-
return op === "create" ? await
|
|
46492
|
+
return op === "create" ? await editorOrEngine("create_scene", "writes") : await editorOrEngine("save_scene", "writes");
|
|
46302
46493
|
case "scene_tree":
|
|
46303
46494
|
return await this.readFromFile(args, "scenePath", "read_scene_tree", (file) => ({
|
|
46304
46495
|
scenePath: file,
|
|
@@ -46308,28 +46499,28 @@ class GodotServer {
|
|
|
46308
46499
|
case "scene_node":
|
|
46309
46500
|
switch (op) {
|
|
46310
46501
|
case "add":
|
|
46311
|
-
return await
|
|
46502
|
+
return await editorOrEngine("add_node", "writes");
|
|
46312
46503
|
case "get":
|
|
46313
|
-
return await
|
|
46504
|
+
return await editorOrEngine("get_node_properties", "reads");
|
|
46314
46505
|
case "set":
|
|
46315
|
-
return await
|
|
46506
|
+
return await editorOrEngine("set_node_properties", "writes");
|
|
46316
46507
|
case "duplicate":
|
|
46317
|
-
return await
|
|
46508
|
+
return await editorOrEngine("duplicate_node", "writes", { parentPath: args["parentNodePath"] });
|
|
46318
46509
|
case "reparent":
|
|
46319
|
-
return await
|
|
46510
|
+
return await editorOrEngine("reparent_node", "writes");
|
|
46320
46511
|
case "delete":
|
|
46321
|
-
return await
|
|
46512
|
+
return await editorOrEngine("delete_node", "writes");
|
|
46322
46513
|
default:
|
|
46323
|
-
return await
|
|
46514
|
+
return await editorOrEngine("set_tilemap_cells", "writes", { tilemapNodePath: args["nodePath"] });
|
|
46324
46515
|
}
|
|
46325
46516
|
case "scene_signal":
|
|
46326
46517
|
switch (op) {
|
|
46327
46518
|
case "connect":
|
|
46328
|
-
return await
|
|
46519
|
+
return await editorOrEngine("connect_signal", "writes");
|
|
46329
46520
|
case "disconnect":
|
|
46330
|
-
return await
|
|
46521
|
+
return await editorOrEngine("disconnect_signal", "writes");
|
|
46331
46522
|
default:
|
|
46332
|
-
return await
|
|
46523
|
+
return await editorOrEngine("list_connections", "reads");
|
|
46333
46524
|
}
|
|
46334
46525
|
case "scene_animation":
|
|
46335
46526
|
switch (op) {
|
|
@@ -46380,17 +46571,17 @@ class GodotServer {
|
|
|
46380
46571
|
}), async () => await bridge("read_resource", kept));
|
|
46381
46572
|
}
|
|
46382
46573
|
case "create":
|
|
46383
|
-
return await
|
|
46574
|
+
return await editorOrEngine("create_resource", "writes");
|
|
46384
46575
|
case "modify":
|
|
46385
|
-
return await
|
|
46576
|
+
return await editorOrEngine("modify_resource", "writes");
|
|
46386
46577
|
case "create_shader":
|
|
46387
|
-
return await
|
|
46578
|
+
return await editorOrEngine("create_shader", "writes", { shaderPath: args["resourcePath"] });
|
|
46388
46579
|
case "create_tileset":
|
|
46389
|
-
return await
|
|
46580
|
+
return await editorOrEngine("create_tileset", "writes", { tilesetPath: args["resourcePath"] });
|
|
46390
46581
|
case "set_theme_color":
|
|
46391
|
-
return await
|
|
46582
|
+
return await editorOrEngine("set_theme_color", "writes", { themePath: args["resourcePath"] });
|
|
46392
46583
|
default:
|
|
46393
|
-
return await
|
|
46584
|
+
return await editorOrEngine("set_theme_font_size", "writes", { themePath: args["resourcePath"] });
|
|
46394
46585
|
}
|
|
46395
46586
|
case "editor_launch":
|
|
46396
46587
|
if (op === "close") {
|
|
@@ -47256,6 +47447,10 @@ class GodotServer {
|
|
|
47256
47447
|
if (unknown.length > 0) {
|
|
47257
47448
|
return this.createErrorResponse(`project_info cannot include ${unknown.join(", ")}. Sections: ${Object.keys(PROJECT_INFO_SECTIONS2).join(", ")}.`);
|
|
47258
47449
|
}
|
|
47450
|
+
const forValidation = ["preset", "detail"].filter((name) => args[name] !== undefined);
|
|
47451
|
+
if (forValidation.length > 0 && !include.includes("validation")) {
|
|
47452
|
+
return this.createErrorResponse(`${forValidation.join(" and ")} ${forValidation.length === 1 ? "is" : "are"} read only by the validation section, which this call does not include: add "validation" to include, or leave ${forValidation.length === 1 ? "it" : "them"} out.`);
|
|
47453
|
+
}
|
|
47259
47454
|
const project = this.project(args);
|
|
47260
47455
|
if (!project.ok) {
|
|
47261
47456
|
return project.response;
|
|
@@ -47640,7 +47835,7 @@ class GodotServer {
|
|
|
47640
47835
|
isError: true
|
|
47641
47836
|
};
|
|
47642
47837
|
}
|
|
47643
|
-
const failed = failedCases(report, Buffer.concat(console2).toString("utf8"));
|
|
47838
|
+
const { failed, briefed } = failedWithin(failedCases(report, Buffer.concat(console2).toString("utf8")), FAILED_BUDGET);
|
|
47644
47839
|
const orphans = orphansPrinted(said);
|
|
47645
47840
|
const leftBehind = new Map(orphans.suites.map((suite) => [suite.path, suite.orphans]));
|
|
47646
47841
|
const orphansIn = (path) => path === null ? 0 : leftBehind.get(path) ?? 0;
|
|
@@ -47680,6 +47875,7 @@ class GodotServer {
|
|
|
47680
47875
|
skipped: report.skipped,
|
|
47681
47876
|
time: report.time,
|
|
47682
47877
|
failed,
|
|
47878
|
+
failedWithoutDetail: briefed > 0 ? briefed : undefined,
|
|
47683
47879
|
hookFailures: hookFailures.length > 0 ? hookFailures : undefined,
|
|
47684
47880
|
uncaughtScriptErrors: uncaughtFolded.length > 0 ? uncaughtFolded : undefined,
|
|
47685
47881
|
warnings: warnings.length > 0 ? warnings : undefined,
|
|
@@ -47691,6 +47887,7 @@ class GodotServer {
|
|
|
47691
47887
|
...scanned,
|
|
47692
47888
|
note: [
|
|
47693
47889
|
missingNote,
|
|
47890
|
+
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,
|
|
47694
47891
|
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,
|
|
47695
47892
|
uncaughtNote(uncaught),
|
|
47696
47893
|
ranAs.startsWith("warnings") && warnings.length === 0 ? "gdUnit4 exits 101 for orphan nodes when nothing failed, and this run printed no count of them: orphan reporting may be off in the project settings." : notRunNote(notRun, readBoolean(args, "failFast") === true)
|
|
@@ -47854,7 +48051,11 @@ class GodotServer {
|
|
|
47854
48051
|
};
|
|
47855
48052
|
}
|
|
47856
48053
|
markedIfStale(answer) {
|
|
47857
|
-
|
|
48054
|
+
const status = this.godotBridge.getStatus();
|
|
48055
|
+
if (!hasSaidWhoItIs(status)) {
|
|
48056
|
+
return answer;
|
|
48057
|
+
}
|
|
48058
|
+
return markIfStale(answer, status.addonVersion, SERVER_VERSION, status.addonDigest, shippedEditorDigest(), this.editorCodeOnDisk());
|
|
47858
48059
|
}
|
|
47859
48060
|
async diagnosticsOf(args) {
|
|
47860
48061
|
const waited = await this.waitForTheEditorOpenedHere(args, []);
|
|
@@ -48675,7 +48876,7 @@ class GodotServer {
|
|
|
48675
48876
|
return;
|
|
48676
48877
|
}
|
|
48677
48878
|
const connected = this.godotBridge.isConnected() ? this.godotBridge.getStatus().editorPid : undefined;
|
|
48678
|
-
const pids = editorsOfProjectIn(tree, projectPath).filter((pid) => pid !== connected);
|
|
48879
|
+
const pids = editorsOfProjectIn(tree, projectPath).filter((pid) => pid !== connected && alive(pid));
|
|
48679
48880
|
const started = await startTimesOf(pids);
|
|
48680
48881
|
return pids.map((pid) => {
|
|
48681
48882
|
const at = started.get(pid) ?? tree.get(pid)?.startedAt;
|
|
@@ -50914,6 +51115,45 @@ class GodotServer {
|
|
|
50914
51115
|
...engineExtras(outcome)
|
|
50915
51116
|
});
|
|
50916
51117
|
}
|
|
51118
|
+
async editorOrEngine(command, args, askedAs, effect) {
|
|
51119
|
+
const contained = this.containProjectFiles(args);
|
|
51120
|
+
if (!contained.ok) {
|
|
51121
|
+
return contained.response;
|
|
51122
|
+
}
|
|
51123
|
+
const project = this.project(contained.value);
|
|
51124
|
+
if (!project.ok) {
|
|
51125
|
+
return project.response;
|
|
51126
|
+
}
|
|
51127
|
+
const projectPath = project.value.path;
|
|
51128
|
+
const waited = await this.waitForTheEditorOpenedHere(contained.value, []);
|
|
51129
|
+
if (this.editorHasOpen(projectPath)) {
|
|
51130
|
+
return await this.handleViaBridge(command, contained.value, askedAs);
|
|
51131
|
+
}
|
|
51132
|
+
if (effect === "writes") {
|
|
51133
|
+
if (waited !== null) {
|
|
51134
|
+
return waited;
|
|
51135
|
+
}
|
|
51136
|
+
const arriving = await this.editorsNotYetConnected(projectPath);
|
|
51137
|
+
if (arriving !== undefined && arriving.length > 0) {
|
|
51138
|
+
const named = arriving.map((one) => `pid ${one.pid}`).join(", ");
|
|
51139
|
+
return this.createErrorResponse(`${askedAs} was not run: an editor of this project is running (${named}) and has not reached this server, and saving a scene or resource it holds would put back what a write made around it.`, [
|
|
51140
|
+
"editor_status names it under editorsNotYetConnected and says when it has connected; the write then goes through it",
|
|
51141
|
+
`If it never connects it is not reaching this server: end ${arriving.length === 1 ? "that pid" : "those pids"} and ask again`
|
|
51142
|
+
]);
|
|
51143
|
+
}
|
|
51144
|
+
}
|
|
51145
|
+
const outcome = await this.operation("run_editor_tool", { command, arguments: contained.value }, projectPath);
|
|
51146
|
+
if (!outcome.ok) {
|
|
51147
|
+
return this.answer(outcome);
|
|
51148
|
+
}
|
|
51149
|
+
return this.jsonTextResponse({
|
|
51150
|
+
...outcome.payload,
|
|
51151
|
+
...effect === "reads" ? {
|
|
51152
|
+
readBy: "a headless engine, since no editor serves this project; unsaved changes in an editor are not seen"
|
|
51153
|
+
} : { writtenBy: "a headless engine, since no editor serves this project" },
|
|
51154
|
+
...engineExtras(outcome)
|
|
51155
|
+
});
|
|
51156
|
+
}
|
|
50917
51157
|
async handleMoveFile(given) {
|
|
50918
51158
|
const project = this.project(given);
|
|
50919
51159
|
if (!project.ok) {
|
|
@@ -51583,7 +51823,7 @@ async function runGodotServer() {
|
|
|
51583
51823
|
const server = new GodotServer;
|
|
51584
51824
|
await server.run();
|
|
51585
51825
|
}
|
|
51586
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, CLOSE_ASK_MS = 3000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
51826
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, CLOSE_ASK_MS = 3000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
51587
51827
|
var init_server = __esm(() => {
|
|
51588
51828
|
init_dist();
|
|
51589
51829
|
init_stdio();
|
|
@@ -52799,9 +53039,13 @@ so take it off when the question it was for is answered.
|
|
|
52799
53039
|
|
|
52800
53040
|
\`scene_tree\` for what a scene holds, rather than what the \`.tscn\` text implies, and
|
|
52801
53041
|
\`resource_edit read\` for a resource: both read the file through the editor when one serves the
|
|
52802
|
-
project and through a headless engine when none does, with the same answer. \`
|
|
52803
|
-
\`scene_signal\` and the other \`resource_edit\` ops but \`move\` change it through the
|
|
52804
|
-
|
|
53042
|
+
project and through a headless engine when none does, with the same answer. \`scene_create\`,
|
|
53043
|
+
\`scene_node\`, \`scene_signal\` and the other \`resource_edit\` ops but \`move\` change it through the
|
|
53044
|
+
editor when one serves the project, so the files keep their formatting, and through a headless
|
|
53045
|
+
engine running the editor's own tool when none does. That engine runs the scene's scripts, which an
|
|
53046
|
+
editor runs only as tools, so the answer puts back what they changed as the file loaded, and refuses a
|
|
53047
|
+
file where it cannot, such as a scene whose script does not compile; open the editor
|
|
53048
|
+
(\`editor_launch\` with \`hidden: true\`) and ask again for those. \`script_info\` comes from the editor's own language server, and so does
|
|
52805
53049
|
\`script_diagnostics\` when this project's editor is open; without it a headless engine compiles the
|
|
52806
53050
|
scripts and reports their errors, with lines but no columns and no warnings.
|
|
52807
53051
|
|
|
@@ -52878,7 +53122,9 @@ control outside a headless viewport. Read the refusal instead of retrying the ca
|
|
|
52878
53122
|
Every answer is read back from the engine after the change rather than echoed from the request, and
|
|
52879
53123
|
whatever the engine said on stderr comes back under \`engine_messages\`. A headless engine runs the
|
|
52880
53124
|
project's autoloads after the operation has answered, and what it reports from then until it exits
|
|
52881
|
-
comes back separately, under \`engine_messages_after_answer\`: none of it changes the answer.
|
|
53125
|
+
comes back separately, under \`engine_messages_after_answer\`: none of it changes the answer. A
|
|
53126
|
+
check or write of a script waits for the autoloads to start, so the scripts naming them compile, and
|
|
53127
|
+
what they report while starting comes back under \`engine_messages_before_operation\`.
|
|
52882
53128
|
|
|
52883
53129
|
## More
|
|
52884
53130
|
|