gdharness 1.1.46 → 1.1.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 +479 -157
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +11 -5
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +1 -1
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +63 -4
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +100 -15
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +8 -1
- package/build/index.js +520 -205
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -7819,11 +7819,14 @@ function dictionary(entries) {
|
|
|
7819
7819
|
|
|
7820
7820
|
// src/call-signal.ts
|
|
7821
7821
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7822
|
-
function withCallSignal(signal, answer) {
|
|
7823
|
-
return signal === undefined ? answer() : current.run(signal, answer);
|
|
7822
|
+
function withCallSignal(signal, answer, progress) {
|
|
7823
|
+
return signal === undefined && progress === undefined ? answer() : current.run({ signal, progress }, answer);
|
|
7824
7824
|
}
|
|
7825
7825
|
function callSignal() {
|
|
7826
|
-
return current.getStore();
|
|
7826
|
+
return current.getStore()?.signal;
|
|
7827
|
+
}
|
|
7828
|
+
function callProgress() {
|
|
7829
|
+
return current.getStore()?.progress;
|
|
7827
7830
|
}
|
|
7828
7831
|
var current;
|
|
7829
7832
|
var init_call_signal = __esm(() => {
|
|
@@ -17588,7 +17591,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17588
17591
|
},
|
|
17589
17592
|
{
|
|
17590
17593
|
name: "project_test",
|
|
17591
|
-
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. 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. 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. 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. Needs gdUnit4 under addons/gdUnit4.",
|
|
17594
|
+
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. 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. 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. Needs gdUnit4 under addons/gdUnit4.",
|
|
17592
17595
|
parameters: {
|
|
17593
17596
|
projectPath: PROJECT_PATH,
|
|
17594
17597
|
path: {
|
|
@@ -18158,7 +18161,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18158
18161
|
says: {
|
|
18159
18162
|
type: "string",
|
|
18160
18163
|
ops: ["find"],
|
|
18161
|
-
description: 'find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively, which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules ("*won*|*lost*"); write \\| for a bar that is part of the words.'
|
|
18164
|
+
description: 'find: part of what the node has written on it as drawn, the way the text op reads it, case-insensitively and with a typed space matching any space the game draws (a non-breaking one included), which is how a button is reached by the word on it rather than by a generated path. Its own text, so a row is found by the label in it, and hidden nodes match. A bare word is a contains; write a glob and it is one, matched against the whole of what the node says, the same as namePattern, and a glob that finds nothing says under note how many nodes it would find open at both ends. A label with a line break is matched with the break in the words, and a backslash followed by n counts as one. Alternatives separated by | match when any one of them does, each by these rules ("*won*|*lost*"); write \\| for a bar that is part of the words.'
|
|
18162
18165
|
},
|
|
18163
18166
|
limit: {
|
|
18164
18167
|
type: "integer",
|
|
@@ -18336,7 +18339,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18336
18339
|
says: {
|
|
18337
18340
|
type: "string",
|
|
18338
18341
|
ops: ["click"],
|
|
18339
|
-
description: "click: the words on the control to click, matched the way runtime_inspect find matches says (a plain word is contained in the text, a pattern with * or ? is a glob over it, and | separates alternatives), among the controls on screen, and pressed in the same frame it was found in, so a panel rebuilding between a find and a click cannot free it first. Words on a label inside a button press the button. A control with something drawn over its centre that would take the pointer, such as a screen shown over it or a dialog stacked above its own, is not on screen; nor is one on a hidden canvas layer, one outside the viewport, or one a container clips away that a click does not scroll. The refusal counts those apart. The matches are ranked the way a person picks what to press: an enabled button before anything else, a disabled one included, then a control whose whole text, or one whole line of it such as the title on a card, is the words before one saying them as part of more, and the best one is pressed; found.picked says why. Several matches at the best rank are refused with each one listed, and index picks one. A match saying the words only as part of more is named under found.partOf, and is refused when
|
|
18342
|
+
description: "click: the words on the control to click, matched the way runtime_inspect find matches says (a plain word is contained in the text, a pattern with * or ? is a glob over it, and | separates alternatives), among the controls on screen, and pressed in the same frame it was found in, so a panel rebuilding between a find and a click cannot free it first. Words on a label inside a button press the button. A control with something drawn over its centre that would take the pointer, such as a screen shown over it or a dialog stacked above its own, is not on screen; nor is one on a hidden canvas layer, one outside the viewport, or one a container clips away that a click does not scroll. The refusal counts those apart. The matches are ranked the way a person picks what to press: an enabled button before anything else, a disabled one included, then a control whose whole text, or one whole line of it such as the title on a card, is the words before one saying them as part of more, and the best one is pressed; found.picked says why. Several matches at the best rank are refused with each one listed, and index picks one. A match saying the words only as part of more is named under found.partOf, and is refused when another control says exactly the words (hidden, off the screen, covered, or on screen taking clicks without being a button), since it would stand in for that one, and when it lets the pointer through to what is under it; index presses it anyway. Words found only inside longer words (\"out\" in \"Scout\") rank after every other match and are refused unless index is given; words starting a longer word (\"Recruit\" in \"Recruits\") count. An empty field says its placeholder, and a control with no text of its own, a picture on a button, says its accessibility name. An item of an open menu is drawn by the menu rather than held as a control, so a click finds none, and the refusal names the menu and the choose call that picks it."
|
|
18340
18343
|
},
|
|
18341
18344
|
action: {
|
|
18342
18345
|
type: "string",
|
|
@@ -18413,7 +18416,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18413
18416
|
requires: [],
|
|
18414
18417
|
operations: {
|
|
18415
18418
|
click: {
|
|
18416
|
-
summary: `press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says "the words", or "the middle of the label" with a note saying why they could not be placed, and link_pressed
|
|
18419
|
+
summary: `press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says "the words", or "the middle of the label" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room`,
|
|
18417
18420
|
requires: []
|
|
18418
18421
|
},
|
|
18419
18422
|
choose: {
|
|
@@ -34035,6 +34038,42 @@ var init_config_pin = __esm(() => {
|
|
|
34035
34038
|
PINNED = /\bgdharness@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/g;
|
|
34036
34039
|
});
|
|
34037
34040
|
|
|
34041
|
+
// src/cut-test-run.ts
|
|
34042
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync16, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
34043
|
+
import { dirname as dirname12, join as join18 } from "node:path";
|
|
34044
|
+
function keptAt(projectPath) {
|
|
34045
|
+
return join18(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
34046
|
+
}
|
|
34047
|
+
function keepCutTestRun(projectPath, run) {
|
|
34048
|
+
const path = keptAt(projectPath);
|
|
34049
|
+
try {
|
|
34050
|
+
mkdirSync9(dirname12(path), { recursive: true });
|
|
34051
|
+
writeFileSync11(path, `${JSON.stringify(run, null, 2)}
|
|
34052
|
+
`, "utf8");
|
|
34053
|
+
} catch {}
|
|
34054
|
+
}
|
|
34055
|
+
function takeCutTestRun(projectPath) {
|
|
34056
|
+
const path = keptAt(projectPath);
|
|
34057
|
+
if (!existsSync14(path)) {
|
|
34058
|
+
return null;
|
|
34059
|
+
}
|
|
34060
|
+
try {
|
|
34061
|
+
const run = JSON.parse(readFileSync16(path, "utf8"));
|
|
34062
|
+
rmSync8(path, { force: true });
|
|
34063
|
+
return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
|
|
34064
|
+
} catch {
|
|
34065
|
+
rmSync8(path, { force: true });
|
|
34066
|
+
return null;
|
|
34067
|
+
}
|
|
34068
|
+
}
|
|
34069
|
+
function cutTestRunNote(run) {
|
|
34070
|
+
const finished = run.suitesFinished?.length ?? 0;
|
|
34071
|
+
const failing = run.suitesFailing?.length ?? 0;
|
|
34072
|
+
const got = finished === 0 ? "before any suite had finished" : `after ${finished === 1 ? "one suite" : `${finished} suites`} had finished${failing === 0 ? "" : finished === 1 ? ", with failures" : `, ${failing} of them with failures`}`;
|
|
34073
|
+
return `The test run before this one in this project, of ${run.paths.join(", ")}, was cut off by its client at ${run.cutAt} ${got}${run.suiteRunning === undefined ? "" : `, while ${run.suiteRunning} was running`}; what it finished is under previousRunCut.`;
|
|
34074
|
+
}
|
|
34075
|
+
var init_cut_test_run = () => {};
|
|
34076
|
+
|
|
34038
34077
|
// src/framing.ts
|
|
34039
34078
|
function frame(message) {
|
|
34040
34079
|
const body = Buffer.from(JSON.stringify(message), "utf8");
|
|
@@ -34823,25 +34862,25 @@ var init_desktop = () => {};
|
|
|
34823
34862
|
// src/editor-log.ts
|
|
34824
34863
|
import {
|
|
34825
34864
|
closeSync,
|
|
34826
|
-
existsSync as
|
|
34827
|
-
mkdirSync as
|
|
34865
|
+
existsSync as existsSync15,
|
|
34866
|
+
mkdirSync as mkdirSync10,
|
|
34828
34867
|
openSync,
|
|
34829
|
-
readFileSync as
|
|
34868
|
+
readFileSync as readFileSync17,
|
|
34830
34869
|
readSync,
|
|
34831
|
-
rmSync as
|
|
34870
|
+
rmSync as rmSync9,
|
|
34832
34871
|
statSync as statSync7,
|
|
34833
|
-
writeFileSync as
|
|
34872
|
+
writeFileSync as writeFileSync12
|
|
34834
34873
|
} from "node:fs";
|
|
34835
|
-
import { dirname as
|
|
34874
|
+
import { dirname as dirname13, join as join19 } from "node:path";
|
|
34836
34875
|
function editorLogPath(projectPath) {
|
|
34837
|
-
return
|
|
34876
|
+
return join19(projectPath, ".godot", "gdharness-editor.log");
|
|
34838
34877
|
}
|
|
34839
34878
|
function editorLogNotePath(projectPath) {
|
|
34840
|
-
return
|
|
34879
|
+
return join19(projectPath, ".godot", "gdharness-editor.json");
|
|
34841
34880
|
}
|
|
34842
34881
|
function readEditorLogNote(projectPath) {
|
|
34843
34882
|
try {
|
|
34844
|
-
const fields = asParams(JSON.parse(
|
|
34883
|
+
const fields = asParams(JSON.parse(readFileSync17(editorLogNotePath(projectPath), "utf8")));
|
|
34845
34884
|
const pid = readNumber(fields, "pid");
|
|
34846
34885
|
const startedAt = readString(fields, "startedAt");
|
|
34847
34886
|
return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
|
|
@@ -34852,16 +34891,16 @@ function readEditorLogNote(projectPath) {
|
|
|
34852
34891
|
function clearEditorLog(projectPath) {
|
|
34853
34892
|
try {
|
|
34854
34893
|
const path = editorLogPath(projectPath);
|
|
34855
|
-
|
|
34856
|
-
|
|
34857
|
-
|
|
34894
|
+
mkdirSync10(dirname13(path), { recursive: true });
|
|
34895
|
+
rmSync9(path, { force: true });
|
|
34896
|
+
rmSync9(editorLogNotePath(projectPath), { force: true });
|
|
34858
34897
|
} catch {}
|
|
34859
34898
|
}
|
|
34860
34899
|
function writeEditorLogNote(projectPath, pid) {
|
|
34861
34900
|
try {
|
|
34862
34901
|
const path = editorLogNotePath(projectPath);
|
|
34863
|
-
|
|
34864
|
-
|
|
34902
|
+
mkdirSync10(dirname13(path), { recursive: true });
|
|
34903
|
+
writeFileSync12(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
34865
34904
|
} catch {}
|
|
34866
34905
|
}
|
|
34867
34906
|
function theConsoleWasNotCaptured(why, projectPath) {
|
|
@@ -34882,7 +34921,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
34882
34921
|
return { kind: "older server", notedPid: noted?.pid ?? null };
|
|
34883
34922
|
}
|
|
34884
34923
|
const path = editorLogPath(projectPath);
|
|
34885
|
-
if (!
|
|
34924
|
+
if (!existsSync15(path)) {
|
|
34886
34925
|
return { kind: "not written" };
|
|
34887
34926
|
}
|
|
34888
34927
|
const log = new GameLog;
|
|
@@ -34894,7 +34933,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
34894
34933
|
function theFirstOf(path) {
|
|
34895
34934
|
const whole = statSync7(path).size;
|
|
34896
34935
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
34897
|
-
return { text:
|
|
34936
|
+
return { text: readFileSync17(path, "utf8") };
|
|
34898
34937
|
}
|
|
34899
34938
|
const handle = openSync(path, "r");
|
|
34900
34939
|
try {
|
|
@@ -38708,12 +38747,16 @@ function whyNoReport(printed, asked) {
|
|
|
38708
38747
|
return printed.some((line) => NOTHING_RAN.test(line)) ? `no test cases found at ${asked}` : null;
|
|
38709
38748
|
}
|
|
38710
38749
|
function scriptErrorsPrinted(printed) {
|
|
38750
|
+
return discoveryListing(printed)?.errors ?? [];
|
|
38751
|
+
}
|
|
38752
|
+
function discoveryListing(printed) {
|
|
38711
38753
|
const start = printed.findIndex((line) => line.includes(DISCOVERY_FAILED));
|
|
38712
38754
|
if (start < 0) {
|
|
38713
|
-
return
|
|
38755
|
+
return null;
|
|
38714
38756
|
}
|
|
38715
38757
|
const found = [];
|
|
38716
38758
|
let message = null;
|
|
38759
|
+
let to = start + 1;
|
|
38717
38760
|
for (const line of printed.slice(start + 1)) {
|
|
38718
38761
|
const at = /^\s*at (res:\/\/.+):(\d+)\s*$/.exec(line);
|
|
38719
38762
|
if (at !== null && message !== null) {
|
|
@@ -38724,8 +38767,10 @@ function scriptErrorsPrinted(printed) {
|
|
|
38724
38767
|
} else {
|
|
38725
38768
|
break;
|
|
38726
38769
|
}
|
|
38770
|
+
to += 1;
|
|
38727
38771
|
}
|
|
38728
|
-
|
|
38772
|
+
const errors = found.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : (a.line ?? 0) - (b.line ?? 0));
|
|
38773
|
+
return { errors, from: start, to };
|
|
38729
38774
|
}
|
|
38730
38775
|
function unspelled(text) {
|
|
38731
38776
|
return text.replace(/<(?:LF|CR|TAB|BS|FF|VT|BEL|ESC|DEL|0x[0-9A-F]{2})>/g, (spelling) => spelling.startsWith("<0x") ? String.fromCharCode(Number.parseInt(spelling.slice(3, 5), 16)) : SPELLED[spelling] ?? spelling);
|
|
@@ -38835,7 +38880,51 @@ function orphansPrinted(printed) {
|
|
|
38835
38880
|
suites
|
|
38836
38881
|
};
|
|
38837
38882
|
}
|
|
38838
|
-
|
|
38883
|
+
function finishesASuite(line) {
|
|
38884
|
+
return SUITE_COUNTS.test(line) && !line.includes(OVERALL);
|
|
38885
|
+
}
|
|
38886
|
+
function suitesPrinted(printed) {
|
|
38887
|
+
const finished = [];
|
|
38888
|
+
let running = null;
|
|
38889
|
+
let failed = [];
|
|
38890
|
+
for (const line of printed) {
|
|
38891
|
+
const started = RUNNING_SUITE.exec(line);
|
|
38892
|
+
if (started?.[1] !== undefined) {
|
|
38893
|
+
running = started[1];
|
|
38894
|
+
failed = [];
|
|
38895
|
+
continue;
|
|
38896
|
+
}
|
|
38897
|
+
if (running === null) {
|
|
38898
|
+
continue;
|
|
38899
|
+
}
|
|
38900
|
+
const failing = CASE_FAILED.exec(line)?.[1];
|
|
38901
|
+
if (failing !== undefined) {
|
|
38902
|
+
failed.push(failing);
|
|
38903
|
+
continue;
|
|
38904
|
+
}
|
|
38905
|
+
const counted = finishesASuite(line) ? SUITE_COUNTS.exec(line) : null;
|
|
38906
|
+
if (counted !== null) {
|
|
38907
|
+
finished.push({
|
|
38908
|
+
path: running,
|
|
38909
|
+
cases: Number(counted[1]),
|
|
38910
|
+
errors: Number(counted[2]),
|
|
38911
|
+
failures: Number(counted[3]),
|
|
38912
|
+
failed
|
|
38913
|
+
});
|
|
38914
|
+
running = null;
|
|
38915
|
+
}
|
|
38916
|
+
}
|
|
38917
|
+
return { finished, running };
|
|
38918
|
+
}
|
|
38919
|
+
function testProgressMessage(printed, elapsedMs) {
|
|
38920
|
+
const { finished, running } = suitesPrinted(printed);
|
|
38921
|
+
const failing = finished.filter((suite) => suite.errors + suite.failures > 0).length;
|
|
38922
|
+
const seconds = Math.floor(elapsedMs / 1000);
|
|
38923
|
+
const took = seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
38924
|
+
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`}`;
|
|
38925
|
+
return `${done}${running === null ? "" : `; running ${running}`}; ${took} in.`;
|
|
38926
|
+
}
|
|
38927
|
+
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;
|
|
38839
38928
|
var init_junit = __esm(() => {
|
|
38840
38929
|
MalformedReportError = class MalformedReportError extends Error {
|
|
38841
38930
|
constructor(message) {
|
|
@@ -38867,6 +38956,8 @@ var init_junit = __esm(() => {
|
|
|
38867
38956
|
PRINTED_DICTIONARY = /^\{\n\t[\s\S]*\n {2}\}$/;
|
|
38868
38957
|
RUNNING_SUITE = /Run Test Suite:\s*(res:\/\/.+?\.(?:gd|cs))(?=\s|$)/;
|
|
38869
38958
|
ORPHANS = /(\d+)\s+orphans/;
|
|
38959
|
+
SUITE_COUNTS = /(\d+)\s+test cases\s*\|\s*(\d+)\s+errors\s*\|\s*(\d+)\s+failures/;
|
|
38960
|
+
CASE_FAILED = /^\s*res:\/\/.+?\.(?:gd|cs) > (.+?) (?:FAILED|ERROR)(?:\s|$)/;
|
|
38870
38961
|
});
|
|
38871
38962
|
|
|
38872
38963
|
// src/keeper-channel.ts
|
|
@@ -38912,10 +39003,10 @@ function askTheKeeperToStop(address, withinMs) {
|
|
|
38912
39003
|
var init_keeper_channel = () => {};
|
|
38913
39004
|
|
|
38914
39005
|
// src/lsp_client.ts
|
|
38915
|
-
import { existsSync as
|
|
39006
|
+
import { existsSync as existsSync16, realpathSync as realpathSync4 } from "node:fs";
|
|
38916
39007
|
import { readFile, realpath } from "node:fs/promises";
|
|
38917
39008
|
import { createConnection as createConnection3 } from "node:net";
|
|
38918
|
-
import { dirname as
|
|
39009
|
+
import { dirname as dirname14, join as join20, resolve as resolve5 } from "node:path";
|
|
38919
39010
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
38920
39011
|
function diagnosticsKey(uri) {
|
|
38921
39012
|
let decoded;
|
|
@@ -39047,7 +39138,7 @@ class GodotLSPClient {
|
|
|
39047
39138
|
if (this.initialized) {
|
|
39048
39139
|
return;
|
|
39049
39140
|
}
|
|
39050
|
-
const rootPath = this.rootPath ??
|
|
39141
|
+
const rootPath = this.rootPath ?? dirname14(resolve5(filePath));
|
|
39051
39142
|
await this.initialize(rootPath);
|
|
39052
39143
|
}
|
|
39053
39144
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -39458,7 +39549,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
39458
39549
|
} catch {
|
|
39459
39550
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
39460
39551
|
}
|
|
39461
|
-
if (!
|
|
39552
|
+
if (!existsSync16(join20(projectPath, "project.godot"))) {
|
|
39462
39553
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
39463
39554
|
}
|
|
39464
39555
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -39667,13 +39758,13 @@ var init_process_time = __esm(() => {
|
|
|
39667
39758
|
});
|
|
39668
39759
|
|
|
39669
39760
|
// src/project-scan.ts
|
|
39670
|
-
import { existsSync as
|
|
39671
|
-
import { join as
|
|
39761
|
+
import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync18 } from "node:fs";
|
|
39762
|
+
import { join as join21 } from "node:path";
|
|
39672
39763
|
function skipped(name) {
|
|
39673
39764
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
39674
39765
|
}
|
|
39675
39766
|
function steppedOver(directory) {
|
|
39676
|
-
return
|
|
39767
|
+
return existsSync17(join21(directory, ".gdignore"));
|
|
39677
39768
|
}
|
|
39678
39769
|
function projectStructure(projectPath) {
|
|
39679
39770
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -39686,7 +39777,7 @@ function projectStructure(projectPath) {
|
|
|
39686
39777
|
continue;
|
|
39687
39778
|
}
|
|
39688
39779
|
if (entry.isDirectory()) {
|
|
39689
|
-
visit(
|
|
39780
|
+
visit(join21(directory, entry.name));
|
|
39690
39781
|
} else if (entry.isFile()) {
|
|
39691
39782
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
39692
39783
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -39716,14 +39807,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
39716
39807
|
}
|
|
39717
39808
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
39718
39809
|
if (entry.isDirectory()) {
|
|
39719
|
-
visit(
|
|
39810
|
+
visit(join21(directory, entry.name), spelled);
|
|
39720
39811
|
continue;
|
|
39721
39812
|
}
|
|
39722
39813
|
if (!entry.isFile()) {
|
|
39723
39814
|
continue;
|
|
39724
39815
|
}
|
|
39725
39816
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
39726
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
39817
|
+
if (CARRIES_A_UID.has(extension) && !existsSync17(join21(directory, `${entry.name}.uid`))) {
|
|
39727
39818
|
found.push(spelled);
|
|
39728
39819
|
}
|
|
39729
39820
|
}
|
|
@@ -39744,7 +39835,7 @@ function projectTexts(projectPath) {
|
|
|
39744
39835
|
if (skipped(entry.name)) {
|
|
39745
39836
|
continue;
|
|
39746
39837
|
}
|
|
39747
|
-
const absolute =
|
|
39838
|
+
const absolute = join21(directory, entry.name);
|
|
39748
39839
|
const spelled = `${prefix}${entry.name}`;
|
|
39749
39840
|
if (entry.isDirectory()) {
|
|
39750
39841
|
visit(absolute, `${spelled}/`);
|
|
@@ -39754,7 +39845,7 @@ function projectTexts(projectPath) {
|
|
|
39754
39845
|
if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
|
|
39755
39846
|
continue;
|
|
39756
39847
|
}
|
|
39757
|
-
const bytes =
|
|
39848
|
+
const bytes = readFileSync18(absolute);
|
|
39758
39849
|
if (!isBinary(bytes)) {
|
|
39759
39850
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
39760
39851
|
}
|
|
@@ -39771,7 +39862,7 @@ function ignoredTexts(projectPath) {
|
|
|
39771
39862
|
if (skipped(entry.name)) {
|
|
39772
39863
|
continue;
|
|
39773
39864
|
}
|
|
39774
|
-
const absolute =
|
|
39865
|
+
const absolute = join21(directory, entry.name);
|
|
39775
39866
|
const spelled = `${prefix}${entry.name}`;
|
|
39776
39867
|
if (entry.isDirectory()) {
|
|
39777
39868
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -39781,7 +39872,7 @@ function ignoredTexts(projectPath) {
|
|
|
39781
39872
|
if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
|
|
39782
39873
|
continue;
|
|
39783
39874
|
}
|
|
39784
|
-
const bytes =
|
|
39875
|
+
const bytes = readFileSync18(absolute);
|
|
39785
39876
|
if (!isBinary(bytes)) {
|
|
39786
39877
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
39787
39878
|
}
|
|
@@ -39811,7 +39902,7 @@ function searchProject(projectPath, options) {
|
|
|
39811
39902
|
if (skipped(entry.name)) {
|
|
39812
39903
|
continue;
|
|
39813
39904
|
}
|
|
39814
|
-
const entryPath =
|
|
39905
|
+
const entryPath = join21(directory, entry.name);
|
|
39815
39906
|
if (entry.isDirectory()) {
|
|
39816
39907
|
visit(entryPath);
|
|
39817
39908
|
continue;
|
|
@@ -39820,7 +39911,7 @@ function searchProject(projectPath, options) {
|
|
|
39820
39911
|
if (!entry.isFile() || extensions !== null && !extensions.has(extension)) {
|
|
39821
39912
|
continue;
|
|
39822
39913
|
}
|
|
39823
|
-
const bytes =
|
|
39914
|
+
const bytes = readFileSync18(entryPath);
|
|
39824
39915
|
if (extensions === null && isBinary(bytes)) {
|
|
39825
39916
|
continue;
|
|
39826
39917
|
}
|
|
@@ -39897,8 +39988,8 @@ var init_project_scan = __esm(() => {
|
|
|
39897
39988
|
});
|
|
39898
39989
|
|
|
39899
39990
|
// src/rename.ts
|
|
39900
|
-
import { existsSync as
|
|
39901
|
-
import { dirname as
|
|
39991
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync19, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync13 } from "node:fs";
|
|
39992
|
+
import { dirname as dirname15, posix } from "node:path";
|
|
39902
39993
|
|
|
39903
39994
|
class Script {
|
|
39904
39995
|
path;
|
|
@@ -40599,25 +40690,25 @@ function rewritesOf(plan, texts, move) {
|
|
|
40599
40690
|
}
|
|
40600
40691
|
function applyRewrites(rewrites) {
|
|
40601
40692
|
for (const rewrite of rewrites) {
|
|
40602
|
-
const now =
|
|
40693
|
+
const now = readFileSync19(rewrite.absolute, "utf8");
|
|
40603
40694
|
if (now !== rewrite.before) {
|
|
40604
40695
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
40605
40696
|
}
|
|
40606
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
40697
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync18(rewrite.writeTo)) {
|
|
40607
40698
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
40608
40699
|
}
|
|
40609
40700
|
}
|
|
40610
40701
|
const done = [];
|
|
40611
40702
|
try {
|
|
40612
40703
|
for (const rewrite of rewrites) {
|
|
40613
|
-
|
|
40704
|
+
mkdirSync11(dirname15(rewrite.writeTo), { recursive: true });
|
|
40614
40705
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
40615
|
-
|
|
40706
|
+
writeFileSync13(staging, rewrite.after, "utf8");
|
|
40616
40707
|
renameSync(staging, rewrite.writeTo);
|
|
40617
40708
|
let movedUid = false;
|
|
40618
40709
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
40619
40710
|
unlinkSync2(rewrite.absolute);
|
|
40620
|
-
if (
|
|
40711
|
+
if (existsSync18(`${rewrite.absolute}.uid`)) {
|
|
40621
40712
|
renameSync(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
40622
40713
|
movedUid = true;
|
|
40623
40714
|
}
|
|
@@ -40627,7 +40718,7 @@ function applyRewrites(rewrites) {
|
|
|
40627
40718
|
} catch (error) {
|
|
40628
40719
|
for (const { rewrite, movedUid } of done.reverse()) {
|
|
40629
40720
|
try {
|
|
40630
|
-
|
|
40721
|
+
writeFileSync13(rewrite.absolute, rewrite.before, "utf8");
|
|
40631
40722
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
40632
40723
|
unlinkSync2(rewrite.writeTo);
|
|
40633
40724
|
if (movedUid) {
|
|
@@ -40663,8 +40754,8 @@ var init_rename = __esm(() => {
|
|
|
40663
40754
|
});
|
|
40664
40755
|
|
|
40665
40756
|
// src/rename-symbol.ts
|
|
40666
|
-
import { existsSync as
|
|
40667
|
-
import { basename as basename5, dirname as
|
|
40757
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync12, readdirSync as readdirSync8, realpathSync as realpathSync5, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "node:fs";
|
|
40758
|
+
import { basename as basename5, dirname as dirname16, join as join22, relative as relative4, resolve as resolve6 } from "node:path";
|
|
40668
40759
|
function resourcePathOf(projectPath, absolute) {
|
|
40669
40760
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
40670
40761
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -40677,9 +40768,9 @@ function onDiskSpelling(path) {
|
|
|
40677
40768
|
let at = resolve6(path);
|
|
40678
40769
|
for (;; ) {
|
|
40679
40770
|
try {
|
|
40680
|
-
return
|
|
40771
|
+
return join22(realpathSync5.native(at), ...missing.reverse());
|
|
40681
40772
|
} catch {
|
|
40682
|
-
const parent =
|
|
40773
|
+
const parent = dirname16(at);
|
|
40683
40774
|
if (parent === at) {
|
|
40684
40775
|
return resolve6(path);
|
|
40685
40776
|
}
|
|
@@ -40730,7 +40821,7 @@ async function renameSymbol(request, services) {
|
|
|
40730
40821
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
40731
40822
|
};
|
|
40732
40823
|
}
|
|
40733
|
-
if (
|
|
40824
|
+
if (existsSync19(request.newScriptPath)) {
|
|
40734
40825
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
40735
40826
|
}
|
|
40736
40827
|
}
|
|
@@ -40906,7 +40997,7 @@ async function renameSymbol(request, services) {
|
|
|
40906
40997
|
scripts,
|
|
40907
40998
|
from: declaringPath,
|
|
40908
40999
|
to: moveTo,
|
|
40909
|
-
exists: (path) =>
|
|
41000
|
+
exists: (path) => existsSync19(`${projectPath}/${path.slice("res://".length)}`)
|
|
40910
41001
|
}, plan);
|
|
40911
41002
|
}
|
|
40912
41003
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? null : declaringPath);
|
|
@@ -41105,15 +41196,15 @@ ${mention.kind}`;
|
|
|
41105
41196
|
};
|
|
41106
41197
|
}
|
|
41107
41198
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
41108
|
-
const directory =
|
|
41109
|
-
|
|
41199
|
+
const directory = join22(projectPath, ".godot", "gdharness-renames");
|
|
41200
|
+
mkdirSync12(directory, { recursive: true });
|
|
41110
41201
|
detailWrites += 1;
|
|
41111
|
-
const path =
|
|
41112
|
-
|
|
41202
|
+
const path = join22(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
41203
|
+
writeFileSync14(path, `${JSON.stringify(detail, null, 2)}
|
|
41113
41204
|
`, "utf8");
|
|
41114
41205
|
const kept = readdirSync8(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
41115
41206
|
for (const old of kept.slice(10)) {
|
|
41116
|
-
|
|
41207
|
+
rmSync10(join22(directory, old), { force: true });
|
|
41117
41208
|
}
|
|
41118
41209
|
return path;
|
|
41119
41210
|
}
|
|
@@ -41153,16 +41244,16 @@ var init_rename_symbol = __esm(() => {
|
|
|
41153
41244
|
});
|
|
41154
41245
|
|
|
41155
41246
|
// src/restart-note.ts
|
|
41156
|
-
import { mkdirSync as
|
|
41157
|
-
import { dirname as
|
|
41247
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync20, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
|
|
41248
|
+
import { dirname as dirname17, join as join23 } from "node:path";
|
|
41158
41249
|
function restartNotePath(projectPath) {
|
|
41159
|
-
return
|
|
41250
|
+
return join23(projectPath, ".godot", "gdharness-restart.json");
|
|
41160
41251
|
}
|
|
41161
41252
|
function noteRestartBegun(note) {
|
|
41162
41253
|
const path = restartNotePath(note.projectPath);
|
|
41163
41254
|
try {
|
|
41164
|
-
|
|
41165
|
-
|
|
41255
|
+
mkdirSync13(dirname17(path), { recursive: true });
|
|
41256
|
+
writeFileSync15(path, `${JSON.stringify(note, null, 2)}
|
|
41166
41257
|
`, "utf8");
|
|
41167
41258
|
} catch (error) {
|
|
41168
41259
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -41170,7 +41261,7 @@ function noteRestartBegun(note) {
|
|
|
41170
41261
|
}
|
|
41171
41262
|
function restartOwed(projectPath) {
|
|
41172
41263
|
try {
|
|
41173
|
-
const parsed = JSON.parse(
|
|
41264
|
+
const parsed = JSON.parse(readFileSync20(restartNotePath(projectPath), "utf8"));
|
|
41174
41265
|
if (typeof parsed !== "object" || parsed === null) {
|
|
41175
41266
|
return null;
|
|
41176
41267
|
}
|
|
@@ -41185,7 +41276,7 @@ function restartOwed(projectPath) {
|
|
|
41185
41276
|
}
|
|
41186
41277
|
function restartSettled(projectPath) {
|
|
41187
41278
|
try {
|
|
41188
|
-
|
|
41279
|
+
rmSync11(restartNotePath(projectPath), { force: true });
|
|
41189
41280
|
} catch (error) {
|
|
41190
41281
|
console.error(`[SERVER] Could not clear the restart note: ${errorMessage(error)}`);
|
|
41191
41282
|
}
|
|
@@ -41198,28 +41289,28 @@ var init_restart_note = __esm(() => {
|
|
|
41198
41289
|
import { execFile as execFile4 } from "node:child_process";
|
|
41199
41290
|
import { createHash as createHash4 } from "node:crypto";
|
|
41200
41291
|
import {
|
|
41201
|
-
mkdirSync as
|
|
41292
|
+
mkdirSync as mkdirSync14,
|
|
41202
41293
|
openSync as openSync2,
|
|
41203
41294
|
readdirSync as readdirSync9,
|
|
41204
|
-
readFileSync as
|
|
41295
|
+
readFileSync as readFileSync21,
|
|
41205
41296
|
realpathSync as realpathSync6,
|
|
41206
|
-
rmSync as
|
|
41297
|
+
rmSync as rmSync12,
|
|
41207
41298
|
statSync as statSync8,
|
|
41208
|
-
writeFileSync as
|
|
41299
|
+
writeFileSync as writeFileSync16
|
|
41209
41300
|
} from "node:fs";
|
|
41210
|
-
import { basename as basename6, dirname as
|
|
41301
|
+
import { basename as basename6, dirname as dirname18, join as join24, resolve as resolve7 } from "node:path";
|
|
41211
41302
|
import { promisify as promisify4 } from "node:util";
|
|
41212
41303
|
function runsDirectory() {
|
|
41213
|
-
return
|
|
41304
|
+
return join24(runtimeDirectory(), "runs");
|
|
41214
41305
|
}
|
|
41215
41306
|
function realAsFarAsItExists(path) {
|
|
41216
41307
|
const missing = [];
|
|
41217
41308
|
let at = path;
|
|
41218
41309
|
for (;; ) {
|
|
41219
41310
|
try {
|
|
41220
|
-
return
|
|
41311
|
+
return join24(realpathSync6.native(at), ...missing);
|
|
41221
41312
|
} catch {
|
|
41222
|
-
const parent =
|
|
41313
|
+
const parent = dirname18(at);
|
|
41223
41314
|
if (parent === at) {
|
|
41224
41315
|
return path;
|
|
41225
41316
|
}
|
|
@@ -41234,26 +41325,26 @@ function projectKey(projectPath) {
|
|
|
41234
41325
|
return createHash4("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
41235
41326
|
}
|
|
41236
41327
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
41237
|
-
return
|
|
41328
|
+
return join24(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
41238
41329
|
}
|
|
41239
41330
|
function openTranscript(startedAt) {
|
|
41240
|
-
|
|
41241
|
-
const path =
|
|
41331
|
+
mkdirSync14(runsDirectory(), { recursive: true });
|
|
41332
|
+
const path = join24(runsDirectory(), `run-${startedAt}.log`);
|
|
41242
41333
|
return { path, fd: openSync2(path, "a") };
|
|
41243
41334
|
}
|
|
41244
41335
|
function noteIsAbout(note, playing) {
|
|
41245
41336
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
41246
41337
|
}
|
|
41247
41338
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
41248
|
-
return
|
|
41339
|
+
return join24(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
41249
41340
|
}
|
|
41250
41341
|
function writeEditorRunNote(note) {
|
|
41251
|
-
|
|
41252
|
-
|
|
41342
|
+
mkdirSync14(runsDirectory(), { recursive: true });
|
|
41343
|
+
writeFileSync16(editorNotePath(note.projectPath), JSON.stringify(note, null, 2), "utf8");
|
|
41253
41344
|
}
|
|
41254
41345
|
function readEditorRunNote(projectPath) {
|
|
41255
41346
|
for (const directory of runtimeDirectories()) {
|
|
41256
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
41347
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join24(directory, "runs", "editor-run.json"));
|
|
41257
41348
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
41258
41349
|
return note;
|
|
41259
41350
|
}
|
|
@@ -41263,7 +41354,7 @@ function readEditorRunNote(projectPath) {
|
|
|
41263
41354
|
function editorNoteAt(path) {
|
|
41264
41355
|
let parsed;
|
|
41265
41356
|
try {
|
|
41266
|
-
parsed = JSON.parse(
|
|
41357
|
+
parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
41267
41358
|
} catch {
|
|
41268
41359
|
return null;
|
|
41269
41360
|
}
|
|
@@ -41287,7 +41378,7 @@ function readRunRecord(projectPath) {
|
|
|
41287
41378
|
if (own !== null) {
|
|
41288
41379
|
return own;
|
|
41289
41380
|
}
|
|
41290
|
-
const shared = recordAt(
|
|
41381
|
+
const shared = recordAt(join24(directory, "runs", SHARED_NOTE));
|
|
41291
41382
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
41292
41383
|
return shared;
|
|
41293
41384
|
}
|
|
@@ -41299,13 +41390,13 @@ function everyRunRecord() {
|
|
|
41299
41390
|
for (const directory of runtimeDirectories()) {
|
|
41300
41391
|
let entries;
|
|
41301
41392
|
try {
|
|
41302
|
-
entries = readdirSync9(
|
|
41393
|
+
entries = readdirSync9(join24(directory, "runs"));
|
|
41303
41394
|
} catch {
|
|
41304
41395
|
continue;
|
|
41305
41396
|
}
|
|
41306
41397
|
for (const entry of entries) {
|
|
41307
41398
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
41308
|
-
const record = recordAt(
|
|
41399
|
+
const record = recordAt(join24(directory, "runs", entry));
|
|
41309
41400
|
if (record !== null) {
|
|
41310
41401
|
found.push(record);
|
|
41311
41402
|
}
|
|
@@ -41317,7 +41408,7 @@ function everyRunRecord() {
|
|
|
41317
41408
|
function recordAt(path) {
|
|
41318
41409
|
let parsed;
|
|
41319
41410
|
try {
|
|
41320
|
-
parsed = JSON.parse(
|
|
41411
|
+
parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
41321
41412
|
} catch {
|
|
41322
41413
|
return null;
|
|
41323
41414
|
}
|
|
@@ -41368,7 +41459,7 @@ async function runningAs(pid) {
|
|
|
41368
41459
|
const began = await startedAt(pid, null);
|
|
41369
41460
|
const when = began === null ? {} : { startedAt: began };
|
|
41370
41461
|
if (process.platform === "linux") {
|
|
41371
|
-
const raw =
|
|
41462
|
+
const raw = readFileSync21(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
41372
41463
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
41373
41464
|
}
|
|
41374
41465
|
const args = (await execFileAsync("ps", ["-p", String(pid), "-o", "args="], { encoding: "utf8", timeout: 15000 })).stdout.trim();
|
|
@@ -41513,12 +41604,12 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
41513
41604
|
}
|
|
41514
41605
|
function clearRunRecord(projectPath) {
|
|
41515
41606
|
for (const directory of runtimeDirectories()) {
|
|
41516
|
-
const shared =
|
|
41607
|
+
const shared = join24(directory, "runs", SHARED_NOTE);
|
|
41517
41608
|
const sharedRecord = recordAt(shared);
|
|
41518
41609
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
41519
41610
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
41520
41611
|
try {
|
|
41521
|
-
|
|
41612
|
+
rmSync12(path, { force: true });
|
|
41522
41613
|
} catch {}
|
|
41523
41614
|
}
|
|
41524
41615
|
}
|
|
@@ -41543,12 +41634,12 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
41543
41634
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
41544
41635
|
continue;
|
|
41545
41636
|
}
|
|
41546
|
-
const path =
|
|
41637
|
+
const path = join24(directory, entry);
|
|
41547
41638
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
41548
41639
|
continue;
|
|
41549
41640
|
}
|
|
41550
41641
|
try {
|
|
41551
|
-
|
|
41642
|
+
rmSync12(path);
|
|
41552
41643
|
} catch {}
|
|
41553
41644
|
}
|
|
41554
41645
|
}
|
|
@@ -41562,6 +41653,104 @@ var init_run_record = __esm(() => {
|
|
|
41562
41653
|
execFileAsync = promisify4(execFile4);
|
|
41563
41654
|
});
|
|
41564
41655
|
|
|
41656
|
+
// src/script-origins.ts
|
|
41657
|
+
import { join as join25 } from "node:path";
|
|
41658
|
+
function unresolvedClassIn(message) {
|
|
41659
|
+
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
41660
|
+
}
|
|
41661
|
+
async function originsOf(errors, declared, check) {
|
|
41662
|
+
const listed = new Set(errors.map((error) => error.path));
|
|
41663
|
+
const origins = [];
|
|
41664
|
+
const unconfirmed = [];
|
|
41665
|
+
const asked = new Set;
|
|
41666
|
+
const waiting = errors.map((error) => unresolvedClassIn(error.message)).filter((name) => name !== null);
|
|
41667
|
+
while (waiting.length > 0 && asked.size < MOST_CHECKED) {
|
|
41668
|
+
const name = waiting.shift() ?? "";
|
|
41669
|
+
const path = declared.get(name);
|
|
41670
|
+
if (path === undefined || listed.has(path) || asked.has(path)) {
|
|
41671
|
+
continue;
|
|
41672
|
+
}
|
|
41673
|
+
asked.add(path);
|
|
41674
|
+
const checked = await check(path);
|
|
41675
|
+
const own = checked.errors.filter((error) => error.path === path);
|
|
41676
|
+
if (own.length === 0) {
|
|
41677
|
+
unconfirmed.push({
|
|
41678
|
+
path,
|
|
41679
|
+
declares: name,
|
|
41680
|
+
why: checked.failure === null ? "the engine's check of it found no error" : `the engine's check of it did not finish (${checked.failure})`
|
|
41681
|
+
});
|
|
41682
|
+
continue;
|
|
41683
|
+
}
|
|
41684
|
+
for (const error of own) {
|
|
41685
|
+
origins.push({ ...error, declares: name });
|
|
41686
|
+
const deeper = unresolvedClassIn(error.message);
|
|
41687
|
+
if (deeper !== null) {
|
|
41688
|
+
waiting.push(deeper);
|
|
41689
|
+
}
|
|
41690
|
+
}
|
|
41691
|
+
}
|
|
41692
|
+
return { origins, unconfirmed };
|
|
41693
|
+
}
|
|
41694
|
+
function reloadedAt(entry) {
|
|
41695
|
+
for (const line of entry.detail) {
|
|
41696
|
+
const at = RELOADED_AT.exec(line.trim());
|
|
41697
|
+
if (at !== null) {
|
|
41698
|
+
return { path: at[1] ?? "", line: Number(at[2]) === 0 ? null : Number(at[2]) };
|
|
41699
|
+
}
|
|
41700
|
+
}
|
|
41701
|
+
return null;
|
|
41702
|
+
}
|
|
41703
|
+
function checkWithEngine(godotPath, projectPath) {
|
|
41704
|
+
return async (path) => {
|
|
41705
|
+
const logDir = scratchDirectory("tests");
|
|
41706
|
+
try {
|
|
41707
|
+
const ran = await runEngine(godotPath, [
|
|
41708
|
+
"--headless",
|
|
41709
|
+
"--log-file",
|
|
41710
|
+
join25(logDir, "engine.log"),
|
|
41711
|
+
"--path",
|
|
41712
|
+
projectPath,
|
|
41713
|
+
"--check-only",
|
|
41714
|
+
"-s",
|
|
41715
|
+
path
|
|
41716
|
+
], { timeout: { ms: 60000, said: "minute" } });
|
|
41717
|
+
const errors = ran.log.select({ severity: "error", sinceLastCall: false, limit: 50 }).entries.flatMap((entry) => {
|
|
41718
|
+
const at = reloadedAt(entry);
|
|
41719
|
+
return at === null ? [] : [{ ...at, message: entry.text }];
|
|
41720
|
+
});
|
|
41721
|
+
return { errors, failure: ran.failure === null ? null : howItEnded(ran) };
|
|
41722
|
+
} finally {
|
|
41723
|
+
discard(logDir);
|
|
41724
|
+
}
|
|
41725
|
+
};
|
|
41726
|
+
}
|
|
41727
|
+
function withoutListed(entries, errors) {
|
|
41728
|
+
if (errors.length === 0) {
|
|
41729
|
+
return [...entries];
|
|
41730
|
+
}
|
|
41731
|
+
const listing = discoveryListing(entries.map((entry) => entry.text));
|
|
41732
|
+
const paths = new Set(errors.map((error) => error.path));
|
|
41733
|
+
return entries.filter((entry, at) => {
|
|
41734
|
+
if (listing !== null && at >= listing.from && at < listing.to) {
|
|
41735
|
+
return false;
|
|
41736
|
+
}
|
|
41737
|
+
const reloaded = reloadedAt(entry);
|
|
41738
|
+
if (reloaded !== null && errors.some((error) => error.path === reloaded.path && error.line === reloaded.line && error.message === entry.text)) {
|
|
41739
|
+
return false;
|
|
41740
|
+
}
|
|
41741
|
+
const failedToLoad = /^Failed to load script "(res:\/\/[^"]+)" with error "[^"]+"\.$/.exec(entry.text);
|
|
41742
|
+
return failedToLoad === null || !paths.has(failedToLoad[1] ?? "");
|
|
41743
|
+
});
|
|
41744
|
+
}
|
|
41745
|
+
var UNRESOLVED, MOST_CHECKED = 8, RELOADED_AT;
|
|
41746
|
+
var init_script_origins = __esm(() => {
|
|
41747
|
+
init_engine_run();
|
|
41748
|
+
init_junit();
|
|
41749
|
+
init_scratch();
|
|
41750
|
+
UNRESOLVED = /Could not resolve class "([A-Za-z_][A-Za-z0-9_]*)", because of a parser error/;
|
|
41751
|
+
RELOADED_AT = /GDScript::reload \((res:\/\/.+):(\d+)\)$/;
|
|
41752
|
+
});
|
|
41753
|
+
|
|
41565
41754
|
// src/server.ts
|
|
41566
41755
|
import { execFile as execFile5, spawn as spawn4 } from "node:child_process";
|
|
41567
41756
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
@@ -41570,17 +41759,17 @@ import {
|
|
|
41570
41759
|
closeSync as closeSync2,
|
|
41571
41760
|
constants,
|
|
41572
41761
|
copyFileSync,
|
|
41573
|
-
existsSync as
|
|
41762
|
+
existsSync as existsSync20,
|
|
41574
41763
|
fstatSync,
|
|
41575
|
-
mkdirSync as
|
|
41764
|
+
mkdirSync as mkdirSync15,
|
|
41576
41765
|
openSync as openSync3,
|
|
41577
41766
|
readdirSync as readdirSync10,
|
|
41578
|
-
readFileSync as
|
|
41767
|
+
readFileSync as readFileSync22,
|
|
41579
41768
|
readSync as readSync2,
|
|
41580
41769
|
rmdirSync as rmdirSync2,
|
|
41581
41770
|
statSync as statSync9
|
|
41582
41771
|
} from "node:fs";
|
|
41583
|
-
import { basename as basename7, dirname as
|
|
41772
|
+
import { basename as basename7, dirname as dirname19, extname as extname2, isAbsolute as isAbsolute3, join as join26, normalize as normalize2, relative as relative5 } from "node:path";
|
|
41584
41773
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
41585
41774
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
41586
41775
|
import { promisify as promisify5 } from "node:util";
|
|
@@ -41601,16 +41790,16 @@ function captureDestinationRefusal(outputPath, projects) {
|
|
|
41601
41790
|
if (extname2(outputPath).toLowerCase() !== ".png") {
|
|
41602
41791
|
return `outputPath ${outputPath} does not end in .png, which is what a capture is.`;
|
|
41603
41792
|
}
|
|
41604
|
-
const directory = realPathOr(
|
|
41605
|
-
if (!
|
|
41606
|
-
return `outputPath's directory ${
|
|
41793
|
+
const directory = realPathOr(dirname19(outputPath));
|
|
41794
|
+
if (!existsSync20(directory)) {
|
|
41795
|
+
return `outputPath's directory ${dirname19(outputPath)} does not exist; a capture creates no directories.`;
|
|
41607
41796
|
}
|
|
41608
|
-
const where =
|
|
41797
|
+
const where = join26(directory, basename7(outputPath));
|
|
41609
41798
|
const inside = projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
41610
41799
|
if (inside !== undefined) {
|
|
41611
41800
|
return `outputPath ${outputPath} is inside the project at ${inside}; a capture is saved outside it, so it never becomes part of the game.`;
|
|
41612
41801
|
}
|
|
41613
|
-
if (
|
|
41802
|
+
if (existsSync20(where)) {
|
|
41614
41803
|
return `outputPath ${outputPath} already exists; a capture is not written over a file. Name a new one.`;
|
|
41615
41804
|
}
|
|
41616
41805
|
return null;
|
|
@@ -42083,18 +42272,72 @@ function settingsNamedBy(op, args) {
|
|
|
42083
42272
|
}
|
|
42084
42273
|
}
|
|
42085
42274
|
function foldedScriptErrors(errors) {
|
|
42086
|
-
const
|
|
42275
|
+
const unresolved = new Map;
|
|
42276
|
+
for (const error of errors) {
|
|
42277
|
+
const name = unresolvedClassIn(error.message);
|
|
42278
|
+
if (name !== null) {
|
|
42279
|
+
unresolved.set(error.path, (unresolved.get(error.path) ?? new Set).add(name));
|
|
42280
|
+
}
|
|
42281
|
+
}
|
|
42282
|
+
const follows = (error) => {
|
|
42283
|
+
const names = unresolved.get(error.path);
|
|
42284
|
+
if (names === undefined) {
|
|
42285
|
+
return false;
|
|
42286
|
+
}
|
|
42287
|
+
const reparsed = /Could not parse global class "([A-Za-z_][A-Za-z0-9_]*)"/.exec(error.message)?.[1];
|
|
42288
|
+
return error.message.includes('the inferred type "Variant"') || reparsed !== undefined && names.has(reparsed);
|
|
42289
|
+
};
|
|
42290
|
+
const scriptErrors = errors.filter((error) => !error.message.includes(DEPENDED_FAILED) && !follows(error));
|
|
42087
42291
|
const own = new Set(scriptErrors.map((error) => error.path));
|
|
42088
42292
|
return {
|
|
42089
42293
|
scriptErrors,
|
|
42090
|
-
failedThroughDependency: [...new Set(errors.map((error) => error.path))].filter((path) => !own.has(path))
|
|
42294
|
+
failedThroughDependency: [...new Set(errors.map((error) => error.path))].filter((path) => !own.has(path)),
|
|
42295
|
+
followFromUnresolved: errors.filter(follows)
|
|
42091
42296
|
};
|
|
42092
42297
|
}
|
|
42093
42298
|
function nonEmpty(lists) {
|
|
42094
42299
|
return Object.fromEntries(Object.entries(lists).filter(([, list]) => list.length > 0));
|
|
42095
42300
|
}
|
|
42096
|
-
function
|
|
42097
|
-
const
|
|
42301
|
+
function withPreviousCut(answer, previous) {
|
|
42302
|
+
const content = answer.content.map((part) => ({ ...part }));
|
|
42303
|
+
const last = content.at(-1);
|
|
42304
|
+
let json = null;
|
|
42305
|
+
try {
|
|
42306
|
+
const parsed = JSON.parse(last?.text ?? "");
|
|
42307
|
+
json = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { ...parsed } : null;
|
|
42308
|
+
} catch {}
|
|
42309
|
+
if (last !== undefined && json !== null) {
|
|
42310
|
+
last.text = answerJson({ ...json, previousRunCut: previous });
|
|
42311
|
+
}
|
|
42312
|
+
const note = cutTestRunNote(previous);
|
|
42313
|
+
const first = content[0];
|
|
42314
|
+
if (first === undefined || first === last && json !== null || typeof first.text !== "string") {
|
|
42315
|
+
content.unshift({ type: "text", text: note });
|
|
42316
|
+
} else {
|
|
42317
|
+
first.text = `${first.text} ${note}`;
|
|
42318
|
+
}
|
|
42319
|
+
return { ...answer, content };
|
|
42320
|
+
}
|
|
42321
|
+
function finishedBeforeTheEnd(finished, failing, running) {
|
|
42322
|
+
if (finished === 0 && running === null) {
|
|
42323
|
+
return "";
|
|
42324
|
+
}
|
|
42325
|
+
const got = finished === 0 ? "no suite had finished" : `${finished === 1 ? "one suite" : `${finished} suites`} finished${failing === 0 ? "" : finished === 1 ? ", with failures" : `, ${failing} of them with failures`}`;
|
|
42326
|
+
const listed = finished === 0 ? "" : `; ${finished === 1 ? "it is" : "they are"} under suitesFinished${failing === 0 ? "" : ", with the failing cases under suitesFailing"}, so a run of the rest need not repeat ${finished === 1 ? "it" : "them"}`;
|
|
42327
|
+
return ` Before it ended, ${got}${running === null ? "" : `${finished === 0 ? " and" : ", and"} ${running} was running`}${listed}.`;
|
|
42328
|
+
}
|
|
42329
|
+
function scriptErrorsNote(errors, {
|
|
42330
|
+
origins,
|
|
42331
|
+
unconfirmed
|
|
42332
|
+
} = {
|
|
42333
|
+
origins: [],
|
|
42334
|
+
unconfirmed: []
|
|
42335
|
+
}) {
|
|
42336
|
+
const {
|
|
42337
|
+
scriptErrors: own,
|
|
42338
|
+
failedThroughDependency: dependents,
|
|
42339
|
+
followFromUnresolved: consequences
|
|
42340
|
+
} = foldedScriptErrors(errors);
|
|
42098
42341
|
const causes = new Map;
|
|
42099
42342
|
for (const error of own) {
|
|
42100
42343
|
if (!causes.has(error.path)) {
|
|
@@ -42102,16 +42345,29 @@ function scriptErrorsNote(errors) {
|
|
|
42102
42345
|
}
|
|
42103
42346
|
}
|
|
42104
42347
|
const scripts = causes.size + dependents.length;
|
|
42105
|
-
const
|
|
42348
|
+
const at = (error) => error.line === null ? error.path : `${error.path}:${error.line}`;
|
|
42349
|
+
const sentence = (text) => `${text}${/[.!?]\)?$/.test(text) ? "" : "."}`;
|
|
42350
|
+
const traced = new Set;
|
|
42351
|
+
const failedOn = (declares) => {
|
|
42352
|
+
const through = [...causes.values()].filter((error) => unresolvedClassIn(error.message) === declares);
|
|
42353
|
+
for (const error of through) {
|
|
42354
|
+
traced.add(error.path);
|
|
42355
|
+
}
|
|
42356
|
+
return through.length === 0 ? "" : ` ${through.map(at).join(", ")} could not resolve ${declares} because of it.`;
|
|
42357
|
+
};
|
|
42358
|
+
const first = [...new Map(origins.map((origin) => [origin.path, origin])).values()].sort((a, b) => Number(unresolvedClassIn(a.message) !== null) - Number(unresolvedClassIn(b.message) !== null));
|
|
42359
|
+
const named = [
|
|
42360
|
+
...first.map((origin) => `${at(origin)} ${sentence(origin.message)} gdUnit4 did not name this script, which declares ${origin.declares}; the engine's own check of it found the error.${failedOn(origin.declares)}`),
|
|
42361
|
+
...unconfirmed.map((origin) => `${origin.path} declares ${origin.declares}, which gdUnit4 did not name, and ${origin.why}.${failedOn(origin.declares)}`),
|
|
42362
|
+
...[...causes.values()].filter((error) => !traced.has(error.path)).map((error) => `${at(error)} ${sentence(error.message)}`)
|
|
42363
|
+
].join(" ");
|
|
42106
42364
|
const following = dependents.length === 0 ? "" : causes.size === 0 ? `${dependents.join(", ")} ${dependents.length === 1 ? "depends" : "depend"} on a script that did not compile, and gdUnit4 named none with an error of its own: script_diagnostics on one of them finds it.` : `${dependents.length === 1 ? "One more" : `${dependents.length} more`} failed only because a script ${dependents.length === 1 ? "it depends" : "they depend"} on did: ${dependents.join(", ")}.`;
|
|
42107
42365
|
const more = own.length - causes.size;
|
|
42108
42366
|
const rest = more > 0 ? `${more === 1 ? "One more error" : `${more} more errors`} in ${causes.size === 1 ? "that script" : "those scripts"} ${more === 1 ? "is" : "are"} under scriptErrors.` : "";
|
|
42109
|
-
|
|
42110
|
-
|
|
42111
|
-
|
|
42112
|
-
|
|
42113
|
-
rest
|
|
42114
|
-
].filter((part) => part !== "").join(" ");
|
|
42367
|
+
const after = consequences.length > 0 ? `${consequences.length === 1 ? "One more error comes" : `${consequences.length} more errors come`} only from a class that did not resolve, which the engine reads as Variant, and ${consequences.length === 1 ? "is" : "are"} under followFromUnresolved.` : "";
|
|
42368
|
+
const unnamed = first.length;
|
|
42369
|
+
const opening = unnamed === 0 ? `No tests ran: gdUnit4 could not load ${scripts === 1 ? "one script" : `${scripts} scripts`} while looking for suites, and it runs no suite at all when one fails to load.` : `No tests ran: ${scripts + unnamed} scripts did not compile, and gdUnit4 runs no suite at all when one fails to load. It could not load ${scripts === 1 ? "one" : scripts} of them while looking for suites, and ${unnamed === 1 ? "the one it did not name comes" : `the ${unnamed} it did not name come`} first.`;
|
|
42370
|
+
return [opening, named, following, rest, after].filter((part) => part !== "").join(" ");
|
|
42115
42371
|
}
|
|
42116
42372
|
function timedOutVerdict(timeoutMs, silentForMs) {
|
|
42117
42373
|
const quiet = Math.round(silentForMs);
|
|
@@ -42169,7 +42425,7 @@ function aboveTheRunner(entry) {
|
|
|
42169
42425
|
}
|
|
42170
42426
|
function elsewhereIn(projectPath, asked) {
|
|
42171
42427
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
42172
|
-
if (wanted === "" ||
|
|
42428
|
+
if (wanted === "" || existsSync20(join26(projectPath, wanted))) {
|
|
42173
42429
|
return "";
|
|
42174
42430
|
}
|
|
42175
42431
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -42213,7 +42469,7 @@ function testPathsIn(projectPath, given) {
|
|
|
42213
42469
|
};
|
|
42214
42470
|
}
|
|
42215
42471
|
function hasMainScene(projectFile) {
|
|
42216
|
-
const scene = parseProjectGodot(
|
|
42472
|
+
const scene = parseProjectGodot(readFileSync22(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
42217
42473
|
return typeof scene === "string" && scene !== "";
|
|
42218
42474
|
}
|
|
42219
42475
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -42221,13 +42477,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
42221
42477
|
return false;
|
|
42222
42478
|
}
|
|
42223
42479
|
try {
|
|
42224
|
-
return
|
|
42480
|
+
return readFileSync22(join26(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
42225
42481
|
} catch {
|
|
42226
42482
|
return false;
|
|
42227
42483
|
}
|
|
42228
42484
|
}
|
|
42229
42485
|
function editorPlaysHeadless(projectFile) {
|
|
42230
|
-
const runArgs = parseProjectGodot(
|
|
42486
|
+
const runArgs = parseProjectGodot(readFileSync22(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
42231
42487
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
42232
42488
|
}
|
|
42233
42489
|
function camelCased(params) {
|
|
@@ -42246,7 +42502,7 @@ function needsItsOwnProcess(asked) {
|
|
|
42246
42502
|
class GodotServer {
|
|
42247
42503
|
mcp;
|
|
42248
42504
|
locator = new GodotLocator;
|
|
42249
|
-
operationsScript =
|
|
42505
|
+
operationsScript = join26(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
42250
42506
|
godotBridge;
|
|
42251
42507
|
tools = buildToolDefinitions();
|
|
42252
42508
|
activeProcess = null;
|
|
@@ -42549,11 +42805,20 @@ class GodotServer {
|
|
|
42549
42805
|
return checked.response;
|
|
42550
42806
|
}
|
|
42551
42807
|
const named = args["projectPath"];
|
|
42552
|
-
if (typeof named === "string" &&
|
|
42808
|
+
if (typeof named === "string" && existsSync20(join26(named, "project.godot"))) {
|
|
42553
42809
|
this.lastProjectPath = named;
|
|
42554
42810
|
}
|
|
42555
42811
|
this.updates.refresh();
|
|
42556
|
-
const
|
|
42812
|
+
const token = request.params._meta?.progressToken;
|
|
42813
|
+
const progress = token === undefined ? undefined : (done, message) => {
|
|
42814
|
+
extra.sendNotification({
|
|
42815
|
+
method: "notifications/progress",
|
|
42816
|
+
params: { progressToken: token, progress: done, message }
|
|
42817
|
+
}).catch((error) => {
|
|
42818
|
+
this.logDebug(`Progress for ${spec.name} was not sent: ${errorMessage(error)}`);
|
|
42819
|
+
});
|
|
42820
|
+
};
|
|
42821
|
+
const answer = await withCallSignal(extra.signal, () => this.answered(spec.name, checked.op ?? "", args), progress);
|
|
42557
42822
|
return this.withFeedbackNotice(this.withUpdateNotice(answer));
|
|
42558
42823
|
});
|
|
42559
42824
|
}
|
|
@@ -43023,8 +43288,8 @@ class GodotServer {
|
|
|
43023
43288
|
if (path === undefined) {
|
|
43024
43289
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
43025
43290
|
}
|
|
43026
|
-
const file =
|
|
43027
|
-
if (!
|
|
43291
|
+
const file = join26(path, "project.godot");
|
|
43292
|
+
if (!existsSync20(file)) {
|
|
43028
43293
|
return {
|
|
43029
43294
|
ok: false,
|
|
43030
43295
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -43635,7 +43900,7 @@ class GodotServer {
|
|
|
43635
43900
|
if (!engine.ok) {
|
|
43636
43901
|
return engine.response;
|
|
43637
43902
|
}
|
|
43638
|
-
const application = parseProjectGodot(
|
|
43903
|
+
const application = parseProjectGodot(readFileSync22(project.value.file, "utf8"))["application"] ?? {};
|
|
43639
43904
|
const name = application["config/name"];
|
|
43640
43905
|
const mainScene = application["run/main_scene"];
|
|
43641
43906
|
const info = {
|
|
@@ -43691,18 +43956,18 @@ class GodotServer {
|
|
|
43691
43956
|
if (!engine.ok) {
|
|
43692
43957
|
return engine.response;
|
|
43693
43958
|
}
|
|
43694
|
-
const folder =
|
|
43959
|
+
const folder = dirname19(output.absolutePath);
|
|
43695
43960
|
try {
|
|
43696
|
-
|
|
43961
|
+
mkdirSync15(folder, { recursive: true });
|
|
43697
43962
|
} catch (error) {
|
|
43698
|
-
return this.createErrorResponse(`Cannot create ${
|
|
43963
|
+
return this.createErrorResponse(`Cannot create ${dirname19(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
43699
43964
|
}
|
|
43700
43965
|
const debug = readBoolean(args, "debug") ?? false;
|
|
43701
43966
|
const exportLogs = scratchDirectory("export");
|
|
43702
43967
|
const exportArgs = [
|
|
43703
43968
|
"--headless",
|
|
43704
43969
|
"--log-file",
|
|
43705
|
-
|
|
43970
|
+
join26(exportLogs, "engine.log"),
|
|
43706
43971
|
"--path",
|
|
43707
43972
|
project.value.path,
|
|
43708
43973
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -43710,7 +43975,7 @@ class GodotServer {
|
|
|
43710
43975
|
output.absolutePath
|
|
43711
43976
|
];
|
|
43712
43977
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
43713
|
-
const writtenAt = () =>
|
|
43978
|
+
const writtenAt = () => existsSync20(output.absolutePath) ? statSync9(output.absolutePath).mtimeMs : null;
|
|
43714
43979
|
const before = writtenAt();
|
|
43715
43980
|
let ending;
|
|
43716
43981
|
try {
|
|
@@ -43723,7 +43988,38 @@ class GodotServer {
|
|
|
43723
43988
|
}
|
|
43724
43989
|
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt());
|
|
43725
43990
|
}
|
|
43991
|
+
reportTestProgress(log) {
|
|
43992
|
+
const progress = callProgress();
|
|
43993
|
+
if (progress === undefined) {
|
|
43994
|
+
return;
|
|
43995
|
+
}
|
|
43996
|
+
const began = Date.now();
|
|
43997
|
+
let read = 0;
|
|
43998
|
+
let lastSent = began;
|
|
43999
|
+
const ticker = setInterval(() => {
|
|
44000
|
+
const entries = log.all;
|
|
44001
|
+
const finishedOne = entries.slice(read).some((entry) => finishesASuite(entry.text));
|
|
44002
|
+
read = entries.length;
|
|
44003
|
+
const now = Date.now();
|
|
44004
|
+
if (!finishedOne && now - lastSent < TEST_HEARTBEAT_MS) {
|
|
44005
|
+
return;
|
|
44006
|
+
}
|
|
44007
|
+
lastSent = now;
|
|
44008
|
+
progress(Math.floor((now - began) / 1000), testProgressMessage(entries.map((entry) => entry.text), now - began));
|
|
44009
|
+
}, 1000);
|
|
44010
|
+
ticker.unref();
|
|
44011
|
+
return ticker;
|
|
44012
|
+
}
|
|
43726
44013
|
async handleRunTests(args) {
|
|
44014
|
+
const answer = await this.runTests(args);
|
|
44015
|
+
const project = this.project(args);
|
|
44016
|
+
if (!project.ok || callSignal()?.aborted === true) {
|
|
44017
|
+
return answer;
|
|
44018
|
+
}
|
|
44019
|
+
const previous = takeCutTestRun(project.value.path);
|
|
44020
|
+
return previous === null ? answer : withPreviousCut(answer, previous);
|
|
44021
|
+
}
|
|
44022
|
+
async runTests(args) {
|
|
43727
44023
|
const project = this.project(args);
|
|
43728
44024
|
if (!project.ok) {
|
|
43729
44025
|
return project.response;
|
|
@@ -43732,7 +44028,7 @@ class GodotServer {
|
|
|
43732
44028
|
if (!paths.ok) {
|
|
43733
44029
|
return this.createErrorResponse(paths.reason, PATH_SOLUTIONS);
|
|
43734
44030
|
}
|
|
43735
|
-
const missing = paths.value.filter((path) => !
|
|
44031
|
+
const missing = paths.value.filter((path) => !existsSync20(join26(project.value.path, path.slice("res://".length))));
|
|
43736
44032
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
43737
44033
|
if (asked.length === 0) {
|
|
43738
44034
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
@@ -43748,7 +44044,7 @@ class GodotServer {
|
|
|
43748
44044
|
};
|
|
43749
44045
|
}
|
|
43750
44046
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
43751
|
-
if (!
|
|
44047
|
+
if (!existsSync20(join26(project.value.path, runner))) {
|
|
43752
44048
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
43753
44049
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
43754
44050
|
]);
|
|
@@ -43793,6 +44089,7 @@ class GodotServer {
|
|
|
43793
44089
|
run.process.stderr?.on("data", () => {
|
|
43794
44090
|
lastSaid = Date.now();
|
|
43795
44091
|
});
|
|
44092
|
+
const reporting = this.reportTestProgress(run.log);
|
|
43796
44093
|
let silentForMs = 0;
|
|
43797
44094
|
const timedOut = await new Promise((resolve) => {
|
|
43798
44095
|
const timer = setTimeout(() => {
|
|
@@ -43809,25 +44106,26 @@ class GodotServer {
|
|
|
43809
44106
|
resolve(false);
|
|
43810
44107
|
});
|
|
43811
44108
|
});
|
|
44109
|
+
clearInterval(reporting);
|
|
43812
44110
|
if (timedOut && run.process.pid !== undefined) {
|
|
43813
44111
|
await untilGone([run.process.pid]);
|
|
43814
44112
|
}
|
|
43815
44113
|
await run.settled;
|
|
43816
|
-
const reportsDir =
|
|
44114
|
+
const reportsDir = join26(project.value.path, ".godot", ...ours.split("/"));
|
|
43817
44115
|
let report = null;
|
|
43818
44116
|
let reportProblem = null;
|
|
43819
44117
|
try {
|
|
43820
|
-
const written =
|
|
44118
|
+
const written = existsSync20(reportsDir) ? readdirSync10(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
43821
44119
|
const newest = written.at(-1);
|
|
43822
44120
|
if (newest !== undefined) {
|
|
43823
|
-
report = parseJUnit(
|
|
44121
|
+
report = parseJUnit(readFileSync22(join26(reportsDir, newest, "results.xml"), "utf8"));
|
|
43824
44122
|
}
|
|
43825
44123
|
} catch (error) {
|
|
43826
44124
|
reportProblem = errorMessage(error);
|
|
43827
44125
|
} finally {
|
|
43828
44126
|
discard(reportsDir, userData);
|
|
43829
44127
|
try {
|
|
43830
|
-
rmdirSync2(
|
|
44128
|
+
rmdirSync2(dirname19(reportsDir));
|
|
43831
44129
|
} catch {}
|
|
43832
44130
|
}
|
|
43833
44131
|
const engineEntries = run.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries.map(aboveTheRunner);
|
|
@@ -43849,7 +44147,9 @@ class GodotServer {
|
|
|
43849
44147
|
const nothingRan = timedOut ? null : whyNoReport(said, asked.join(" and "));
|
|
43850
44148
|
const cutShort = timedOut ? timedOutVerdict(timeoutMs, silentForMs) : null;
|
|
43851
44149
|
const hung = cutShort?.hung ?? false;
|
|
43852
|
-
const
|
|
44150
|
+
const printedErrors = timedOut || exitCode !== 105 ? [] : scriptErrorsPrinted(said);
|
|
44151
|
+
const declaring = printedErrors.some((error) => unresolvedClassIn(error.message) !== null) ? await originsOf(printedErrors, declaredClasses(project.value.path), checkWithEngine(engine.value, project.value.path)) : { origins: [], unconfirmed: [] };
|
|
44152
|
+
const scriptErrors = [...declaring.origins, ...printedErrors];
|
|
43853
44153
|
const brokenScripts = new Set(scriptErrors.map((error) => error.path)).size;
|
|
43854
44154
|
const ranAs = cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ?? (run.exitSignal === null ? `exit ${exitCode ?? "unknown"}` : howItExited(run));
|
|
43855
44155
|
const nothingAt = missing.join(" and ");
|
|
@@ -43862,9 +44162,28 @@ class GodotServer {
|
|
|
43862
44162
|
printed: echoed.entries.map((entry) => entry.text),
|
|
43863
44163
|
...echoed.omitted > 0 ? { printedOmitted: echoed.omitted } : {}
|
|
43864
44164
|
};
|
|
44165
|
+
const sofar = report === null ? suitesPrinted(said) : null;
|
|
43865
44166
|
if (report === null) {
|
|
43866
|
-
const
|
|
44167
|
+
const finished = sofar?.finished ?? [];
|
|
44168
|
+
const running = sofar?.running ?? null;
|
|
44169
|
+
const failing = finished.filter((suite) => suite.errors + suite.failures > 0);
|
|
44170
|
+
const atTheEnd = {
|
|
44171
|
+
...nonEmpty({
|
|
44172
|
+
suitesFinished: finished.map((suite) => suite.path),
|
|
44173
|
+
suitesFailing: failing.map(({ path, errors, failures, failed }) => ({
|
|
44174
|
+
path,
|
|
44175
|
+
errors,
|
|
44176
|
+
failures,
|
|
44177
|
+
failed
|
|
44178
|
+
}))
|
|
44179
|
+
}),
|
|
44180
|
+
...running === null ? {} : { suiteRunning: running }
|
|
44181
|
+
};
|
|
44182
|
+
const said = nothingRan !== null ? `No tests ran: ${ranAs}.` : scriptErrors.length > 0 ? scriptErrorsNote(printedErrors, declaring) : reportProblem !== null ? `The test run's report could not be read (${reportProblem}); the engine's own verdict was ${ranAs}.` : `The test run wrote no report (${ranAs}).${finishedBeforeTheEnd(finished.length, failing.length, running)}`;
|
|
43867
44183
|
const note = missingNote === undefined ? said : `${said} ${missingNote}`;
|
|
44184
|
+
if (callSignal()?.aborted === true) {
|
|
44185
|
+
keepCutTestRun(project.value.path, { cutAt: new Date().toISOString(), paths: asked, ...atTheEnd });
|
|
44186
|
+
}
|
|
43868
44187
|
return {
|
|
43869
44188
|
content: [
|
|
43870
44189
|
{ type: "text", text: note },
|
|
@@ -43880,8 +44199,9 @@ class GodotServer {
|
|
|
43880
44199
|
...timedOut ? { timedOut, silentForMs } : {},
|
|
43881
44200
|
...missing.length > 0 ? { missing } : {},
|
|
43882
44201
|
...nonEmpty(foldedScriptErrors(scriptErrors)),
|
|
44202
|
+
...atTheEnd,
|
|
43883
44203
|
arguments: cmdArgs,
|
|
43884
|
-
entries: forAnswer(printed.slice(0, 60).map(aboveTheRunner)),
|
|
44204
|
+
entries: forAnswer(withoutListed(printed, scriptErrors).slice(0, 60).map(aboveTheRunner)),
|
|
43885
44205
|
...printedLines,
|
|
43886
44206
|
savesNote,
|
|
43887
44207
|
...scanned
|
|
@@ -44014,11 +44334,11 @@ class GodotServer {
|
|
|
44014
44334
|
const cached = cachedClasses(projectPath);
|
|
44015
44335
|
const contradicted = cached === null ? [] : contradictedDiagnostics(messages, cached, (resourcePath) => {
|
|
44016
44336
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
44017
|
-
if (!contained.ok || !
|
|
44337
|
+
if (!contained.ok || !existsSync20(contained.absolutePath)) {
|
|
44018
44338
|
return null;
|
|
44019
44339
|
}
|
|
44020
44340
|
try {
|
|
44021
|
-
return
|
|
44341
|
+
return readFileSync22(contained.absolutePath, "utf8");
|
|
44022
44342
|
} catch {
|
|
44023
44343
|
return null;
|
|
44024
44344
|
}
|
|
@@ -44074,11 +44394,11 @@ class GodotServer {
|
|
|
44074
44394
|
}
|
|
44075
44395
|
sourceOfClass(projectPath, resourcePath) {
|
|
44076
44396
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
44077
|
-
if (!contained.ok || !
|
|
44397
|
+
if (!contained.ok || !existsSync20(contained.absolutePath)) {
|
|
44078
44398
|
return null;
|
|
44079
44399
|
}
|
|
44080
44400
|
try {
|
|
44081
|
-
return
|
|
44401
|
+
return readFileSync22(contained.absolutePath, "utf8");
|
|
44082
44402
|
} catch {
|
|
44083
44403
|
return null;
|
|
44084
44404
|
}
|
|
@@ -44389,7 +44709,7 @@ class GodotServer {
|
|
|
44389
44709
|
return null;
|
|
44390
44710
|
}
|
|
44391
44711
|
try {
|
|
44392
|
-
return settingKeys(
|
|
44712
|
+
return settingKeys(readFileSync22(join26(projectPath, "project.godot"), "utf8"));
|
|
44393
44713
|
} catch {
|
|
44394
44714
|
return null;
|
|
44395
44715
|
}
|
|
@@ -44711,7 +45031,7 @@ class GodotServer {
|
|
|
44711
45031
|
}
|
|
44712
45032
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
44713
45033
|
const budgetMs = budget.ms;
|
|
44714
|
-
if (!
|
|
45034
|
+
if (!existsSync20(join26(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
44715
45035
|
return runtimeVerdict(null, {
|
|
44716
45036
|
addon: false,
|
|
44717
45037
|
budgetMs,
|
|
@@ -44970,7 +45290,7 @@ class GodotServer {
|
|
|
44970
45290
|
return 0;
|
|
44971
45291
|
}
|
|
44972
45292
|
try {
|
|
44973
|
-
return reportAlreadyInTranscript(
|
|
45293
|
+
return reportAlreadyInTranscript(readFileSync22(game.transcript), readFileSync22(report));
|
|
44974
45294
|
} catch (error) {
|
|
44975
45295
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
44976
45296
|
return 0;
|
|
@@ -45728,7 +46048,7 @@ class GodotServer {
|
|
|
45728
46048
|
notes.push(endedWithoutACode({
|
|
45729
46049
|
throughEditor: run.throughEditor,
|
|
45730
46050
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
45731
|
-
couldAnnounce: run.projectPath !== null &&
|
|
46051
|
+
couldAnnounce: run.projectPath !== null && existsSync20(join26(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
45732
46052
|
endedHere: typeof run.endedHere === "string",
|
|
45733
46053
|
pickedUp: run.pickedUp === true
|
|
45734
46054
|
}));
|
|
@@ -46289,7 +46609,7 @@ class GodotServer {
|
|
|
46289
46609
|
if (!scriptPath.ok) {
|
|
46290
46610
|
return this.createErrorResponse(scriptPath.reason);
|
|
46291
46611
|
}
|
|
46292
|
-
if (!
|
|
46612
|
+
if (!existsSync20(scriptPath.absolutePath)) {
|
|
46293
46613
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
46294
46614
|
}
|
|
46295
46615
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -46577,7 +46897,7 @@ class GodotServer {
|
|
|
46577
46897
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
46578
46898
|
const run = await this.currentRun();
|
|
46579
46899
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
46580
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !
|
|
46900
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync20(join26(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
46581
46901
|
return null;
|
|
46582
46902
|
}
|
|
46583
46903
|
const already = runtimesAnnounced().running;
|
|
@@ -46661,7 +46981,7 @@ class GodotServer {
|
|
|
46661
46981
|
}
|
|
46662
46982
|
}
|
|
46663
46983
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
46664
|
-
const screenshotPath = screenshotDir ?
|
|
46984
|
+
const screenshotPath = screenshotDir ? join26(screenshotDir, "capture.png") : null;
|
|
46665
46985
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
46666
46986
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
46667
46987
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
@@ -46710,7 +47030,7 @@ ${staleNote}`);
|
|
|
46710
47030
|
|
|
46711
47031
|
${staleNote}`}`
|
|
46712
47032
|
},
|
|
46713
|
-
{ type: "image", data:
|
|
47033
|
+
{ type: "image", data: readFileSync22(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
46714
47034
|
]
|
|
46715
47035
|
};
|
|
46716
47036
|
} finally {
|
|
@@ -46811,7 +47131,7 @@ async function runGodotServer() {
|
|
|
46811
47131
|
const server = new GodotServer;
|
|
46812
47132
|
await server.run();
|
|
46813
47133
|
}
|
|
46814
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, GREETING_WAIT_MS = 1e4, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, STOP_WAIT_MS = 1e4, KEEPER_ANSWER_MS = 15000, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, MOST_GROUPS = 20, 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, SCAN_WAIT_MS = 30000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
|
|
47134
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, GREETING_WAIT_MS = 1e4, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, STOP_WAIT_MS = 1e4, KEEPER_ANSWER_MS = 15000, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, MOST_GROUPS = 20, 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, SCAN_WAIT_MS = 30000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, 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, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
|
|
46815
47135
|
var init_server3 = __esm(() => {
|
|
46816
47136
|
init_mcp();
|
|
46817
47137
|
init_stdio2();
|
|
@@ -46825,6 +47145,7 @@ var init_server3 = __esm(() => {
|
|
|
46825
47145
|
init_class_cache();
|
|
46826
47146
|
init_class_note();
|
|
46827
47147
|
init_config_pin();
|
|
47148
|
+
init_cut_test_run();
|
|
46828
47149
|
init_dap_client();
|
|
46829
47150
|
init_desktop();
|
|
46830
47151
|
init_editor_log();
|
|
@@ -46852,12 +47173,13 @@ var init_server3 = __esm(() => {
|
|
|
46852
47173
|
init_run_record();
|
|
46853
47174
|
init_runtime_client();
|
|
46854
47175
|
init_scratch();
|
|
47176
|
+
init_script_origins();
|
|
46855
47177
|
init_server_version();
|
|
46856
47178
|
init_setup();
|
|
46857
47179
|
init_tool_definitions();
|
|
46858
47180
|
init_update_check();
|
|
46859
47181
|
run4 = promisify5(execFile5);
|
|
46860
|
-
__dirname2 =
|
|
47182
|
+
__dirname2 = dirname19(fileURLToPath5(import.meta.url));
|
|
46861
47183
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
46862
47184
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
46863
47185
|
VALUE_NAMES = {
|
|
@@ -46907,8 +47229,8 @@ var init_server3 = __esm(() => {
|
|
|
46907
47229
|
init_bridge_announce();
|
|
46908
47230
|
init_errors();
|
|
46909
47231
|
init_godot_path();
|
|
46910
|
-
import { existsSync as
|
|
46911
|
-
import { join as
|
|
47232
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
47233
|
+
import { join as join27, resolve as resolve8 } from "node:path";
|
|
46912
47234
|
|
|
46913
47235
|
// src/harnesses.ts
|
|
46914
47236
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -48239,7 +48561,7 @@ class UsageError extends Refusal {
|
|
|
48239
48561
|
function projectArgument(at) {
|
|
48240
48562
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
48241
48563
|
const projectPath = resolve8(named ?? ".");
|
|
48242
|
-
if (!
|
|
48564
|
+
if (!existsSync21(join27(projectPath, "project.godot"))) {
|
|
48243
48565
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
48244
48566
|
}
|
|
48245
48567
|
return projectPath;
|
|
@@ -48284,7 +48606,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
48284
48606
|
return;
|
|
48285
48607
|
}
|
|
48286
48608
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
48287
|
-
if (!
|
|
48609
|
+
if (!existsSync21(join27(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
48288
48610
|
console.log(` that file is not in this project, so it boots with a missing script: restore it, or point the entry at res://${RUNTIME_AUTOLOAD.path}`);
|
|
48289
48611
|
}
|
|
48290
48612
|
}
|
|
@@ -48416,7 +48738,7 @@ async function setup() {
|
|
|
48416
48738
|
console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
|
|
48417
48739
|
}
|
|
48418
48740
|
if (!args.includes("--no-skill")) {
|
|
48419
|
-
const directories = skillDirectories(chosen.harnesses, projectPath,
|
|
48741
|
+
const directories = skillDirectories(chosen.harnesses, projectPath, existsSync21);
|
|
48420
48742
|
for (const written of writeSkill(directories, getLocalVersion())) {
|
|
48421
48743
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
48422
48744
|
}
|
|
@@ -48530,7 +48852,7 @@ async function upgrade() {
|
|
|
48530
48852
|
moved.push(took.from);
|
|
48531
48853
|
}
|
|
48532
48854
|
}
|
|
48533
|
-
for (const written of writeSkill(skillDirectories(already, projectPath,
|
|
48855
|
+
for (const written of writeSkill(skillDirectories(already, projectPath, existsSync21), version)) {
|
|
48534
48856
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
48535
48857
|
}
|
|
48536
48858
|
console.log(`
|