gdharness 1.1.47 → 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
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. 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. 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: {
|
|
@@ -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 {
|
|
@@ -38841,7 +38880,51 @@ function orphansPrinted(printed) {
|
|
|
38841
38880
|
suites
|
|
38842
38881
|
};
|
|
38843
38882
|
}
|
|
38844
|
-
|
|
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;
|
|
38845
38928
|
var init_junit = __esm(() => {
|
|
38846
38929
|
MalformedReportError = class MalformedReportError extends Error {
|
|
38847
38930
|
constructor(message) {
|
|
@@ -38873,6 +38956,8 @@ var init_junit = __esm(() => {
|
|
|
38873
38956
|
PRINTED_DICTIONARY = /^\{\n\t[\s\S]*\n {2}\}$/;
|
|
38874
38957
|
RUNNING_SUITE = /Run Test Suite:\s*(res:\/\/.+?\.(?:gd|cs))(?=\s|$)/;
|
|
38875
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|$)/;
|
|
38876
38961
|
});
|
|
38877
38962
|
|
|
38878
38963
|
// src/keeper-channel.ts
|
|
@@ -38918,10 +39003,10 @@ function askTheKeeperToStop(address, withinMs) {
|
|
|
38918
39003
|
var init_keeper_channel = () => {};
|
|
38919
39004
|
|
|
38920
39005
|
// src/lsp_client.ts
|
|
38921
|
-
import { existsSync as
|
|
39006
|
+
import { existsSync as existsSync16, realpathSync as realpathSync4 } from "node:fs";
|
|
38922
39007
|
import { readFile, realpath } from "node:fs/promises";
|
|
38923
39008
|
import { createConnection as createConnection3 } from "node:net";
|
|
38924
|
-
import { dirname as
|
|
39009
|
+
import { dirname as dirname14, join as join20, resolve as resolve5 } from "node:path";
|
|
38925
39010
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
38926
39011
|
function diagnosticsKey(uri) {
|
|
38927
39012
|
let decoded;
|
|
@@ -39053,7 +39138,7 @@ class GodotLSPClient {
|
|
|
39053
39138
|
if (this.initialized) {
|
|
39054
39139
|
return;
|
|
39055
39140
|
}
|
|
39056
|
-
const rootPath = this.rootPath ??
|
|
39141
|
+
const rootPath = this.rootPath ?? dirname14(resolve5(filePath));
|
|
39057
39142
|
await this.initialize(rootPath);
|
|
39058
39143
|
}
|
|
39059
39144
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -39464,7 +39549,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
39464
39549
|
} catch {
|
|
39465
39550
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
39466
39551
|
}
|
|
39467
|
-
if (!
|
|
39552
|
+
if (!existsSync16(join20(projectPath, "project.godot"))) {
|
|
39468
39553
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
39469
39554
|
}
|
|
39470
39555
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -39673,13 +39758,13 @@ var init_process_time = __esm(() => {
|
|
|
39673
39758
|
});
|
|
39674
39759
|
|
|
39675
39760
|
// src/project-scan.ts
|
|
39676
|
-
import { existsSync as
|
|
39677
|
-
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";
|
|
39678
39763
|
function skipped(name) {
|
|
39679
39764
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
39680
39765
|
}
|
|
39681
39766
|
function steppedOver(directory) {
|
|
39682
|
-
return
|
|
39767
|
+
return existsSync17(join21(directory, ".gdignore"));
|
|
39683
39768
|
}
|
|
39684
39769
|
function projectStructure(projectPath) {
|
|
39685
39770
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -39692,7 +39777,7 @@ function projectStructure(projectPath) {
|
|
|
39692
39777
|
continue;
|
|
39693
39778
|
}
|
|
39694
39779
|
if (entry.isDirectory()) {
|
|
39695
|
-
visit(
|
|
39780
|
+
visit(join21(directory, entry.name));
|
|
39696
39781
|
} else if (entry.isFile()) {
|
|
39697
39782
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
39698
39783
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -39722,14 +39807,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
39722
39807
|
}
|
|
39723
39808
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
39724
39809
|
if (entry.isDirectory()) {
|
|
39725
|
-
visit(
|
|
39810
|
+
visit(join21(directory, entry.name), spelled);
|
|
39726
39811
|
continue;
|
|
39727
39812
|
}
|
|
39728
39813
|
if (!entry.isFile()) {
|
|
39729
39814
|
continue;
|
|
39730
39815
|
}
|
|
39731
39816
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
39732
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
39817
|
+
if (CARRIES_A_UID.has(extension) && !existsSync17(join21(directory, `${entry.name}.uid`))) {
|
|
39733
39818
|
found.push(spelled);
|
|
39734
39819
|
}
|
|
39735
39820
|
}
|
|
@@ -39750,7 +39835,7 @@ function projectTexts(projectPath) {
|
|
|
39750
39835
|
if (skipped(entry.name)) {
|
|
39751
39836
|
continue;
|
|
39752
39837
|
}
|
|
39753
|
-
const absolute =
|
|
39838
|
+
const absolute = join21(directory, entry.name);
|
|
39754
39839
|
const spelled = `${prefix}${entry.name}`;
|
|
39755
39840
|
if (entry.isDirectory()) {
|
|
39756
39841
|
visit(absolute, `${spelled}/`);
|
|
@@ -39760,7 +39845,7 @@ function projectTexts(projectPath) {
|
|
|
39760
39845
|
if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
|
|
39761
39846
|
continue;
|
|
39762
39847
|
}
|
|
39763
|
-
const bytes =
|
|
39848
|
+
const bytes = readFileSync18(absolute);
|
|
39764
39849
|
if (!isBinary(bytes)) {
|
|
39765
39850
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
39766
39851
|
}
|
|
@@ -39777,7 +39862,7 @@ function ignoredTexts(projectPath) {
|
|
|
39777
39862
|
if (skipped(entry.name)) {
|
|
39778
39863
|
continue;
|
|
39779
39864
|
}
|
|
39780
|
-
const absolute =
|
|
39865
|
+
const absolute = join21(directory, entry.name);
|
|
39781
39866
|
const spelled = `${prefix}${entry.name}`;
|
|
39782
39867
|
if (entry.isDirectory()) {
|
|
39783
39868
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -39787,7 +39872,7 @@ function ignoredTexts(projectPath) {
|
|
|
39787
39872
|
if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
|
|
39788
39873
|
continue;
|
|
39789
39874
|
}
|
|
39790
|
-
const bytes =
|
|
39875
|
+
const bytes = readFileSync18(absolute);
|
|
39791
39876
|
if (!isBinary(bytes)) {
|
|
39792
39877
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
39793
39878
|
}
|
|
@@ -39817,7 +39902,7 @@ function searchProject(projectPath, options) {
|
|
|
39817
39902
|
if (skipped(entry.name)) {
|
|
39818
39903
|
continue;
|
|
39819
39904
|
}
|
|
39820
|
-
const entryPath =
|
|
39905
|
+
const entryPath = join21(directory, entry.name);
|
|
39821
39906
|
if (entry.isDirectory()) {
|
|
39822
39907
|
visit(entryPath);
|
|
39823
39908
|
continue;
|
|
@@ -39826,7 +39911,7 @@ function searchProject(projectPath, options) {
|
|
|
39826
39911
|
if (!entry.isFile() || extensions !== null && !extensions.has(extension)) {
|
|
39827
39912
|
continue;
|
|
39828
39913
|
}
|
|
39829
|
-
const bytes =
|
|
39914
|
+
const bytes = readFileSync18(entryPath);
|
|
39830
39915
|
if (extensions === null && isBinary(bytes)) {
|
|
39831
39916
|
continue;
|
|
39832
39917
|
}
|
|
@@ -39903,8 +39988,8 @@ var init_project_scan = __esm(() => {
|
|
|
39903
39988
|
});
|
|
39904
39989
|
|
|
39905
39990
|
// src/rename.ts
|
|
39906
|
-
import { existsSync as
|
|
39907
|
-
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";
|
|
39908
39993
|
|
|
39909
39994
|
class Script {
|
|
39910
39995
|
path;
|
|
@@ -40605,25 +40690,25 @@ function rewritesOf(plan, texts, move) {
|
|
|
40605
40690
|
}
|
|
40606
40691
|
function applyRewrites(rewrites) {
|
|
40607
40692
|
for (const rewrite of rewrites) {
|
|
40608
|
-
const now =
|
|
40693
|
+
const now = readFileSync19(rewrite.absolute, "utf8");
|
|
40609
40694
|
if (now !== rewrite.before) {
|
|
40610
40695
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
40611
40696
|
}
|
|
40612
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
40697
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync18(rewrite.writeTo)) {
|
|
40613
40698
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
40614
40699
|
}
|
|
40615
40700
|
}
|
|
40616
40701
|
const done = [];
|
|
40617
40702
|
try {
|
|
40618
40703
|
for (const rewrite of rewrites) {
|
|
40619
|
-
|
|
40704
|
+
mkdirSync11(dirname15(rewrite.writeTo), { recursive: true });
|
|
40620
40705
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
40621
|
-
|
|
40706
|
+
writeFileSync13(staging, rewrite.after, "utf8");
|
|
40622
40707
|
renameSync(staging, rewrite.writeTo);
|
|
40623
40708
|
let movedUid = false;
|
|
40624
40709
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
40625
40710
|
unlinkSync2(rewrite.absolute);
|
|
40626
|
-
if (
|
|
40711
|
+
if (existsSync18(`${rewrite.absolute}.uid`)) {
|
|
40627
40712
|
renameSync(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
40628
40713
|
movedUid = true;
|
|
40629
40714
|
}
|
|
@@ -40633,7 +40718,7 @@ function applyRewrites(rewrites) {
|
|
|
40633
40718
|
} catch (error) {
|
|
40634
40719
|
for (const { rewrite, movedUid } of done.reverse()) {
|
|
40635
40720
|
try {
|
|
40636
|
-
|
|
40721
|
+
writeFileSync13(rewrite.absolute, rewrite.before, "utf8");
|
|
40637
40722
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
40638
40723
|
unlinkSync2(rewrite.writeTo);
|
|
40639
40724
|
if (movedUid) {
|
|
@@ -40669,8 +40754,8 @@ var init_rename = __esm(() => {
|
|
|
40669
40754
|
});
|
|
40670
40755
|
|
|
40671
40756
|
// src/rename-symbol.ts
|
|
40672
|
-
import { existsSync as
|
|
40673
|
-
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";
|
|
40674
40759
|
function resourcePathOf(projectPath, absolute) {
|
|
40675
40760
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
40676
40761
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -40683,9 +40768,9 @@ function onDiskSpelling(path) {
|
|
|
40683
40768
|
let at = resolve6(path);
|
|
40684
40769
|
for (;; ) {
|
|
40685
40770
|
try {
|
|
40686
|
-
return
|
|
40771
|
+
return join22(realpathSync5.native(at), ...missing.reverse());
|
|
40687
40772
|
} catch {
|
|
40688
|
-
const parent =
|
|
40773
|
+
const parent = dirname16(at);
|
|
40689
40774
|
if (parent === at) {
|
|
40690
40775
|
return resolve6(path);
|
|
40691
40776
|
}
|
|
@@ -40736,7 +40821,7 @@ async function renameSymbol(request, services) {
|
|
|
40736
40821
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
40737
40822
|
};
|
|
40738
40823
|
}
|
|
40739
|
-
if (
|
|
40824
|
+
if (existsSync19(request.newScriptPath)) {
|
|
40740
40825
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
40741
40826
|
}
|
|
40742
40827
|
}
|
|
@@ -40912,7 +40997,7 @@ async function renameSymbol(request, services) {
|
|
|
40912
40997
|
scripts,
|
|
40913
40998
|
from: declaringPath,
|
|
40914
40999
|
to: moveTo,
|
|
40915
|
-
exists: (path) =>
|
|
41000
|
+
exists: (path) => existsSync19(`${projectPath}/${path.slice("res://".length)}`)
|
|
40916
41001
|
}, plan);
|
|
40917
41002
|
}
|
|
40918
41003
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? null : declaringPath);
|
|
@@ -41111,15 +41196,15 @@ ${mention.kind}`;
|
|
|
41111
41196
|
};
|
|
41112
41197
|
}
|
|
41113
41198
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
41114
|
-
const directory =
|
|
41115
|
-
|
|
41199
|
+
const directory = join22(projectPath, ".godot", "gdharness-renames");
|
|
41200
|
+
mkdirSync12(directory, { recursive: true });
|
|
41116
41201
|
detailWrites += 1;
|
|
41117
|
-
const path =
|
|
41118
|
-
|
|
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)}
|
|
41119
41204
|
`, "utf8");
|
|
41120
41205
|
const kept = readdirSync8(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
41121
41206
|
for (const old of kept.slice(10)) {
|
|
41122
|
-
|
|
41207
|
+
rmSync10(join22(directory, old), { force: true });
|
|
41123
41208
|
}
|
|
41124
41209
|
return path;
|
|
41125
41210
|
}
|
|
@@ -41159,16 +41244,16 @@ var init_rename_symbol = __esm(() => {
|
|
|
41159
41244
|
});
|
|
41160
41245
|
|
|
41161
41246
|
// src/restart-note.ts
|
|
41162
|
-
import { mkdirSync as
|
|
41163
|
-
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";
|
|
41164
41249
|
function restartNotePath(projectPath) {
|
|
41165
|
-
return
|
|
41250
|
+
return join23(projectPath, ".godot", "gdharness-restart.json");
|
|
41166
41251
|
}
|
|
41167
41252
|
function noteRestartBegun(note) {
|
|
41168
41253
|
const path = restartNotePath(note.projectPath);
|
|
41169
41254
|
try {
|
|
41170
|
-
|
|
41171
|
-
|
|
41255
|
+
mkdirSync13(dirname17(path), { recursive: true });
|
|
41256
|
+
writeFileSync15(path, `${JSON.stringify(note, null, 2)}
|
|
41172
41257
|
`, "utf8");
|
|
41173
41258
|
} catch (error) {
|
|
41174
41259
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -41176,7 +41261,7 @@ function noteRestartBegun(note) {
|
|
|
41176
41261
|
}
|
|
41177
41262
|
function restartOwed(projectPath) {
|
|
41178
41263
|
try {
|
|
41179
|
-
const parsed = JSON.parse(
|
|
41264
|
+
const parsed = JSON.parse(readFileSync20(restartNotePath(projectPath), "utf8"));
|
|
41180
41265
|
if (typeof parsed !== "object" || parsed === null) {
|
|
41181
41266
|
return null;
|
|
41182
41267
|
}
|
|
@@ -41191,7 +41276,7 @@ function restartOwed(projectPath) {
|
|
|
41191
41276
|
}
|
|
41192
41277
|
function restartSettled(projectPath) {
|
|
41193
41278
|
try {
|
|
41194
|
-
|
|
41279
|
+
rmSync11(restartNotePath(projectPath), { force: true });
|
|
41195
41280
|
} catch (error) {
|
|
41196
41281
|
console.error(`[SERVER] Could not clear the restart note: ${errorMessage(error)}`);
|
|
41197
41282
|
}
|
|
@@ -41204,28 +41289,28 @@ var init_restart_note = __esm(() => {
|
|
|
41204
41289
|
import { execFile as execFile4 } from "node:child_process";
|
|
41205
41290
|
import { createHash as createHash4 } from "node:crypto";
|
|
41206
41291
|
import {
|
|
41207
|
-
mkdirSync as
|
|
41292
|
+
mkdirSync as mkdirSync14,
|
|
41208
41293
|
openSync as openSync2,
|
|
41209
41294
|
readdirSync as readdirSync9,
|
|
41210
|
-
readFileSync as
|
|
41295
|
+
readFileSync as readFileSync21,
|
|
41211
41296
|
realpathSync as realpathSync6,
|
|
41212
|
-
rmSync as
|
|
41297
|
+
rmSync as rmSync12,
|
|
41213
41298
|
statSync as statSync8,
|
|
41214
|
-
writeFileSync as
|
|
41299
|
+
writeFileSync as writeFileSync16
|
|
41215
41300
|
} from "node:fs";
|
|
41216
|
-
import { basename as basename6, dirname as
|
|
41301
|
+
import { basename as basename6, dirname as dirname18, join as join24, resolve as resolve7 } from "node:path";
|
|
41217
41302
|
import { promisify as promisify4 } from "node:util";
|
|
41218
41303
|
function runsDirectory() {
|
|
41219
|
-
return
|
|
41304
|
+
return join24(runtimeDirectory(), "runs");
|
|
41220
41305
|
}
|
|
41221
41306
|
function realAsFarAsItExists(path) {
|
|
41222
41307
|
const missing = [];
|
|
41223
41308
|
let at = path;
|
|
41224
41309
|
for (;; ) {
|
|
41225
41310
|
try {
|
|
41226
|
-
return
|
|
41311
|
+
return join24(realpathSync6.native(at), ...missing);
|
|
41227
41312
|
} catch {
|
|
41228
|
-
const parent =
|
|
41313
|
+
const parent = dirname18(at);
|
|
41229
41314
|
if (parent === at) {
|
|
41230
41315
|
return path;
|
|
41231
41316
|
}
|
|
@@ -41240,26 +41325,26 @@ function projectKey(projectPath) {
|
|
|
41240
41325
|
return createHash4("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
41241
41326
|
}
|
|
41242
41327
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
41243
|
-
return
|
|
41328
|
+
return join24(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
41244
41329
|
}
|
|
41245
41330
|
function openTranscript(startedAt) {
|
|
41246
|
-
|
|
41247
|
-
const path =
|
|
41331
|
+
mkdirSync14(runsDirectory(), { recursive: true });
|
|
41332
|
+
const path = join24(runsDirectory(), `run-${startedAt}.log`);
|
|
41248
41333
|
return { path, fd: openSync2(path, "a") };
|
|
41249
41334
|
}
|
|
41250
41335
|
function noteIsAbout(note, playing) {
|
|
41251
41336
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
41252
41337
|
}
|
|
41253
41338
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
41254
|
-
return
|
|
41339
|
+
return join24(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
41255
41340
|
}
|
|
41256
41341
|
function writeEditorRunNote(note) {
|
|
41257
|
-
|
|
41258
|
-
|
|
41342
|
+
mkdirSync14(runsDirectory(), { recursive: true });
|
|
41343
|
+
writeFileSync16(editorNotePath(note.projectPath), JSON.stringify(note, null, 2), "utf8");
|
|
41259
41344
|
}
|
|
41260
41345
|
function readEditorRunNote(projectPath) {
|
|
41261
41346
|
for (const directory of runtimeDirectories()) {
|
|
41262
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
41347
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join24(directory, "runs", "editor-run.json"));
|
|
41263
41348
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
41264
41349
|
return note;
|
|
41265
41350
|
}
|
|
@@ -41269,7 +41354,7 @@ function readEditorRunNote(projectPath) {
|
|
|
41269
41354
|
function editorNoteAt(path) {
|
|
41270
41355
|
let parsed;
|
|
41271
41356
|
try {
|
|
41272
|
-
parsed = JSON.parse(
|
|
41357
|
+
parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
41273
41358
|
} catch {
|
|
41274
41359
|
return null;
|
|
41275
41360
|
}
|
|
@@ -41293,7 +41378,7 @@ function readRunRecord(projectPath) {
|
|
|
41293
41378
|
if (own !== null) {
|
|
41294
41379
|
return own;
|
|
41295
41380
|
}
|
|
41296
|
-
const shared = recordAt(
|
|
41381
|
+
const shared = recordAt(join24(directory, "runs", SHARED_NOTE));
|
|
41297
41382
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
41298
41383
|
return shared;
|
|
41299
41384
|
}
|
|
@@ -41305,13 +41390,13 @@ function everyRunRecord() {
|
|
|
41305
41390
|
for (const directory of runtimeDirectories()) {
|
|
41306
41391
|
let entries;
|
|
41307
41392
|
try {
|
|
41308
|
-
entries = readdirSync9(
|
|
41393
|
+
entries = readdirSync9(join24(directory, "runs"));
|
|
41309
41394
|
} catch {
|
|
41310
41395
|
continue;
|
|
41311
41396
|
}
|
|
41312
41397
|
for (const entry of entries) {
|
|
41313
41398
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
41314
|
-
const record = recordAt(
|
|
41399
|
+
const record = recordAt(join24(directory, "runs", entry));
|
|
41315
41400
|
if (record !== null) {
|
|
41316
41401
|
found.push(record);
|
|
41317
41402
|
}
|
|
@@ -41323,7 +41408,7 @@ function everyRunRecord() {
|
|
|
41323
41408
|
function recordAt(path) {
|
|
41324
41409
|
let parsed;
|
|
41325
41410
|
try {
|
|
41326
|
-
parsed = JSON.parse(
|
|
41411
|
+
parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
41327
41412
|
} catch {
|
|
41328
41413
|
return null;
|
|
41329
41414
|
}
|
|
@@ -41374,7 +41459,7 @@ async function runningAs(pid) {
|
|
|
41374
41459
|
const began = await startedAt(pid, null);
|
|
41375
41460
|
const when = began === null ? {} : { startedAt: began };
|
|
41376
41461
|
if (process.platform === "linux") {
|
|
41377
|
-
const raw =
|
|
41462
|
+
const raw = readFileSync21(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
41378
41463
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
41379
41464
|
}
|
|
41380
41465
|
const args = (await execFileAsync("ps", ["-p", String(pid), "-o", "args="], { encoding: "utf8", timeout: 15000 })).stdout.trim();
|
|
@@ -41519,12 +41604,12 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
41519
41604
|
}
|
|
41520
41605
|
function clearRunRecord(projectPath) {
|
|
41521
41606
|
for (const directory of runtimeDirectories()) {
|
|
41522
|
-
const shared =
|
|
41607
|
+
const shared = join24(directory, "runs", SHARED_NOTE);
|
|
41523
41608
|
const sharedRecord = recordAt(shared);
|
|
41524
41609
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
41525
41610
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
41526
41611
|
try {
|
|
41527
|
-
|
|
41612
|
+
rmSync12(path, { force: true });
|
|
41528
41613
|
} catch {}
|
|
41529
41614
|
}
|
|
41530
41615
|
}
|
|
@@ -41549,12 +41634,12 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
41549
41634
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
41550
41635
|
continue;
|
|
41551
41636
|
}
|
|
41552
|
-
const path =
|
|
41637
|
+
const path = join24(directory, entry);
|
|
41553
41638
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
41554
41639
|
continue;
|
|
41555
41640
|
}
|
|
41556
41641
|
try {
|
|
41557
|
-
|
|
41642
|
+
rmSync12(path);
|
|
41558
41643
|
} catch {}
|
|
41559
41644
|
}
|
|
41560
41645
|
}
|
|
@@ -41569,7 +41654,7 @@ var init_run_record = __esm(() => {
|
|
|
41569
41654
|
});
|
|
41570
41655
|
|
|
41571
41656
|
// src/script-origins.ts
|
|
41572
|
-
import { join as
|
|
41657
|
+
import { join as join25 } from "node:path";
|
|
41573
41658
|
function unresolvedClassIn(message) {
|
|
41574
41659
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
41575
41660
|
}
|
|
@@ -41622,7 +41707,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
41622
41707
|
const ran = await runEngine(godotPath, [
|
|
41623
41708
|
"--headless",
|
|
41624
41709
|
"--log-file",
|
|
41625
|
-
|
|
41710
|
+
join25(logDir, "engine.log"),
|
|
41626
41711
|
"--path",
|
|
41627
41712
|
projectPath,
|
|
41628
41713
|
"--check-only",
|
|
@@ -41674,17 +41759,17 @@ import {
|
|
|
41674
41759
|
closeSync as closeSync2,
|
|
41675
41760
|
constants,
|
|
41676
41761
|
copyFileSync,
|
|
41677
|
-
existsSync as
|
|
41762
|
+
existsSync as existsSync20,
|
|
41678
41763
|
fstatSync,
|
|
41679
|
-
mkdirSync as
|
|
41764
|
+
mkdirSync as mkdirSync15,
|
|
41680
41765
|
openSync as openSync3,
|
|
41681
41766
|
readdirSync as readdirSync10,
|
|
41682
|
-
readFileSync as
|
|
41767
|
+
readFileSync as readFileSync22,
|
|
41683
41768
|
readSync as readSync2,
|
|
41684
41769
|
rmdirSync as rmdirSync2,
|
|
41685
41770
|
statSync as statSync9
|
|
41686
41771
|
} from "node:fs";
|
|
41687
|
-
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";
|
|
41688
41773
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
41689
41774
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
41690
41775
|
import { promisify as promisify5 } from "node:util";
|
|
@@ -41705,16 +41790,16 @@ function captureDestinationRefusal(outputPath, projects) {
|
|
|
41705
41790
|
if (extname2(outputPath).toLowerCase() !== ".png") {
|
|
41706
41791
|
return `outputPath ${outputPath} does not end in .png, which is what a capture is.`;
|
|
41707
41792
|
}
|
|
41708
|
-
const directory = realPathOr(
|
|
41709
|
-
if (!
|
|
41710
|
-
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.`;
|
|
41711
41796
|
}
|
|
41712
|
-
const where =
|
|
41797
|
+
const where = join26(directory, basename7(outputPath));
|
|
41713
41798
|
const inside = projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
41714
41799
|
if (inside !== undefined) {
|
|
41715
41800
|
return `outputPath ${outputPath} is inside the project at ${inside}; a capture is saved outside it, so it never becomes part of the game.`;
|
|
41716
41801
|
}
|
|
41717
|
-
if (
|
|
41802
|
+
if (existsSync20(where)) {
|
|
41718
41803
|
return `outputPath ${outputPath} already exists; a capture is not written over a file. Name a new one.`;
|
|
41719
41804
|
}
|
|
41720
41805
|
return null;
|
|
@@ -42187,16 +42272,60 @@ function settingsNamedBy(op, args) {
|
|
|
42187
42272
|
}
|
|
42188
42273
|
}
|
|
42189
42274
|
function foldedScriptErrors(errors) {
|
|
42190
|
-
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));
|
|
42191
42291
|
const own = new Set(scriptErrors.map((error) => error.path));
|
|
42192
42292
|
return {
|
|
42193
42293
|
scriptErrors,
|
|
42194
|
-
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)
|
|
42195
42296
|
};
|
|
42196
42297
|
}
|
|
42197
42298
|
function nonEmpty(lists) {
|
|
42198
42299
|
return Object.fromEntries(Object.entries(lists).filter(([, list]) => list.length > 0));
|
|
42199
42300
|
}
|
|
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
|
+
}
|
|
42200
42329
|
function scriptErrorsNote(errors, {
|
|
42201
42330
|
origins,
|
|
42202
42331
|
unconfirmed
|
|
@@ -42204,7 +42333,11 @@ function scriptErrorsNote(errors, {
|
|
|
42204
42333
|
origins: [],
|
|
42205
42334
|
unconfirmed: []
|
|
42206
42335
|
}) {
|
|
42207
|
-
const {
|
|
42336
|
+
const {
|
|
42337
|
+
scriptErrors: own,
|
|
42338
|
+
failedThroughDependency: dependents,
|
|
42339
|
+
followFromUnresolved: consequences
|
|
42340
|
+
} = foldedScriptErrors(errors);
|
|
42208
42341
|
const causes = new Map;
|
|
42209
42342
|
for (const error of own) {
|
|
42210
42343
|
if (!causes.has(error.path)) {
|
|
@@ -42231,12 +42364,10 @@ function scriptErrorsNote(errors, {
|
|
|
42231
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(", ")}.`;
|
|
42232
42365
|
const more = own.length - causes.size;
|
|
42233
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.` : "";
|
|
42234
|
-
|
|
42235
|
-
|
|
42236
|
-
|
|
42237
|
-
|
|
42238
|
-
rest
|
|
42239
|
-
].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(" ");
|
|
42240
42371
|
}
|
|
42241
42372
|
function timedOutVerdict(timeoutMs, silentForMs) {
|
|
42242
42373
|
const quiet = Math.round(silentForMs);
|
|
@@ -42294,7 +42425,7 @@ function aboveTheRunner(entry) {
|
|
|
42294
42425
|
}
|
|
42295
42426
|
function elsewhereIn(projectPath, asked) {
|
|
42296
42427
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
42297
|
-
if (wanted === "" ||
|
|
42428
|
+
if (wanted === "" || existsSync20(join26(projectPath, wanted))) {
|
|
42298
42429
|
return "";
|
|
42299
42430
|
}
|
|
42300
42431
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -42338,7 +42469,7 @@ function testPathsIn(projectPath, given) {
|
|
|
42338
42469
|
};
|
|
42339
42470
|
}
|
|
42340
42471
|
function hasMainScene(projectFile) {
|
|
42341
|
-
const scene = parseProjectGodot(
|
|
42472
|
+
const scene = parseProjectGodot(readFileSync22(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
42342
42473
|
return typeof scene === "string" && scene !== "";
|
|
42343
42474
|
}
|
|
42344
42475
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -42346,13 +42477,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
42346
42477
|
return false;
|
|
42347
42478
|
}
|
|
42348
42479
|
try {
|
|
42349
|
-
return
|
|
42480
|
+
return readFileSync22(join26(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
42350
42481
|
} catch {
|
|
42351
42482
|
return false;
|
|
42352
42483
|
}
|
|
42353
42484
|
}
|
|
42354
42485
|
function editorPlaysHeadless(projectFile) {
|
|
42355
|
-
const runArgs = parseProjectGodot(
|
|
42486
|
+
const runArgs = parseProjectGodot(readFileSync22(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
42356
42487
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
42357
42488
|
}
|
|
42358
42489
|
function camelCased(params) {
|
|
@@ -42371,7 +42502,7 @@ function needsItsOwnProcess(asked) {
|
|
|
42371
42502
|
class GodotServer {
|
|
42372
42503
|
mcp;
|
|
42373
42504
|
locator = new GodotLocator;
|
|
42374
|
-
operationsScript =
|
|
42505
|
+
operationsScript = join26(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
42375
42506
|
godotBridge;
|
|
42376
42507
|
tools = buildToolDefinitions();
|
|
42377
42508
|
activeProcess = null;
|
|
@@ -42674,11 +42805,20 @@ class GodotServer {
|
|
|
42674
42805
|
return checked.response;
|
|
42675
42806
|
}
|
|
42676
42807
|
const named = args["projectPath"];
|
|
42677
|
-
if (typeof named === "string" &&
|
|
42808
|
+
if (typeof named === "string" && existsSync20(join26(named, "project.godot"))) {
|
|
42678
42809
|
this.lastProjectPath = named;
|
|
42679
42810
|
}
|
|
42680
42811
|
this.updates.refresh();
|
|
42681
|
-
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);
|
|
42682
42822
|
return this.withFeedbackNotice(this.withUpdateNotice(answer));
|
|
42683
42823
|
});
|
|
42684
42824
|
}
|
|
@@ -43148,8 +43288,8 @@ class GodotServer {
|
|
|
43148
43288
|
if (path === undefined) {
|
|
43149
43289
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
43150
43290
|
}
|
|
43151
|
-
const file =
|
|
43152
|
-
if (!
|
|
43291
|
+
const file = join26(path, "project.godot");
|
|
43292
|
+
if (!existsSync20(file)) {
|
|
43153
43293
|
return {
|
|
43154
43294
|
ok: false,
|
|
43155
43295
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -43760,7 +43900,7 @@ class GodotServer {
|
|
|
43760
43900
|
if (!engine.ok) {
|
|
43761
43901
|
return engine.response;
|
|
43762
43902
|
}
|
|
43763
|
-
const application = parseProjectGodot(
|
|
43903
|
+
const application = parseProjectGodot(readFileSync22(project.value.file, "utf8"))["application"] ?? {};
|
|
43764
43904
|
const name = application["config/name"];
|
|
43765
43905
|
const mainScene = application["run/main_scene"];
|
|
43766
43906
|
const info = {
|
|
@@ -43816,18 +43956,18 @@ class GodotServer {
|
|
|
43816
43956
|
if (!engine.ok) {
|
|
43817
43957
|
return engine.response;
|
|
43818
43958
|
}
|
|
43819
|
-
const folder =
|
|
43959
|
+
const folder = dirname19(output.absolutePath);
|
|
43820
43960
|
try {
|
|
43821
|
-
|
|
43961
|
+
mkdirSync15(folder, { recursive: true });
|
|
43822
43962
|
} catch (error) {
|
|
43823
|
-
return this.createErrorResponse(`Cannot create ${
|
|
43963
|
+
return this.createErrorResponse(`Cannot create ${dirname19(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
43824
43964
|
}
|
|
43825
43965
|
const debug = readBoolean(args, "debug") ?? false;
|
|
43826
43966
|
const exportLogs = scratchDirectory("export");
|
|
43827
43967
|
const exportArgs = [
|
|
43828
43968
|
"--headless",
|
|
43829
43969
|
"--log-file",
|
|
43830
|
-
|
|
43970
|
+
join26(exportLogs, "engine.log"),
|
|
43831
43971
|
"--path",
|
|
43832
43972
|
project.value.path,
|
|
43833
43973
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -43835,7 +43975,7 @@ class GodotServer {
|
|
|
43835
43975
|
output.absolutePath
|
|
43836
43976
|
];
|
|
43837
43977
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
43838
|
-
const writtenAt = () =>
|
|
43978
|
+
const writtenAt = () => existsSync20(output.absolutePath) ? statSync9(output.absolutePath).mtimeMs : null;
|
|
43839
43979
|
const before = writtenAt();
|
|
43840
43980
|
let ending;
|
|
43841
43981
|
try {
|
|
@@ -43848,7 +43988,38 @@ class GodotServer {
|
|
|
43848
43988
|
}
|
|
43849
43989
|
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt());
|
|
43850
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
|
+
}
|
|
43851
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) {
|
|
43852
44023
|
const project = this.project(args);
|
|
43853
44024
|
if (!project.ok) {
|
|
43854
44025
|
return project.response;
|
|
@@ -43857,7 +44028,7 @@ class GodotServer {
|
|
|
43857
44028
|
if (!paths.ok) {
|
|
43858
44029
|
return this.createErrorResponse(paths.reason, PATH_SOLUTIONS);
|
|
43859
44030
|
}
|
|
43860
|
-
const missing = paths.value.filter((path) => !
|
|
44031
|
+
const missing = paths.value.filter((path) => !existsSync20(join26(project.value.path, path.slice("res://".length))));
|
|
43861
44032
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
43862
44033
|
if (asked.length === 0) {
|
|
43863
44034
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
@@ -43873,7 +44044,7 @@ class GodotServer {
|
|
|
43873
44044
|
};
|
|
43874
44045
|
}
|
|
43875
44046
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
43876
|
-
if (!
|
|
44047
|
+
if (!existsSync20(join26(project.value.path, runner))) {
|
|
43877
44048
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
43878
44049
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
43879
44050
|
]);
|
|
@@ -43918,6 +44089,7 @@ class GodotServer {
|
|
|
43918
44089
|
run.process.stderr?.on("data", () => {
|
|
43919
44090
|
lastSaid = Date.now();
|
|
43920
44091
|
});
|
|
44092
|
+
const reporting = this.reportTestProgress(run.log);
|
|
43921
44093
|
let silentForMs = 0;
|
|
43922
44094
|
const timedOut = await new Promise((resolve) => {
|
|
43923
44095
|
const timer = setTimeout(() => {
|
|
@@ -43934,25 +44106,26 @@ class GodotServer {
|
|
|
43934
44106
|
resolve(false);
|
|
43935
44107
|
});
|
|
43936
44108
|
});
|
|
44109
|
+
clearInterval(reporting);
|
|
43937
44110
|
if (timedOut && run.process.pid !== undefined) {
|
|
43938
44111
|
await untilGone([run.process.pid]);
|
|
43939
44112
|
}
|
|
43940
44113
|
await run.settled;
|
|
43941
|
-
const reportsDir =
|
|
44114
|
+
const reportsDir = join26(project.value.path, ".godot", ...ours.split("/"));
|
|
43942
44115
|
let report = null;
|
|
43943
44116
|
let reportProblem = null;
|
|
43944
44117
|
try {
|
|
43945
|
-
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))) : [];
|
|
43946
44119
|
const newest = written.at(-1);
|
|
43947
44120
|
if (newest !== undefined) {
|
|
43948
|
-
report = parseJUnit(
|
|
44121
|
+
report = parseJUnit(readFileSync22(join26(reportsDir, newest, "results.xml"), "utf8"));
|
|
43949
44122
|
}
|
|
43950
44123
|
} catch (error) {
|
|
43951
44124
|
reportProblem = errorMessage(error);
|
|
43952
44125
|
} finally {
|
|
43953
44126
|
discard(reportsDir, userData);
|
|
43954
44127
|
try {
|
|
43955
|
-
rmdirSync2(
|
|
44128
|
+
rmdirSync2(dirname19(reportsDir));
|
|
43956
44129
|
} catch {}
|
|
43957
44130
|
}
|
|
43958
44131
|
const engineEntries = run.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries.map(aboveTheRunner);
|
|
@@ -43989,9 +44162,28 @@ class GodotServer {
|
|
|
43989
44162
|
printed: echoed.entries.map((entry) => entry.text),
|
|
43990
44163
|
...echoed.omitted > 0 ? { printedOmitted: echoed.omitted } : {}
|
|
43991
44164
|
};
|
|
44165
|
+
const sofar = report === null ? suitesPrinted(said) : null;
|
|
43992
44166
|
if (report === null) {
|
|
43993
|
-
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)}`;
|
|
43994
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
|
+
}
|
|
43995
44187
|
return {
|
|
43996
44188
|
content: [
|
|
43997
44189
|
{ type: "text", text: note },
|
|
@@ -44007,6 +44199,7 @@ class GodotServer {
|
|
|
44007
44199
|
...timedOut ? { timedOut, silentForMs } : {},
|
|
44008
44200
|
...missing.length > 0 ? { missing } : {},
|
|
44009
44201
|
...nonEmpty(foldedScriptErrors(scriptErrors)),
|
|
44202
|
+
...atTheEnd,
|
|
44010
44203
|
arguments: cmdArgs,
|
|
44011
44204
|
entries: forAnswer(withoutListed(printed, scriptErrors).slice(0, 60).map(aboveTheRunner)),
|
|
44012
44205
|
...printedLines,
|
|
@@ -44141,11 +44334,11 @@ class GodotServer {
|
|
|
44141
44334
|
const cached = cachedClasses(projectPath);
|
|
44142
44335
|
const contradicted = cached === null ? [] : contradictedDiagnostics(messages, cached, (resourcePath) => {
|
|
44143
44336
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
44144
|
-
if (!contained.ok || !
|
|
44337
|
+
if (!contained.ok || !existsSync20(contained.absolutePath)) {
|
|
44145
44338
|
return null;
|
|
44146
44339
|
}
|
|
44147
44340
|
try {
|
|
44148
|
-
return
|
|
44341
|
+
return readFileSync22(contained.absolutePath, "utf8");
|
|
44149
44342
|
} catch {
|
|
44150
44343
|
return null;
|
|
44151
44344
|
}
|
|
@@ -44201,11 +44394,11 @@ class GodotServer {
|
|
|
44201
44394
|
}
|
|
44202
44395
|
sourceOfClass(projectPath, resourcePath) {
|
|
44203
44396
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
44204
|
-
if (!contained.ok || !
|
|
44397
|
+
if (!contained.ok || !existsSync20(contained.absolutePath)) {
|
|
44205
44398
|
return null;
|
|
44206
44399
|
}
|
|
44207
44400
|
try {
|
|
44208
|
-
return
|
|
44401
|
+
return readFileSync22(contained.absolutePath, "utf8");
|
|
44209
44402
|
} catch {
|
|
44210
44403
|
return null;
|
|
44211
44404
|
}
|
|
@@ -44516,7 +44709,7 @@ class GodotServer {
|
|
|
44516
44709
|
return null;
|
|
44517
44710
|
}
|
|
44518
44711
|
try {
|
|
44519
|
-
return settingKeys(
|
|
44712
|
+
return settingKeys(readFileSync22(join26(projectPath, "project.godot"), "utf8"));
|
|
44520
44713
|
} catch {
|
|
44521
44714
|
return null;
|
|
44522
44715
|
}
|
|
@@ -44838,7 +45031,7 @@ class GodotServer {
|
|
|
44838
45031
|
}
|
|
44839
45032
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
44840
45033
|
const budgetMs = budget.ms;
|
|
44841
|
-
if (!
|
|
45034
|
+
if (!existsSync20(join26(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
44842
45035
|
return runtimeVerdict(null, {
|
|
44843
45036
|
addon: false,
|
|
44844
45037
|
budgetMs,
|
|
@@ -45097,7 +45290,7 @@ class GodotServer {
|
|
|
45097
45290
|
return 0;
|
|
45098
45291
|
}
|
|
45099
45292
|
try {
|
|
45100
|
-
return reportAlreadyInTranscript(
|
|
45293
|
+
return reportAlreadyInTranscript(readFileSync22(game.transcript), readFileSync22(report));
|
|
45101
45294
|
} catch (error) {
|
|
45102
45295
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
45103
45296
|
return 0;
|
|
@@ -45855,7 +46048,7 @@ class GodotServer {
|
|
|
45855
46048
|
notes.push(endedWithoutACode({
|
|
45856
46049
|
throughEditor: run.throughEditor,
|
|
45857
46050
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
45858
|
-
couldAnnounce: run.projectPath !== null &&
|
|
46051
|
+
couldAnnounce: run.projectPath !== null && existsSync20(join26(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
45859
46052
|
endedHere: typeof run.endedHere === "string",
|
|
45860
46053
|
pickedUp: run.pickedUp === true
|
|
45861
46054
|
}));
|
|
@@ -46416,7 +46609,7 @@ class GodotServer {
|
|
|
46416
46609
|
if (!scriptPath.ok) {
|
|
46417
46610
|
return this.createErrorResponse(scriptPath.reason);
|
|
46418
46611
|
}
|
|
46419
|
-
if (!
|
|
46612
|
+
if (!existsSync20(scriptPath.absolutePath)) {
|
|
46420
46613
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
46421
46614
|
}
|
|
46422
46615
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -46704,7 +46897,7 @@ class GodotServer {
|
|
|
46704
46897
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
46705
46898
|
const run = await this.currentRun();
|
|
46706
46899
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
46707
|
-
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)) {
|
|
46708
46901
|
return null;
|
|
46709
46902
|
}
|
|
46710
46903
|
const already = runtimesAnnounced().running;
|
|
@@ -46788,7 +46981,7 @@ class GodotServer {
|
|
|
46788
46981
|
}
|
|
46789
46982
|
}
|
|
46790
46983
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
46791
|
-
const screenshotPath = screenshotDir ?
|
|
46984
|
+
const screenshotPath = screenshotDir ? join26(screenshotDir, "capture.png") : null;
|
|
46792
46985
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
46793
46986
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
46794
46987
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
@@ -46837,7 +47030,7 @@ ${staleNote}`);
|
|
|
46837
47030
|
|
|
46838
47031
|
${staleNote}`}`
|
|
46839
47032
|
},
|
|
46840
|
-
{ type: "image", data:
|
|
47033
|
+
{ type: "image", data: readFileSync22(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
46841
47034
|
]
|
|
46842
47035
|
};
|
|
46843
47036
|
} finally {
|
|
@@ -46938,7 +47131,7 @@ async function runGodotServer() {
|
|
|
46938
47131
|
const server = new GodotServer;
|
|
46939
47132
|
await server.run();
|
|
46940
47133
|
}
|
|
46941
|
-
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/";
|
|
46942
47135
|
var init_server3 = __esm(() => {
|
|
46943
47136
|
init_mcp();
|
|
46944
47137
|
init_stdio2();
|
|
@@ -46952,6 +47145,7 @@ var init_server3 = __esm(() => {
|
|
|
46952
47145
|
init_class_cache();
|
|
46953
47146
|
init_class_note();
|
|
46954
47147
|
init_config_pin();
|
|
47148
|
+
init_cut_test_run();
|
|
46955
47149
|
init_dap_client();
|
|
46956
47150
|
init_desktop();
|
|
46957
47151
|
init_editor_log();
|
|
@@ -46985,7 +47179,7 @@ var init_server3 = __esm(() => {
|
|
|
46985
47179
|
init_tool_definitions();
|
|
46986
47180
|
init_update_check();
|
|
46987
47181
|
run4 = promisify5(execFile5);
|
|
46988
|
-
__dirname2 =
|
|
47182
|
+
__dirname2 = dirname19(fileURLToPath5(import.meta.url));
|
|
46989
47183
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
46990
47184
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
46991
47185
|
VALUE_NAMES = {
|
|
@@ -47035,8 +47229,8 @@ var init_server3 = __esm(() => {
|
|
|
47035
47229
|
init_bridge_announce();
|
|
47036
47230
|
init_errors();
|
|
47037
47231
|
init_godot_path();
|
|
47038
|
-
import { existsSync as
|
|
47039
|
-
import { join as
|
|
47232
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
47233
|
+
import { join as join27, resolve as resolve8 } from "node:path";
|
|
47040
47234
|
|
|
47041
47235
|
// src/harnesses.ts
|
|
47042
47236
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -48367,7 +48561,7 @@ class UsageError extends Refusal {
|
|
|
48367
48561
|
function projectArgument(at) {
|
|
48368
48562
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
48369
48563
|
const projectPath = resolve8(named ?? ".");
|
|
48370
|
-
if (!
|
|
48564
|
+
if (!existsSync21(join27(projectPath, "project.godot"))) {
|
|
48371
48565
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
48372
48566
|
}
|
|
48373
48567
|
return projectPath;
|
|
@@ -48412,7 +48606,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
48412
48606
|
return;
|
|
48413
48607
|
}
|
|
48414
48608
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
48415
|
-
if (!
|
|
48609
|
+
if (!existsSync21(join27(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
48416
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}`);
|
|
48417
48611
|
}
|
|
48418
48612
|
}
|
|
@@ -48544,7 +48738,7 @@ async function setup() {
|
|
|
48544
48738
|
console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
|
|
48545
48739
|
}
|
|
48546
48740
|
if (!args.includes("--no-skill")) {
|
|
48547
|
-
const directories = skillDirectories(chosen.harnesses, projectPath,
|
|
48741
|
+
const directories = skillDirectories(chosen.harnesses, projectPath, existsSync21);
|
|
48548
48742
|
for (const written of writeSkill(directories, getLocalVersion())) {
|
|
48549
48743
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
48550
48744
|
}
|
|
@@ -48658,7 +48852,7 @@ async function upgrade() {
|
|
|
48658
48852
|
moved.push(took.from);
|
|
48659
48853
|
}
|
|
48660
48854
|
}
|
|
48661
|
-
for (const written of writeSkill(skillDirectories(already, projectPath,
|
|
48855
|
+
for (const written of writeSkill(skillDirectories(already, projectPath, existsSync21), version)) {
|
|
48662
48856
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
48663
48857
|
}
|
|
48664
48858
|
console.log(`
|