gdharness 1.3.11 → 1.3.12
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 +463 -256
- package/build/godot/tests/listed.gd +67 -2
- package/build/keeper.js +21 -4
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -10094,7 +10094,15 @@ var init_import_freshness = __esm(() => {
|
|
|
10094
10094
|
});
|
|
10095
10095
|
|
|
10096
10096
|
// src/library-copies.ts
|
|
10097
|
-
import {
|
|
10097
|
+
import {
|
|
10098
|
+
existsSync as existsSync9,
|
|
10099
|
+
mkdirSync as mkdirSync3,
|
|
10100
|
+
readdirSync as readdirSync7,
|
|
10101
|
+
readFileSync as readFileSync7,
|
|
10102
|
+
renameSync,
|
|
10103
|
+
unlinkSync,
|
|
10104
|
+
writeFileSync as writeFileSync3
|
|
10105
|
+
} from "node:fs";
|
|
10098
10106
|
import { basename as basename6, dirname as dirname6, isAbsolute as isAbsolute5, join as join10 } from "node:path";
|
|
10099
10107
|
function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
10100
10108
|
const setAside = [];
|
|
@@ -10129,6 +10137,44 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
10129
10137
|
}
|
|
10130
10138
|
return { setAside, swept };
|
|
10131
10139
|
}
|
|
10140
|
+
function seedExtensionList(projectPath) {
|
|
10141
|
+
const list = join10(projectPath, ".godot", "extension_list.cfg");
|
|
10142
|
+
if (existsSync9(list)) {
|
|
10143
|
+
return null;
|
|
10144
|
+
}
|
|
10145
|
+
const found = [];
|
|
10146
|
+
const visit = (directory, resDirectory) => {
|
|
10147
|
+
if (existsSync9(join10(directory, ".gdignore"))) {
|
|
10148
|
+
return;
|
|
10149
|
+
}
|
|
10150
|
+
let entries;
|
|
10151
|
+
try {
|
|
10152
|
+
entries = readdirSync7(directory, { withFileTypes: true });
|
|
10153
|
+
} catch {
|
|
10154
|
+
return;
|
|
10155
|
+
}
|
|
10156
|
+
for (const entry of entries) {
|
|
10157
|
+
if (entry.name.startsWith(".")) {
|
|
10158
|
+
continue;
|
|
10159
|
+
}
|
|
10160
|
+
if (entry.isDirectory()) {
|
|
10161
|
+
visit(join10(directory, entry.name), `${resDirectory}${entry.name}/`);
|
|
10162
|
+
} else if (entry.name.endsWith(".gdextension")) {
|
|
10163
|
+
found.push(`${resDirectory}${entry.name}`);
|
|
10164
|
+
}
|
|
10165
|
+
}
|
|
10166
|
+
};
|
|
10167
|
+
visit(projectPath, "res://");
|
|
10168
|
+
if (found.length === 0) {
|
|
10169
|
+
return [];
|
|
10170
|
+
}
|
|
10171
|
+
found.sort();
|
|
10172
|
+
mkdirSync3(dirname6(list), { recursive: true });
|
|
10173
|
+
writeFileSync3(list, `${found.join(`
|
|
10174
|
+
`)}
|
|
10175
|
+
`);
|
|
10176
|
+
return found;
|
|
10177
|
+
}
|
|
10132
10178
|
function windowsLibraries(projectPath) {
|
|
10133
10179
|
const listed = readOrNull(join10(projectPath, ".godot", "extension_list.cfg"));
|
|
10134
10180
|
if (listed === null) {
|
|
@@ -10177,7 +10223,7 @@ var init_library_copies = () => {};
|
|
|
10177
10223
|
|
|
10178
10224
|
// src/reimport.ts
|
|
10179
10225
|
import { createHash as createHash2 } from "node:crypto";
|
|
10180
|
-
import { existsSync as existsSync10, readFileSync as readFileSync8, rmSync as rmSync3, statSync as statSync8, utimesSync, writeFileSync as
|
|
10226
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, rmSync as rmSync3, statSync as statSync8, utimesSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
10181
10227
|
import { basename as basename7, join as join11 } from "node:path";
|
|
10182
10228
|
function forceNextImport(projectPath, resourcePath) {
|
|
10183
10229
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
@@ -10186,7 +10232,7 @@ function forceNextImport(projectPath, resourcePath) {
|
|
|
10186
10232
|
return;
|
|
10187
10233
|
}
|
|
10188
10234
|
const { atime, mtimeMs } = statSync8(sidecar);
|
|
10189
|
-
|
|
10235
|
+
writeFileSync4(sidecar, readFileSync8(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
10190
10236
|
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
10191
10237
|
rmSync3(importRecord(projectPath, resourcePath), { force: true });
|
|
10192
10238
|
}
|
|
@@ -10961,7 +11007,7 @@ var init_script_origins = __esm(() => {
|
|
|
10961
11007
|
});
|
|
10962
11008
|
|
|
10963
11009
|
// src/headless.ts
|
|
10964
|
-
import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as
|
|
11010
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
10965
11011
|
import { dirname as dirname7, join as join14 } from "node:path";
|
|
10966
11012
|
function snakeCased(params) {
|
|
10967
11013
|
const result = emptyRecord();
|
|
@@ -11156,7 +11202,7 @@ async function operationOutcome(engine, operation, params, projectPath) {
|
|
|
11156
11202
|
const paramsDir = scratchDirectory("params");
|
|
11157
11203
|
const paramsFile = join14(paramsDir, `${operation}.json`);
|
|
11158
11204
|
const answerFile = join14(paramsDir, "answer.json");
|
|
11159
|
-
|
|
11205
|
+
writeFileSync5(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
11160
11206
|
const args = [
|
|
11161
11207
|
"--headless",
|
|
11162
11208
|
...engine.editor === true ? ["--editor"] : [],
|
|
@@ -11277,7 +11323,7 @@ var init_headless = __esm(() => {
|
|
|
11277
11323
|
});
|
|
11278
11324
|
|
|
11279
11325
|
// src/update-check.ts
|
|
11280
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
11326
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "node:fs";
|
|
11281
11327
|
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
11282
11328
|
import { join as join15 } from "node:path";
|
|
11283
11329
|
function packageUrl(root) {
|
|
@@ -11311,7 +11357,7 @@ function cacheDirectory(environment) {
|
|
|
11311
11357
|
function cacheFile(environment = process.env) {
|
|
11312
11358
|
try {
|
|
11313
11359
|
const directory = cacheDirectory(environment);
|
|
11314
|
-
|
|
11360
|
+
mkdirSync4(directory, { recursive: true, mode: 448 });
|
|
11315
11361
|
return join15(directory, "update-check.json");
|
|
11316
11362
|
} catch {
|
|
11317
11363
|
return join15(tmpdir3(), "gdharness-update-check.json");
|
|
@@ -11339,7 +11385,7 @@ function readCache(path) {
|
|
|
11339
11385
|
}
|
|
11340
11386
|
function writeCache(path, entry) {
|
|
11341
11387
|
try {
|
|
11342
|
-
|
|
11388
|
+
writeFileSync6(path, JSON.stringify(entry), { encoding: "utf8", mode: 384 });
|
|
11343
11389
|
} catch {}
|
|
11344
11390
|
}
|
|
11345
11391
|
function parts(version) {
|
|
@@ -12023,13 +12069,13 @@ import { createHash as createHash4 } from "node:crypto";
|
|
|
12023
12069
|
import {
|
|
12024
12070
|
cpSync,
|
|
12025
12071
|
existsSync as existsSync14,
|
|
12026
|
-
mkdirSync as
|
|
12072
|
+
mkdirSync as mkdirSync5,
|
|
12027
12073
|
readdirSync as readdirSync9,
|
|
12028
12074
|
readFileSync as readFileSync13,
|
|
12029
12075
|
rmdirSync,
|
|
12030
12076
|
rmSync as rmSync5,
|
|
12031
12077
|
statSync as statSync9,
|
|
12032
|
-
writeFileSync as
|
|
12078
|
+
writeFileSync as writeFileSync7
|
|
12033
12079
|
} from "node:fs";
|
|
12034
12080
|
import { dirname as dirname8, join as join16 } from "node:path";
|
|
12035
12081
|
import { fileURLToPath } from "node:url";
|
|
@@ -12118,15 +12164,15 @@ function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
|
12118
12164
|
const target = join16(projectPath, "addons", name);
|
|
12119
12165
|
const replaced = existsSync14(target);
|
|
12120
12166
|
rmSync5(target, { recursive: true, force: true });
|
|
12121
|
-
|
|
12167
|
+
mkdirSync5(dirname8(target), { recursive: true });
|
|
12122
12168
|
cpSync(source, target, { recursive: true });
|
|
12123
|
-
|
|
12169
|
+
writeFileSync7(join16(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
12124
12170
|
`);
|
|
12125
12171
|
installed.push({ name, path: target, replaced });
|
|
12126
12172
|
}
|
|
12127
|
-
|
|
12173
|
+
writeFileSync7(join16(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
12128
12174
|
`);
|
|
12129
|
-
|
|
12175
|
+
writeFileSync7(join16(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
|
|
12130
12176
|
`);
|
|
12131
12177
|
return installed;
|
|
12132
12178
|
}
|
|
@@ -14767,7 +14813,7 @@ import {
|
|
|
14767
14813
|
readFileSync as readFileSync14,
|
|
14768
14814
|
rmSync as rmSync7,
|
|
14769
14815
|
statSync as statSync10,
|
|
14770
|
-
writeFileSync as
|
|
14816
|
+
writeFileSync as writeFileSync9,
|
|
14771
14817
|
writeSync
|
|
14772
14818
|
} from "node:fs";
|
|
14773
14819
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
@@ -14816,7 +14862,7 @@ async function ensureHelperCompiled(directory = tmpdir4(), budgetMs = HELPER_COM
|
|
|
14816
14862
|
async function compileHelper(directory, assembly, budgetMs) {
|
|
14817
14863
|
const scratch = mkdtempSync2(join18(directory, "gdharness-desktop-source-"));
|
|
14818
14864
|
const source = join18(scratch, "helper.cs");
|
|
14819
|
-
|
|
14865
|
+
writeFileSync9(source, SOURCE, "utf8");
|
|
14820
14866
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
14821
14867
|
const script = [
|
|
14822
14868
|
"$ErrorActionPreference = 'Stop'",
|
|
@@ -14902,9 +14948,10 @@ function throughHelper(how, command, args, files, assembly = helperAssembly()) {
|
|
|
14902
14948
|
how.noActivate === true ? "1" : "0",
|
|
14903
14949
|
files.stop,
|
|
14904
14950
|
how.directory,
|
|
14905
|
-
files.step
|
|
14951
|
+
files.step,
|
|
14952
|
+
how.affinity === undefined ? "" : how.affinity.toString()
|
|
14906
14953
|
];
|
|
14907
|
-
|
|
14954
|
+
writeFileSync9(files.spec, fields.map((field) => Buffer.from(field, "utf8").toString("base64")).join(`
|
|
14908
14955
|
`), "utf8");
|
|
14909
14956
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
14910
14957
|
const script = [
|
|
@@ -14976,6 +15023,9 @@ public static class GdharnessDesktop {
|
|
|
14976
15023
|
[DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr handle);
|
|
14977
15024
|
[DllImport("kernel32.dll")] static extern bool TerminateProcess(IntPtr handle, uint code);
|
|
14978
15025
|
[DllImport("kernel32.dll")] static extern uint SetErrorMode(uint mode);
|
|
15026
|
+
[DllImport("kernel32.dll", SetLastError = true)] static extern bool SetProcessAffinityMask(IntPtr process, UIntPtr mask);
|
|
15027
|
+
[DllImport("kernel32.dll")] static extern uint ResumeThread(IntPtr thread);
|
|
15028
|
+
const uint CreateSuspended = 0x4;
|
|
14979
15029
|
const uint FailCriticalErrors = 0x1;
|
|
14980
15030
|
const uint NoOpenFileErrorBox = 0x8000;
|
|
14981
15031
|
|
|
@@ -15037,7 +15087,8 @@ public static class GdharnessDesktop {
|
|
|
15037
15087
|
public static int RunSpec(string specFile) {
|
|
15038
15088
|
string[] lines = File.ReadAllLines(specFile);
|
|
15039
15089
|
Func<int, string> field = delegate(int index) { return Encoding.UTF8.GetString(Convert.FromBase64String(lines[index])); };
|
|
15040
|
-
|
|
15090
|
+
string affinity = lines.Length > 7 ? field(7) : "";
|
|
15091
|
+
return Run(field(0), field(1), field(2), field(3) == "1", field(4), field(5), field(6), affinity.Length == 0 ? 0UL : ulong.Parse(affinity));
|
|
15041
15092
|
}
|
|
15042
15093
|
|
|
15043
15094
|
static void Step(string stepFile, string step) {
|
|
@@ -15046,7 +15097,9 @@ public static class GdharnessDesktop {
|
|
|
15046
15097
|
}
|
|
15047
15098
|
}
|
|
15048
15099
|
|
|
15049
|
-
|
|
15100
|
+
// With an affinity, the process starts suspended and is held to those processors before its
|
|
15101
|
+
// first thread runs, so every thread it starts afterwards is held to them as well.
|
|
15102
|
+
public static int Run(string desktop, string commandLine, string pidFile, bool noActivate, string stopFile, string directory, string stepFile, ulong affinity) {
|
|
15050
15103
|
StartupInfo startup = new StartupInfo();
|
|
15051
15104
|
startup.cb = Marshal.SizeOf(typeof(StartupInfo));
|
|
15052
15105
|
if (desktop.Length > 0) {
|
|
@@ -15076,9 +15129,19 @@ public static class GdharnessDesktop {
|
|
|
15076
15129
|
}
|
|
15077
15130
|
try {
|
|
15078
15131
|
Step(stepFile, "starting the process");
|
|
15079
|
-
|
|
15132
|
+
uint creation = DetachedProcess | (affinity == 0 ? 0u : CreateSuspended);
|
|
15133
|
+
if (!CreateProcessW(null, new StringBuilder(commandLine), IntPtr.Zero, IntPtr.Zero, true, creation, IntPtr.Zero, directory, ref startup, out started)) {
|
|
15080
15134
|
throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateProcessW " + commandLine);
|
|
15081
15135
|
}
|
|
15136
|
+
if (affinity != 0) {
|
|
15137
|
+
Step(stepFile, "holding the process to its processors");
|
|
15138
|
+
if (!SetProcessAffinityMask(started.process, new UIntPtr(affinity))) {
|
|
15139
|
+
int error = Marshal.GetLastWin32Error();
|
|
15140
|
+
TerminateProcess(started.process, 1);
|
|
15141
|
+
throw new Win32Exception(error, "SetProcessAffinityMask " + affinity);
|
|
15142
|
+
}
|
|
15143
|
+
ResumeThread(started.thread);
|
|
15144
|
+
}
|
|
15082
15145
|
File.WriteAllText(pidFile, started.processId.ToString());
|
|
15083
15146
|
if (noActivate) {
|
|
15084
15147
|
HoldTheForeground(started);
|
|
@@ -15154,7 +15217,13 @@ function readRun(printed) {
|
|
|
15154
15217
|
afterCase = [];
|
|
15155
15218
|
};
|
|
15156
15219
|
const leftOut = new Map;
|
|
15220
|
+
let engineVersion = null;
|
|
15157
15221
|
for (const [at, line] of lines.entries()) {
|
|
15222
|
+
const banner = engineVersion === null ? BANNER.exec(line) : null;
|
|
15223
|
+
if (banner !== null) {
|
|
15224
|
+
engineVersion = banner[1] ?? null;
|
|
15225
|
+
continue;
|
|
15226
|
+
}
|
|
15158
15227
|
const left = LEFT_OUT.exec(line);
|
|
15159
15228
|
if (left !== null) {
|
|
15160
15229
|
const key = `${left[1] ?? ""} > ${left[2] ?? ""}`;
|
|
@@ -15241,7 +15310,7 @@ function readRun(printed) {
|
|
|
15241
15310
|
`);
|
|
15242
15311
|
const starved = STARVED.flatMap((pattern) => [...joined.matchAll(pattern)].map((found) => found[0]));
|
|
15243
15312
|
const unloadable = scriptErrorsPrinted(lines).map((error) => `${error.path}${error.line === null ? "" : `:${String(error.line)}`}: ${error.message}`);
|
|
15244
|
-
return { lines, suites, errors, leaks, starved, executed, unloadable, stoppedIn, leftOut };
|
|
15313
|
+
return { lines, suites, errors, leaks, starved, executed, unloadable, stoppedIn, leftOut, engineVersion };
|
|
15245
15314
|
}
|
|
15246
15315
|
function withNestedErrors(printed, piped) {
|
|
15247
15316
|
const nested = piped.flatMap((entry, at) => {
|
|
@@ -15250,6 +15319,12 @@ function withNestedErrors(printed, piped) {
|
|
|
15250
15319
|
});
|
|
15251
15320
|
return nested.length === 0 ? printed : { ...printed, errors: [...printed.errors, ...nested] };
|
|
15252
15321
|
}
|
|
15322
|
+
function versionFault(said, wanted) {
|
|
15323
|
+
if (said === null) {
|
|
15324
|
+
return `the engine printed no version as it started, so it cannot be told to be Godot ${wanted}`;
|
|
15325
|
+
}
|
|
15326
|
+
return said === wanted || said.startsWith(`${wanted}.`) ? null : `the engine that ran is Godot ${said}, not the ${wanted} --godot-version asks for`;
|
|
15327
|
+
}
|
|
15253
15328
|
function readExpected(file) {
|
|
15254
15329
|
const declared = (value) => typeof value === "object" && value !== null ? value : {};
|
|
15255
15330
|
try {
|
|
@@ -15331,11 +15406,19 @@ function judgeEngine(printed, handed, end) {
|
|
|
15331
15406
|
if (end.ended !== null) {
|
|
15332
15407
|
faults.push(end.ended);
|
|
15333
15408
|
} else if (end.code !== 0 && !explained) {
|
|
15334
|
-
|
|
15409
|
+
const free = end.freeBytes === undefined ? "" : `, with ${(end.freeBytes / 1024 ** 3).toFixed(1)} GB of memory free as it ended`;
|
|
15410
|
+
faults.push(end.code === null ? `the engine was ended by ${end.signal ?? "a signal"}${free}` : `the engine exited ${exitNamed(end.code)}${free}`);
|
|
15411
|
+
}
|
|
15412
|
+
if (printed.lines.every((line) => line.trim() === "")) {
|
|
15413
|
+
faults.push("the engine printed nothing at all");
|
|
15335
15414
|
}
|
|
15336
15415
|
if (printed.unloadable.length > 0) {
|
|
15337
15416
|
faults.push(`gdUnit4 could not load ${named(printed.unloadable)}, so it ran nothing`);
|
|
15338
15417
|
}
|
|
15418
|
+
const wrongEngine = handed.godotVersion === undefined || handed.godotVersion === null ? null : versionFault(printed.engineVersion, handed.godotVersion);
|
|
15419
|
+
if (wrongEngine !== null) {
|
|
15420
|
+
faults.push(wrongEngine);
|
|
15421
|
+
}
|
|
15339
15422
|
if (printed.executed === null || printed.executed === 0) {
|
|
15340
15423
|
faults.push(printed.executed === null ? "the run never printed its summary" : "the run executed no test case at all, whatever it exited with");
|
|
15341
15424
|
}
|
|
@@ -15489,7 +15572,7 @@ ${String(error.warning)}`);
|
|
|
15489
15572
|
const neverPrinted = [...owed.values()].flatMap((debt) => debt.texts.map((text) => ({ suite: debt.suite, during: debt.during, text, warning: debt.warning })));
|
|
15490
15573
|
return { printed: unexpected, neverPrinted };
|
|
15491
15574
|
}
|
|
15492
|
-
var SUITE_OPENED, TIME, CASE_LINE, STATISTICS, ERROR_LINE, WARNING_LINE, EXECUTED, LEFT_OUT, DRIVER_FALLBACK, ORPHANS_REPORTED, STARVED, NESTED_ERROR = "While attempting to print an error, another error was printed:", WINDOWS_EXITS, MEMORY_EXITS, GDUNIT_FAILED = 100, GDUNIT_EXITS, TAIL = 60, MOST_NAMED = 8;
|
|
15575
|
+
var SUITE_OPENED, TIME, CASE_LINE, STATISTICS, ERROR_LINE, WARNING_LINE, EXECUTED, BANNER, LEFT_OUT, DRIVER_FALLBACK, ORPHANS_REPORTED, STARVED, NESTED_ERROR = "While attempting to print an error, another error was printed:", WINDOWS_EXITS, MEMORY_EXITS, GDUNIT_FAILED = 100, GDUNIT_EXITS, TAIL = 60, MOST_NAMED = 8;
|
|
15493
15576
|
var init_suite_judge = __esm(() => {
|
|
15494
15577
|
init_engine_leaks();
|
|
15495
15578
|
init_junit();
|
|
@@ -15500,6 +15583,7 @@ var init_suite_judge = __esm(() => {
|
|
|
15500
15583
|
ERROR_LINE = /^\s*(?:SCRIPT |USER )?ERROR: (.*?)\s*$/;
|
|
15501
15584
|
WARNING_LINE = /^\s*(?:SCRIPT |USER )?WARNING: (.*?)\s*$/;
|
|
15502
15585
|
EXECUTED = /^Executed test cases\s*:\s*\((\d+)\//;
|
|
15586
|
+
BANNER = /^\s*Godot Engine v(\S+?)(?:\s+-\s+\S+)?\s*$/;
|
|
15503
15587
|
LEFT_OUT = /^gdharness left out (res:\/\/.+?\.gd) > ([^:\s]+):(-?\d+)\s*$/;
|
|
15504
15588
|
DRIVER_FALLBACK = /^\s*WARNING: Your video card drivers .*, switching to /;
|
|
15505
15589
|
ORPHANS_REPORTED = /^\s*WARNING: Detected \d+ (?:possible )?orphan nodes\.\s*$/;
|
|
@@ -15527,11 +15611,11 @@ var init_suite_judge = __esm(() => {
|
|
|
15527
15611
|
});
|
|
15528
15612
|
|
|
15529
15613
|
// src/whole-file.ts
|
|
15530
|
-
import { renameSync as renameSync2, rmSync as rmSync9, writeFileSync as
|
|
15614
|
+
import { renameSync as renameSync2, rmSync as rmSync9, writeFileSync as writeFileSync10 } from "node:fs";
|
|
15531
15615
|
function writeWhole(path, text) {
|
|
15532
15616
|
const partial = `${path}.${process.pid}.partial`;
|
|
15533
15617
|
try {
|
|
15534
|
-
|
|
15618
|
+
writeFileSync10(partial, text, "utf8");
|
|
15535
15619
|
} catch {
|
|
15536
15620
|
return false;
|
|
15537
15621
|
}
|
|
@@ -15558,16 +15642,29 @@ var init_whole_file = __esm(() => {
|
|
|
15558
15642
|
});
|
|
15559
15643
|
|
|
15560
15644
|
// src/suite-plan.ts
|
|
15561
|
-
import { existsSync as existsSync18, mkdirSync as
|
|
15562
|
-
import { dirname as dirname10, posix as posix3 } from "node:path";
|
|
15645
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync8, readFileSync as readFileSync18 } from "node:fs";
|
|
15646
|
+
import { dirname as dirname10, join as join21, posix as posix3 } from "node:path";
|
|
15647
|
+
function within(path, folder) {
|
|
15648
|
+
return path === folder || path.startsWith(`${folder}/`);
|
|
15649
|
+
}
|
|
15563
15650
|
|
|
15564
15651
|
class ProjectScripts {
|
|
15565
15652
|
sources = new Map;
|
|
15566
15653
|
classes = new Map;
|
|
15567
15654
|
read = new Map;
|
|
15568
15655
|
projectPath;
|
|
15656
|
+
notSearched;
|
|
15657
|
+
guessWindow;
|
|
15569
15658
|
constructor(projectPath) {
|
|
15570
15659
|
this.projectPath = projectPath;
|
|
15660
|
+
let settings = {};
|
|
15661
|
+
try {
|
|
15662
|
+
settings = parseProjectGodot(readFileSync18(join21(projectPath, "project.godot"), "utf8"))["gdharness"] ?? {};
|
|
15663
|
+
} catch {}
|
|
15664
|
+
const [section, key] = [NOT_SEARCHED_SETTING, GUESS_WINDOW_SETTING].map((setting) => setting.slice("gdharness/".length));
|
|
15665
|
+
const listed = settings[section ?? ""];
|
|
15666
|
+
this.notSearched = typeof listed === "string" ? [...listed.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map((found) => (found[1] ?? "").replace(/\/+$/, "")) : [];
|
|
15667
|
+
this.guessWindow = settings[key ?? ""] !== false;
|
|
15571
15668
|
eachScript(projectPath, (script, source) => {
|
|
15572
15669
|
this.sources.set(script, source);
|
|
15573
15670
|
const declared = declaredClassOf(source);
|
|
@@ -15628,7 +15725,7 @@ class ProjectScripts {
|
|
|
15628
15725
|
}
|
|
15629
15726
|
const declared = NEEDS_WINDOW.exec(read.code)?.[1];
|
|
15630
15727
|
said ??= declared === undefined ? undefined : declared === "true";
|
|
15631
|
-
guessed ||= SCENE_RUNNER.test(read.code);
|
|
15728
|
+
guessed ||= this.guessWindow && SCENE_RUNNER.test(read.code);
|
|
15632
15729
|
const base = this.base(at);
|
|
15633
15730
|
if (base === null) {
|
|
15634
15731
|
return null;
|
|
@@ -15644,8 +15741,16 @@ class ProjectScripts {
|
|
|
15644
15741
|
return null;
|
|
15645
15742
|
}
|
|
15646
15743
|
suitesUnder(roots) {
|
|
15647
|
-
const
|
|
15648
|
-
return [...this.sources.keys()].filter((path) =>
|
|
15744
|
+
const searched = roots.map((root) => root.replace(/\/+$/, ""));
|
|
15745
|
+
return [...this.sources.keys()].filter((path) => searched.some((root) => path.startsWith(`${root}/`) && !this.notSearched.some((folder) => within(path, folder) && !within(root, folder)))).sort().flatMap((path) => this.suite(path) ?? []);
|
|
15746
|
+
}
|
|
15747
|
+
searchedAsTold(paths) {
|
|
15748
|
+
return [
|
|
15749
|
+
...new Set(paths.flatMap((path) => {
|
|
15750
|
+
const root = path.replace(/\/+$/, "");
|
|
15751
|
+
return path.endsWith(".gd") || !this.notSearched.some((folder) => folder.startsWith(`${root}/`) && !within(root, folder)) ? [path] : this.suitesUnder([root]).map((suite) => suite.path);
|
|
15752
|
+
}))
|
|
15753
|
+
];
|
|
15649
15754
|
}
|
|
15650
15755
|
}
|
|
15651
15756
|
function readTimes(file) {
|
|
@@ -15657,7 +15762,7 @@ function readTimes(file) {
|
|
|
15657
15762
|
}
|
|
15658
15763
|
}
|
|
15659
15764
|
function writeJson(file, value) {
|
|
15660
|
-
|
|
15765
|
+
mkdirSync8(dirname10(file), { recursive: true });
|
|
15661
15766
|
writeWhole(file, `${JSON.stringify(value, null, 1)}
|
|
15662
15767
|
`);
|
|
15663
15768
|
}
|
|
@@ -15866,8 +15971,7 @@ function planOf(bin, declared, windowed, lane, all) {
|
|
|
15866
15971
|
lane
|
|
15867
15972
|
};
|
|
15868
15973
|
}
|
|
15869
|
-
function windowNeedsOf(
|
|
15870
|
-
const scripts = new ProjectScripts(projectPath);
|
|
15974
|
+
function windowNeedsOf(scripts, paths) {
|
|
15871
15975
|
const suites = new Map(paths.flatMap((path) => path.endsWith(".gd") ? scripts.suite(path) ?? [] : scripts.suitesUnder([path])).map((suite) => [suite.path, suite]));
|
|
15872
15976
|
const sorted = [...suites.values()].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
15873
15977
|
return {
|
|
@@ -15885,10 +15989,11 @@ function viewportOf(projectPath) {
|
|
|
15885
15989
|
const height = /^window\/size\/viewport_height\s*=\s*(\d+)/m.exec(text)?.[1];
|
|
15886
15990
|
return width === undefined || height === undefined ? null : { width: Number(width), height: Number(height) };
|
|
15887
15991
|
}
|
|
15888
|
-
var SUITE_BASE = "GdUnitTestSuite", CASE_PREFIX = "test_", NEEDS_WINDOW, TEST_DIRECTORIES, SCENE_RUNNER, UNTIMED_CASE = 0.02, SPLIT_PAST_SECONDS = 10, ENGINE_START_SECONDS = 5;
|
|
15992
|
+
var SUITE_BASE = "GdUnitTestSuite", CASE_PREFIX = "test_", NEEDS_WINDOW, TEST_DIRECTORIES, SCENE_RUNNER, NOT_SEARCHED_SETTING = "gdharness/tests/not_searched", GUESS_WINDOW_SETTING = "gdharness/tests/scene_runner_needs_window", UNTIMED_CASE = 0.02, SPLIT_PAST_SECONDS = 10, ENGINE_START_SECONDS = 5;
|
|
15889
15993
|
var init_suite_plan = __esm(() => {
|
|
15890
15994
|
init_class_cache();
|
|
15891
15995
|
init_gdscript_source();
|
|
15996
|
+
init_resources();
|
|
15892
15997
|
init_whole_file();
|
|
15893
15998
|
NEEDS_WINDOW = /^const\s+GDHARNESS_NEEDS_WINDOW\b[^=\n]*=\s*(true|false)\b/m;
|
|
15894
15999
|
TEST_DIRECTORIES = ["tests", "test"];
|
|
@@ -37497,12 +37602,12 @@ var init_stdio = __esm(() => {
|
|
|
37497
37602
|
|
|
37498
37603
|
// src/autoload-failure.ts
|
|
37499
37604
|
import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
|
|
37500
|
-
import { join as
|
|
37605
|
+
import { join as join24 } from "node:path";
|
|
37501
37606
|
function scriptsAutoloaded(projectPath) {
|
|
37502
37607
|
const scripts = new Map;
|
|
37503
37608
|
let settings;
|
|
37504
37609
|
try {
|
|
37505
|
-
settings = parseProjectGodot(readFileSync21(
|
|
37610
|
+
settings = parseProjectGodot(readFileSync21(join24(projectPath, "project.godot"), "utf8"));
|
|
37506
37611
|
} catch {
|
|
37507
37612
|
return scripts;
|
|
37508
37613
|
}
|
|
@@ -37511,7 +37616,7 @@ function scriptsAutoloaded(projectPath) {
|
|
|
37511
37616
|
scripts.set(`res://${path}`, name);
|
|
37512
37617
|
continue;
|
|
37513
37618
|
}
|
|
37514
|
-
const scene =
|
|
37619
|
+
const scene = join24(projectPath, path);
|
|
37515
37620
|
if (!existsSync20(scene)) {
|
|
37516
37621
|
continue;
|
|
37517
37622
|
}
|
|
@@ -37538,7 +37643,7 @@ var init_autoload_failure = __esm(() => {
|
|
|
37538
37643
|
import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as readFileSync22, statSync as statSync14, unlinkSync as unlinkSync2 } from "node:fs";
|
|
37539
37644
|
import { createConnection } from "node:net";
|
|
37540
37645
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
37541
|
-
import { dirname as dirname12, join as
|
|
37646
|
+
import { dirname as dirname12, join as join25, resolve as resolve6 } from "node:path";
|
|
37542
37647
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
37543
37648
|
function runtimeDirectory(variables = process.env) {
|
|
37544
37649
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -37546,19 +37651,19 @@ function runtimeDirectory(variables = process.env) {
|
|
|
37546
37651
|
return explicit;
|
|
37547
37652
|
}
|
|
37548
37653
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
37549
|
-
return
|
|
37654
|
+
return join25(perUser ?? tmpdir5(), "gdharness");
|
|
37550
37655
|
}
|
|
37551
37656
|
function runtimeDirectories(variables = process.env) {
|
|
37552
37657
|
const candidates = [runtimeDirectory(variables)];
|
|
37553
37658
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
37554
37659
|
for (const base of fallbacks) {
|
|
37555
|
-
candidates.push(
|
|
37660
|
+
candidates.push(join25(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
37556
37661
|
}
|
|
37557
37662
|
return [...new Set(candidates.map((path) => resolve6(path)))];
|
|
37558
37663
|
}
|
|
37559
37664
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
37560
37665
|
for (const directory of directories) {
|
|
37561
|
-
const path =
|
|
37666
|
+
const path = join25(directory, `runtime-${pid}.log`);
|
|
37562
37667
|
if (existsSync21(path)) {
|
|
37563
37668
|
return path;
|
|
37564
37669
|
}
|
|
@@ -37714,14 +37819,14 @@ function announcedIn(directory) {
|
|
|
37714
37819
|
for (const entry of readdirSync13(directory)) {
|
|
37715
37820
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
37716
37821
|
if (report) {
|
|
37717
|
-
sweepErrorReport(
|
|
37822
|
+
sweepErrorReport(join25(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
37718
37823
|
continue;
|
|
37719
37824
|
}
|
|
37720
37825
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
37721
37826
|
if (!match) {
|
|
37722
37827
|
continue;
|
|
37723
37828
|
}
|
|
37724
|
-
const file =
|
|
37829
|
+
const file = join25(directory, entry);
|
|
37725
37830
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
37726
37831
|
const running = alive(pid);
|
|
37727
37832
|
entries.push({ file, pid, running });
|
|
@@ -37749,7 +37854,7 @@ function announcedIn(directory) {
|
|
|
37749
37854
|
found.push(announced);
|
|
37750
37855
|
}
|
|
37751
37856
|
const held = new Set(entries.map((one) => one.file));
|
|
37752
|
-
const here =
|
|
37857
|
+
const here = join25(directory, ".");
|
|
37753
37858
|
for (const file of verdicts.keys()) {
|
|
37754
37859
|
if (dirname12(file) === here && !held.has(file)) {
|
|
37755
37860
|
verdicts.delete(file);
|
|
@@ -37758,7 +37863,7 @@ function announcedIn(directory) {
|
|
|
37758
37863
|
return found;
|
|
37759
37864
|
}
|
|
37760
37865
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
37761
|
-
const files = directories.map((directory) =>
|
|
37866
|
+
const files = directories.map((directory) => join25(directory, `runtime-${pid}.json`)).filter((file) => existsSync21(file));
|
|
37762
37867
|
const first = files[0];
|
|
37763
37868
|
if (first === undefined) {
|
|
37764
37869
|
return;
|
|
@@ -38177,10 +38282,10 @@ var init_runtime_client = __esm(() => {
|
|
|
38177
38282
|
});
|
|
38178
38283
|
|
|
38179
38284
|
// src/boot-note.ts
|
|
38180
|
-
import { mkdirSync as
|
|
38181
|
-
import { dirname as dirname13, join as
|
|
38285
|
+
import { mkdirSync as mkdirSync10, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "node:fs";
|
|
38286
|
+
import { dirname as dirname13, join as join26 } from "node:path";
|
|
38182
38287
|
function bootNotePath(projectPath) {
|
|
38183
|
-
return
|
|
38288
|
+
return join26(projectPath, ".godot", "gdharness-boot.json");
|
|
38184
38289
|
}
|
|
38185
38290
|
function readScenes(projectPath) {
|
|
38186
38291
|
try {
|
|
@@ -38200,7 +38305,7 @@ function readBootNote(projectPath, scene) {
|
|
|
38200
38305
|
function writeBootNote(projectPath, scene, announcedAfterMs) {
|
|
38201
38306
|
try {
|
|
38202
38307
|
const path = bootNotePath(projectPath);
|
|
38203
|
-
|
|
38308
|
+
mkdirSync10(dirname13(path), { recursive: true });
|
|
38204
38309
|
const scenes = {
|
|
38205
38310
|
...readScenes(projectPath),
|
|
38206
38311
|
[scene]: {
|
|
@@ -38208,7 +38313,7 @@ function writeBootNote(projectPath, scene, announcedAfterMs) {
|
|
|
38208
38313
|
at: new Date().toISOString()
|
|
38209
38314
|
}
|
|
38210
38315
|
};
|
|
38211
|
-
|
|
38316
|
+
writeFileSync12(path, JSON.stringify({ scenes }, null, 2), "utf8");
|
|
38212
38317
|
} catch {}
|
|
38213
38318
|
}
|
|
38214
38319
|
function halfAsLongAgain(lastBootMs) {
|
|
@@ -38226,10 +38331,10 @@ var init_boot_note = __esm(() => {
|
|
|
38226
38331
|
});
|
|
38227
38332
|
|
|
38228
38333
|
// src/breakpoint-note.ts
|
|
38229
|
-
import { mkdirSync as
|
|
38230
|
-
import { dirname as dirname14, isAbsolute as isAbsolute7, join as
|
|
38334
|
+
import { mkdirSync as mkdirSync11, readFileSync as readFileSync24, rmSync as rmSync11, writeFileSync as writeFileSync13 } from "node:fs";
|
|
38335
|
+
import { dirname as dirname14, isAbsolute as isAbsolute7, join as join27, relative as relative4 } from "node:path";
|
|
38231
38336
|
function breakpointNotePath(projectPath) {
|
|
38232
|
-
return
|
|
38337
|
+
return join27(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
38233
38338
|
}
|
|
38234
38339
|
function writeBreakpointNote(projectPath, held) {
|
|
38235
38340
|
const path = breakpointNotePath(projectPath);
|
|
@@ -38246,8 +38351,8 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
38246
38351
|
lines: [...one.lines]
|
|
38247
38352
|
}))
|
|
38248
38353
|
};
|
|
38249
|
-
|
|
38250
|
-
|
|
38354
|
+
mkdirSync11(dirname14(path), { recursive: true });
|
|
38355
|
+
writeFileSync13(path, `${JSON.stringify(note, null, 2)}
|
|
38251
38356
|
`, "utf8");
|
|
38252
38357
|
} catch (error) {
|
|
38253
38358
|
console.error(`[SERVER] Could not record the breakpoints at ${path}: ${errorMessage(error)}`);
|
|
@@ -38270,7 +38375,7 @@ function readBreakpointNote(projectPath) {
|
|
|
38270
38375
|
}
|
|
38271
38376
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
38272
38377
|
if (lines.length > 0) {
|
|
38273
|
-
held.push({ scriptPath: realAsFarAsItExists(
|
|
38378
|
+
held.push({ scriptPath: realAsFarAsItExists(join27(projectPath, script)), lines });
|
|
38274
38379
|
}
|
|
38275
38380
|
}
|
|
38276
38381
|
return held;
|
|
@@ -38288,10 +38393,10 @@ var init_breakpoint_note = __esm(() => {
|
|
|
38288
38393
|
});
|
|
38289
38394
|
|
|
38290
38395
|
// src/class-note.ts
|
|
38291
|
-
import { mkdirSync as
|
|
38292
|
-
import { dirname as dirname15, join as
|
|
38396
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync25 } from "node:fs";
|
|
38397
|
+
import { dirname as dirname15, join as join28 } from "node:path";
|
|
38293
38398
|
function classNotePath(projectPath) {
|
|
38294
|
-
return
|
|
38399
|
+
return join28(projectPath, ".godot", "gdharness-classes.json");
|
|
38295
38400
|
}
|
|
38296
38401
|
function readClassNote(projectPath) {
|
|
38297
38402
|
try {
|
|
@@ -38325,7 +38430,7 @@ function unfinishedScanIn(value) {
|
|
|
38325
38430
|
function writeClassNote(projectPath, note) {
|
|
38326
38431
|
const path = classNotePath(projectPath);
|
|
38327
38432
|
try {
|
|
38328
|
-
|
|
38433
|
+
mkdirSync12(dirname15(path), { recursive: true });
|
|
38329
38434
|
} catch {
|
|
38330
38435
|
return;
|
|
38331
38436
|
}
|
|
@@ -38338,9 +38443,9 @@ var init_class_note = __esm(() => {
|
|
|
38338
38443
|
|
|
38339
38444
|
// src/config-pin.ts
|
|
38340
38445
|
import { existsSync as existsSync22, readFileSync as readFileSync26 } from "node:fs";
|
|
38341
|
-
import { join as
|
|
38446
|
+
import { join as join29 } from "node:path";
|
|
38342
38447
|
function versionsNamedInConfig(projectPath) {
|
|
38343
|
-
const config =
|
|
38448
|
+
const config = join29(projectPath, ".mcp.json");
|
|
38344
38449
|
if (!existsSync22(config)) {
|
|
38345
38450
|
return [];
|
|
38346
38451
|
}
|
|
@@ -38367,16 +38472,16 @@ var init_config_pin = __esm(() => {
|
|
|
38367
38472
|
});
|
|
38368
38473
|
|
|
38369
38474
|
// src/cut-test-run.ts
|
|
38370
|
-
import { existsSync as existsSync23, mkdirSync as
|
|
38371
|
-
import { dirname as dirname16, join as
|
|
38475
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync13, readFileSync as readFileSync27, rmSync as rmSync12, writeFileSync as writeFileSync14 } from "node:fs";
|
|
38476
|
+
import { dirname as dirname16, join as join30 } from "node:path";
|
|
38372
38477
|
function keptAt(projectPath) {
|
|
38373
|
-
return
|
|
38478
|
+
return join30(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
38374
38479
|
}
|
|
38375
38480
|
function keepCutTestRun(projectPath, run) {
|
|
38376
38481
|
const path = keptAt(projectPath);
|
|
38377
38482
|
try {
|
|
38378
|
-
|
|
38379
|
-
|
|
38483
|
+
mkdirSync13(dirname16(path), { recursive: true });
|
|
38484
|
+
writeFileSync14(path, `${JSON.stringify(run, null, 2)}
|
|
38380
38485
|
`, "utf8");
|
|
38381
38486
|
} catch {}
|
|
38382
38487
|
}
|
|
@@ -39246,20 +39351,20 @@ var init_dap_client = __esm(() => {
|
|
|
39246
39351
|
import {
|
|
39247
39352
|
closeSync as closeSync4,
|
|
39248
39353
|
existsSync as existsSync24,
|
|
39249
|
-
mkdirSync as
|
|
39354
|
+
mkdirSync as mkdirSync14,
|
|
39250
39355
|
openSync as openSync4,
|
|
39251
39356
|
readFileSync as readFileSync28,
|
|
39252
39357
|
readSync as readSync2,
|
|
39253
39358
|
rmSync as rmSync13,
|
|
39254
39359
|
statSync as statSync15,
|
|
39255
|
-
writeFileSync as
|
|
39360
|
+
writeFileSync as writeFileSync15
|
|
39256
39361
|
} from "node:fs";
|
|
39257
|
-
import { dirname as dirname17, join as
|
|
39362
|
+
import { dirname as dirname17, join as join31 } from "node:path";
|
|
39258
39363
|
function editorLogPath(projectPath) {
|
|
39259
|
-
return
|
|
39364
|
+
return join31(projectPath, ".godot", "gdharness-editor.log");
|
|
39260
39365
|
}
|
|
39261
39366
|
function editorLogNotePath(projectPath) {
|
|
39262
|
-
return
|
|
39367
|
+
return join31(projectPath, ".godot", "gdharness-editor.json");
|
|
39263
39368
|
}
|
|
39264
39369
|
function readEditorLogNote(projectPath) {
|
|
39265
39370
|
try {
|
|
@@ -39275,7 +39380,7 @@ function readEditorLogNote(projectPath) {
|
|
|
39275
39380
|
function clearEditorLog(projectPath) {
|
|
39276
39381
|
try {
|
|
39277
39382
|
const path = editorLogPath(projectPath);
|
|
39278
|
-
|
|
39383
|
+
mkdirSync14(dirname17(path), { recursive: true });
|
|
39279
39384
|
rmSync13(path, { force: true });
|
|
39280
39385
|
rmSync13(editorLogNotePath(projectPath), { force: true });
|
|
39281
39386
|
} catch {}
|
|
@@ -39283,8 +39388,8 @@ function clearEditorLog(projectPath) {
|
|
|
39283
39388
|
function writeEditorLogNote(projectPath, pid, processStartedAt) {
|
|
39284
39389
|
try {
|
|
39285
39390
|
const path = editorLogNotePath(projectPath);
|
|
39286
|
-
|
|
39287
|
-
|
|
39391
|
+
mkdirSync14(dirname17(path), { recursive: true });
|
|
39392
|
+
writeFileSync15(path, JSON.stringify({
|
|
39288
39393
|
pid,
|
|
39289
39394
|
startedAt: new Date().toISOString(),
|
|
39290
39395
|
...processStartedAt === undefined ? {} : { processStartedAt }
|
|
@@ -39528,7 +39633,7 @@ var init_editor_log = __esm(() => {
|
|
|
39528
39633
|
});
|
|
39529
39634
|
|
|
39530
39635
|
// src/rename.ts
|
|
39531
|
-
import { existsSync as existsSync25, mkdirSync as
|
|
39636
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync15, readFileSync as readFileSync29, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync16 } from "node:fs";
|
|
39532
39637
|
import { dirname as dirname18, posix as posix4 } from "node:path";
|
|
39533
39638
|
|
|
39534
39639
|
class Script {
|
|
@@ -40244,9 +40349,9 @@ function applyRewrites(rewrites) {
|
|
|
40244
40349
|
const done = [];
|
|
40245
40350
|
try {
|
|
40246
40351
|
for (const rewrite of rewrites) {
|
|
40247
|
-
|
|
40352
|
+
mkdirSync15(dirname18(rewrite.writeTo), { recursive: true });
|
|
40248
40353
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
40249
|
-
|
|
40354
|
+
writeFileSync16(staging, rewrite.after, "utf8");
|
|
40250
40355
|
renameSync3(staging, rewrite.writeTo);
|
|
40251
40356
|
let movedUid = false;
|
|
40252
40357
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
@@ -40261,7 +40366,7 @@ function applyRewrites(rewrites) {
|
|
|
40261
40366
|
} catch (error) {
|
|
40262
40367
|
for (const { rewrite, movedUid } of done.reverse()) {
|
|
40263
40368
|
try {
|
|
40264
|
-
|
|
40369
|
+
writeFileSync16(rewrite.absolute, rewrite.before, "utf8");
|
|
40265
40370
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
40266
40371
|
unlinkSync3(rewrite.writeTo);
|
|
40267
40372
|
if (movedUid) {
|
|
@@ -40300,7 +40405,7 @@ var init_rename = __esm(() => {
|
|
|
40300
40405
|
import { existsSync as existsSync26, realpathSync as realpathSync3 } from "node:fs";
|
|
40301
40406
|
import { readFile, realpath } from "node:fs/promises";
|
|
40302
40407
|
import { createConnection as createConnection3 } from "node:net";
|
|
40303
|
-
import { dirname as dirname19, join as
|
|
40408
|
+
import { dirname as dirname19, join as join32, resolve as resolve7 } from "node:path";
|
|
40304
40409
|
import { setTimeout as delay6 } from "node:timers/promises";
|
|
40305
40410
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
40306
40411
|
function initializeTimeoutMs() {
|
|
@@ -40978,7 +41083,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
40978
41083
|
} catch {
|
|
40979
41084
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
40980
41085
|
}
|
|
40981
|
-
if (!existsSync26(
|
|
41086
|
+
if (!existsSync26(join32(projectPath, "project.godot"))) {
|
|
40982
41087
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
40983
41088
|
}
|
|
40984
41089
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -41138,8 +41243,8 @@ var init_lsp_client = __esm(() => {
|
|
|
41138
41243
|
});
|
|
41139
41244
|
|
|
41140
41245
|
// src/rename-symbol.ts
|
|
41141
|
-
import { existsSync as existsSync27, mkdirSync as
|
|
41142
|
-
import { basename as basename9, dirname as dirname20, join as
|
|
41246
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync16, readdirSync as readdirSync14, realpathSync as realpathSync4, rmSync as rmSync14, writeFileSync as writeFileSync17 } from "node:fs";
|
|
41247
|
+
import { basename as basename9, dirname as dirname20, join as join33, relative as relative5, resolve as resolve8 } from "node:path";
|
|
41143
41248
|
function resourcePathOf(projectPath, absolute) {
|
|
41144
41249
|
const inside = relative5(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
41145
41250
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -41152,7 +41257,7 @@ function onDiskSpelling(path) {
|
|
|
41152
41257
|
let at = resolve8(path);
|
|
41153
41258
|
for (;; ) {
|
|
41154
41259
|
try {
|
|
41155
|
-
return
|
|
41260
|
+
return join33(realpathSync4.native(at), ...missing.reverse());
|
|
41156
41261
|
} catch {
|
|
41157
41262
|
const parent = dirname20(at);
|
|
41158
41263
|
if (parent === at) {
|
|
@@ -41581,15 +41686,15 @@ ${mention.kind}`;
|
|
|
41581
41686
|
};
|
|
41582
41687
|
}
|
|
41583
41688
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
41584
|
-
const directory =
|
|
41585
|
-
|
|
41689
|
+
const directory = join33(projectPath, ".godot", "gdharness-renames");
|
|
41690
|
+
mkdirSync16(directory, { recursive: true });
|
|
41586
41691
|
detailWrites += 1;
|
|
41587
|
-
const path =
|
|
41588
|
-
|
|
41692
|
+
const path = join33(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
41693
|
+
writeFileSync17(path, `${JSON.stringify(detail, null, 2)}
|
|
41589
41694
|
`, "utf8");
|
|
41590
41695
|
const kept = readdirSync14(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
41591
41696
|
for (const old of kept.slice(10)) {
|
|
41592
|
-
rmSync14(
|
|
41697
|
+
rmSync14(join33(directory, old), { force: true });
|
|
41593
41698
|
}
|
|
41594
41699
|
return path;
|
|
41595
41700
|
}
|
|
@@ -41631,17 +41736,17 @@ var init_rename_symbol = __esm(() => {
|
|
|
41631
41736
|
// src/file-move.ts
|
|
41632
41737
|
import {
|
|
41633
41738
|
existsSync as existsSync28,
|
|
41634
|
-
mkdirSync as
|
|
41739
|
+
mkdirSync as mkdirSync17,
|
|
41635
41740
|
readdirSync as readdirSync15,
|
|
41636
41741
|
readFileSync as readFileSync30,
|
|
41637
41742
|
renameSync as renameSync4,
|
|
41638
41743
|
statSync as statSync16,
|
|
41639
|
-
writeFileSync as
|
|
41744
|
+
writeFileSync as writeFileSync18
|
|
41640
41745
|
} from "node:fs";
|
|
41641
|
-
import { basename as basename10, dirname as dirname21, extname as extname3, join as
|
|
41746
|
+
import { basename as basename10, dirname as dirname21, extname as extname3, join as join34, relative as relative6 } from "node:path";
|
|
41642
41747
|
function filesUnder(directory) {
|
|
41643
41748
|
return readdirSync15(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
41644
|
-
const path =
|
|
41749
|
+
const path = join34(directory, entry.name);
|
|
41645
41750
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
41646
41751
|
});
|
|
41647
41752
|
}
|
|
@@ -41662,8 +41767,8 @@ function unseenByTheEngine(projectPath, path) {
|
|
|
41662
41767
|
return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
|
|
41663
41768
|
}
|
|
41664
41769
|
if (at < steps.length - 1) {
|
|
41665
|
-
directory =
|
|
41666
|
-
if (existsSync28(
|
|
41770
|
+
directory = join34(directory, step);
|
|
41771
|
+
if (existsSync28(join34(directory, ".gdignore"))) {
|
|
41667
41772
|
return `${path} is under a directory holding a .gdignore, which the engine does not load`;
|
|
41668
41773
|
}
|
|
41669
41774
|
}
|
|
@@ -41675,12 +41780,12 @@ function installedAddonOf(projectPath, path) {
|
|
|
41675
41780
|
if (steps[0] !== "addons" || steps[1] === undefined) {
|
|
41676
41781
|
return null;
|
|
41677
41782
|
}
|
|
41678
|
-
return existsSync28(
|
|
41783
|
+
return existsSync28(join34(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
|
|
41679
41784
|
}
|
|
41680
41785
|
function binaryResources(projectPath) {
|
|
41681
41786
|
const found = [];
|
|
41682
41787
|
const visit = (directory, prefix) => {
|
|
41683
|
-
if (existsSync28(
|
|
41788
|
+
if (existsSync28(join34(directory, ".gdignore"))) {
|
|
41684
41789
|
return;
|
|
41685
41790
|
}
|
|
41686
41791
|
for (const entry of readdirSync15(directory, { withFileTypes: true })) {
|
|
@@ -41689,7 +41794,7 @@ function binaryResources(projectPath) {
|
|
|
41689
41794
|
}
|
|
41690
41795
|
const path = `${prefix}${entry.name}`;
|
|
41691
41796
|
if (entry.isDirectory()) {
|
|
41692
|
-
visit(
|
|
41797
|
+
visit(join34(directory, entry.name), `${path}/`);
|
|
41693
41798
|
} else if (/\.(scn|res)$/i.test(entry.name)) {
|
|
41694
41799
|
found.push(path);
|
|
41695
41800
|
}
|
|
@@ -41699,7 +41804,7 @@ function binaryResources(projectPath) {
|
|
|
41699
41804
|
return found;
|
|
41700
41805
|
}
|
|
41701
41806
|
function targetPathOf(projectPath, absolute) {
|
|
41702
|
-
const inside = relative6(onDiskSpelling(projectPath),
|
|
41807
|
+
const inside = relative6(onDiskSpelling(projectPath), join34(onDiskSpelling(dirname21(absolute)), basename10(absolute)));
|
|
41703
41808
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
41704
41809
|
return null;
|
|
41705
41810
|
}
|
|
@@ -41754,7 +41859,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
41754
41859
|
return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
|
|
41755
41860
|
}
|
|
41756
41861
|
if (!isDirectory) {
|
|
41757
|
-
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync28(
|
|
41862
|
+
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync28(join34(projectPath, path.slice("res://".length))));
|
|
41758
41863
|
if (owner !== undefined && owner !== null) {
|
|
41759
41864
|
return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
|
|
41760
41865
|
}
|
|
@@ -41788,12 +41893,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
41788
41893
|
scripts,
|
|
41789
41894
|
moves,
|
|
41790
41895
|
named: [from],
|
|
41791
|
-
exists: (path) => existsSync28(
|
|
41896
|
+
exists: (path) => existsSync28(join34(projectPath, path.slice("res://".length)))
|
|
41792
41897
|
}, { edits: [], mentions: [] });
|
|
41793
41898
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
|
|
41794
41899
|
const binaries = [];
|
|
41795
41900
|
for (const path of binaryResources(projectPath)) {
|
|
41796
|
-
const bytes = readFileSync30(
|
|
41901
|
+
const bytes = readFileSync30(join34(projectPath, path.slice("res://".length)));
|
|
41797
41902
|
const names = [...moves.keys()].find((moving) => bytes.includes(moving));
|
|
41798
41903
|
if (names !== undefined) {
|
|
41799
41904
|
binaries.push({
|
|
@@ -41812,7 +41917,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
41812
41917
|
}));
|
|
41813
41918
|
const left = leftAloneReport(mentions);
|
|
41814
41919
|
const rewrites = rewritesOf(plan, texts, null);
|
|
41815
|
-
const absoluteOf = (path) =>
|
|
41920
|
+
const absoluteOf = (path) => join34(projectPath, path.slice("res://".length));
|
|
41816
41921
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
41817
41922
|
const moved = [...moves].map(([source, target]) => {
|
|
41818
41923
|
const rewrite = rewritten.get(source);
|
|
@@ -41871,7 +41976,7 @@ function applyFileMove(outcome) {
|
|
|
41871
41976
|
}
|
|
41872
41977
|
const undo = [];
|
|
41873
41978
|
const rename = (source, target) => {
|
|
41874
|
-
|
|
41979
|
+
mkdirSync17(dirname21(target), { recursive: true });
|
|
41875
41980
|
renameSync4(source, target);
|
|
41876
41981
|
undo.push(() => {
|
|
41877
41982
|
renameSync4(target, source);
|
|
@@ -41880,10 +41985,10 @@ function applyFileMove(outcome) {
|
|
|
41880
41985
|
try {
|
|
41881
41986
|
for (const rewrite of outcome.inPlace) {
|
|
41882
41987
|
const staging = `${rewrite.absolute}.gdharness-move`;
|
|
41883
|
-
|
|
41988
|
+
writeFileSync18(staging, rewrite.after, "utf8");
|
|
41884
41989
|
renameSync4(staging, rewrite.absolute);
|
|
41885
41990
|
undo.push(() => {
|
|
41886
|
-
|
|
41991
|
+
writeFileSync18(rewrite.absolute, rewrite.before, "utf8");
|
|
41887
41992
|
});
|
|
41888
41993
|
}
|
|
41889
41994
|
if (outcome.directory !== null) {
|
|
@@ -41895,9 +42000,9 @@ function applyFileMove(outcome) {
|
|
|
41895
42000
|
}
|
|
41896
42001
|
if (file.text !== null) {
|
|
41897
42002
|
const { before, after } = file.text;
|
|
41898
|
-
|
|
42003
|
+
writeFileSync18(file.absoluteTo, after, "utf8");
|
|
41899
42004
|
undo.push(() => {
|
|
41900
|
-
|
|
42005
|
+
writeFileSync18(file.absoluteTo, before, "utf8");
|
|
41901
42006
|
});
|
|
41902
42007
|
}
|
|
41903
42008
|
}
|
|
@@ -45599,16 +45704,16 @@ var DEBUG_PREFIX = "debug ";
|
|
|
45599
45704
|
var init_keeper_channel = () => {};
|
|
45600
45705
|
|
|
45601
45706
|
// src/lifecycle-log.ts
|
|
45602
|
-
import { appendFileSync, mkdirSync as
|
|
45603
|
-
import { dirname as dirname22, join as
|
|
45707
|
+
import { appendFileSync, mkdirSync as mkdirSync18, renameSync as renameSync5, statSync as statSync17 } from "node:fs";
|
|
45708
|
+
import { dirname as dirname22, join as join35 } from "node:path";
|
|
45604
45709
|
import process14 from "node:process";
|
|
45605
45710
|
function lifecycleLogPath() {
|
|
45606
|
-
return
|
|
45711
|
+
return join35(runtimeDirectory(), "servers.log");
|
|
45607
45712
|
}
|
|
45608
45713
|
function writeLifecycle(event, fields = {}) {
|
|
45609
45714
|
const path = lifecycleLogPath();
|
|
45610
45715
|
try {
|
|
45611
|
-
|
|
45716
|
+
mkdirSync18(dirname22(path), { recursive: true });
|
|
45612
45717
|
if ((statSync17(path, { throwIfNoEntry: false })?.size ?? 0) > LIFECYCLE_LOG_LIMIT) {
|
|
45613
45718
|
try {
|
|
45614
45719
|
renameSync5(path, `${path}.1`);
|
|
@@ -45967,16 +46072,16 @@ var init_prompts = __esm(() => {
|
|
|
45967
46072
|
});
|
|
45968
46073
|
|
|
45969
46074
|
// src/restart-note.ts
|
|
45970
|
-
import { mkdirSync as
|
|
45971
|
-
import { dirname as dirname23, join as
|
|
46075
|
+
import { mkdirSync as mkdirSync19, readFileSync as readFileSync31, rmSync as rmSync15, writeFileSync as writeFileSync19 } from "node:fs";
|
|
46076
|
+
import { dirname as dirname23, join as join36 } from "node:path";
|
|
45972
46077
|
function restartNotePath(projectPath) {
|
|
45973
|
-
return
|
|
46078
|
+
return join36(projectPath, ".godot", "gdharness-restart.json");
|
|
45974
46079
|
}
|
|
45975
46080
|
function noteRestartBegun(note) {
|
|
45976
46081
|
const path = restartNotePath(note.projectPath);
|
|
45977
46082
|
try {
|
|
45978
|
-
|
|
45979
|
-
|
|
46083
|
+
mkdirSync19(dirname23(path), { recursive: true });
|
|
46084
|
+
writeFileSync19(path, `${JSON.stringify(note, null, 2)}
|
|
45980
46085
|
`, "utf8");
|
|
45981
46086
|
} catch (error) {
|
|
45982
46087
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -46010,10 +46115,10 @@ var init_restart_note = __esm(() => {
|
|
|
46010
46115
|
|
|
46011
46116
|
// src/run-record.ts
|
|
46012
46117
|
import { createHash as createHash7 } from "node:crypto";
|
|
46013
|
-
import { mkdirSync as
|
|
46014
|
-
import { basename as basename11, join as
|
|
46118
|
+
import { mkdirSync as mkdirSync20, openSync as openSync5, readdirSync as readdirSync16, readFileSync as readFileSync32, rmSync as rmSync16, statSync as statSync18 } from "node:fs";
|
|
46119
|
+
import { basename as basename11, join as join37, resolve as resolve9 } from "node:path";
|
|
46015
46120
|
function runsDirectory() {
|
|
46016
|
-
return
|
|
46121
|
+
return join37(runtimeDirectory(), "runs");
|
|
46017
46122
|
}
|
|
46018
46123
|
function projectKey(projectPath) {
|
|
46019
46124
|
const real = realAsFarAsItExists(resolve9(projectPath)).replace(/[\\/]+$/, "");
|
|
@@ -46021,14 +46126,14 @@ function projectKey(projectPath) {
|
|
|
46021
46126
|
return createHash7("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
46022
46127
|
}
|
|
46023
46128
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
46024
|
-
return
|
|
46129
|
+
return join37(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
46025
46130
|
}
|
|
46026
46131
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
46027
|
-
return
|
|
46132
|
+
return join37(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
46028
46133
|
}
|
|
46029
46134
|
function writeLatestRun(servedBy, latest) {
|
|
46030
46135
|
try {
|
|
46031
|
-
|
|
46136
|
+
mkdirSync20(runsDirectory(), { recursive: true });
|
|
46032
46137
|
writeWhole(latestRunPath(servedBy), JSON.stringify(latest, null, 2));
|
|
46033
46138
|
} catch {}
|
|
46034
46139
|
}
|
|
@@ -46048,24 +46153,24 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
46048
46153
|
}
|
|
46049
46154
|
}
|
|
46050
46155
|
function openTranscript(startedAt) {
|
|
46051
|
-
|
|
46052
|
-
const path =
|
|
46156
|
+
mkdirSync20(runsDirectory(), { recursive: true });
|
|
46157
|
+
const path = join37(runsDirectory(), `run-${startedAt}.log`);
|
|
46053
46158
|
return { path, fd: openSync5(path, "a") };
|
|
46054
46159
|
}
|
|
46055
46160
|
function profilePath(startedAt) {
|
|
46056
|
-
return
|
|
46161
|
+
return join37(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
46057
46162
|
}
|
|
46058
46163
|
function profileMarkPath(startedAt) {
|
|
46059
|
-
return
|
|
46164
|
+
return join37(runsDirectory(), `run-${startedAt}.profile-mark.json`);
|
|
46060
46165
|
}
|
|
46061
46166
|
function noteIsAbout(note, playing) {
|
|
46062
46167
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
46063
46168
|
}
|
|
46064
46169
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
46065
|
-
return
|
|
46170
|
+
return join37(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
46066
46171
|
}
|
|
46067
46172
|
function writeEditorRunNote(note) {
|
|
46068
|
-
|
|
46173
|
+
mkdirSync20(runsDirectory(), { recursive: true });
|
|
46069
46174
|
const path = editorNotePath(note.projectPath);
|
|
46070
46175
|
if (!writeWhole(path, JSON.stringify(note, null, 2))) {
|
|
46071
46176
|
throw new Error(`the editor run note could not be written at ${path}`);
|
|
@@ -46115,13 +46220,13 @@ function everyRunRecord() {
|
|
|
46115
46220
|
for (const directory of runtimeDirectories()) {
|
|
46116
46221
|
let entries;
|
|
46117
46222
|
try {
|
|
46118
|
-
entries = readdirSync16(
|
|
46223
|
+
entries = readdirSync16(join37(directory, "runs"));
|
|
46119
46224
|
} catch {
|
|
46120
46225
|
continue;
|
|
46121
46226
|
}
|
|
46122
46227
|
for (const entry of entries) {
|
|
46123
46228
|
if (entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
46124
|
-
const record = recordAt(
|
|
46229
|
+
const record = recordAt(join37(directory, "runs", entry));
|
|
46125
46230
|
if (record !== null) {
|
|
46126
46231
|
found.push(record);
|
|
46127
46232
|
}
|
|
@@ -46389,7 +46494,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
46389
46494
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
46390
46495
|
continue;
|
|
46391
46496
|
}
|
|
46392
|
-
const path =
|
|
46497
|
+
const path = join37(directory, entry);
|
|
46393
46498
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
46394
46499
|
continue;
|
|
46395
46500
|
}
|
|
@@ -46639,18 +46744,18 @@ var init_server_instructions = __esm(() => {
|
|
|
46639
46744
|
|
|
46640
46745
|
// src/test-selection.ts
|
|
46641
46746
|
import { existsSync as existsSync29, readFileSync as readFileSync34 } from "node:fs";
|
|
46642
|
-
import { basename as basename12, join as
|
|
46747
|
+
import { basename as basename12, join as join38 } from "node:path";
|
|
46643
46748
|
function casesIn(projectPath, suite) {
|
|
46644
|
-
const text = readFileSync34(
|
|
46749
|
+
const text = readFileSync34(join38(projectPath, suite.slice("res://".length)), "utf8");
|
|
46645
46750
|
return [...text.matchAll(/^func\s+(test_\w*)\s*\(/gm)].map((match) => match[1] ?? "");
|
|
46646
46751
|
}
|
|
46647
46752
|
function scriptsWithoutCases(projectPath, asked) {
|
|
46648
|
-
return asked.filter((path) => path.endsWith(".gd") && existsSync29(
|
|
46753
|
+
return asked.filter((path) => path.endsWith(".gd") && existsSync29(join38(projectPath, path.slice("res://".length))) && casesIn(projectPath, path).length === 0);
|
|
46649
46754
|
}
|
|
46650
46755
|
function suitesUnder(projectPath, asked) {
|
|
46651
46756
|
const scripts = projectFiles(projectPath, [".gd"]).map((path) => `res://${path}`);
|
|
46652
46757
|
return [
|
|
46653
|
-
...new Set(asked.flatMap((path) => path.endsWith(".gd") ? existsSync29(
|
|
46758
|
+
...new Set(asked.flatMap((path) => path.endsWith(".gd") ? existsSync29(join38(projectPath, path.slice("res://".length))) ? [path] : [] : scripts.filter((script) => script.startsWith(`${path}/`))))
|
|
46654
46759
|
];
|
|
46655
46760
|
}
|
|
46656
46761
|
function selectTests(projectPath, asked, given) {
|
|
@@ -46713,7 +46818,7 @@ import {
|
|
|
46713
46818
|
copyFileSync,
|
|
46714
46819
|
existsSync as existsSync30,
|
|
46715
46820
|
fstatSync as fstatSync2,
|
|
46716
|
-
mkdirSync as
|
|
46821
|
+
mkdirSync as mkdirSync21,
|
|
46717
46822
|
openSync as openSync6,
|
|
46718
46823
|
readdirSync as readdirSync17,
|
|
46719
46824
|
readFileSync as readFileSync35,
|
|
@@ -46721,9 +46826,9 @@ import {
|
|
|
46721
46826
|
realpathSync as realpathSync5,
|
|
46722
46827
|
rmdirSync as rmdirSync2,
|
|
46723
46828
|
statSync as statSync19,
|
|
46724
|
-
writeFileSync as
|
|
46829
|
+
writeFileSync as writeFileSync20
|
|
46725
46830
|
} from "node:fs";
|
|
46726
|
-
import { basename as basename13, dirname as dirname24, extname as extname4, isAbsolute as isAbsolute8, join as
|
|
46831
|
+
import { basename as basename13, dirname as dirname24, extname as extname4, isAbsolute as isAbsolute8, join as join39, normalize as normalize2, relative as relative7 } from "node:path";
|
|
46727
46832
|
import { setTimeout as delay7 } from "node:timers/promises";
|
|
46728
46833
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
46729
46834
|
function editorRestartTimeoutMs() {
|
|
@@ -46782,7 +46887,7 @@ function writtenFileRefusal(path, rule) {
|
|
|
46782
46887
|
if (!existsSync30(directory)) {
|
|
46783
46888
|
return `${named}'s directory ${dirname24(path)} does not exist; ${what} creates no directories.`;
|
|
46784
46889
|
}
|
|
46785
|
-
const where =
|
|
46890
|
+
const where = join39(directory, basename13(path));
|
|
46786
46891
|
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realAsFarAsItExists(project), where));
|
|
46787
46892
|
if (inside !== undefined) {
|
|
46788
46893
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
@@ -47429,7 +47534,7 @@ function notRunNote(notRun, failFast) {
|
|
|
47429
47534
|
function presetsIn(projectPath) {
|
|
47430
47535
|
let text;
|
|
47431
47536
|
try {
|
|
47432
|
-
text = readFileSync35(
|
|
47537
|
+
text = readFileSync35(join39(projectPath, "export_presets.cfg"), "utf8");
|
|
47433
47538
|
} catch {
|
|
47434
47539
|
return null;
|
|
47435
47540
|
}
|
|
@@ -47510,7 +47615,7 @@ function filesIn(folder) {
|
|
|
47510
47615
|
try {
|
|
47511
47616
|
for (const entry of readdirSync17(folder, { withFileTypes: true })) {
|
|
47512
47617
|
if (entry.isFile()) {
|
|
47513
|
-
found.set(entry.name, statSync19(
|
|
47618
|
+
found.set(entry.name, statSync19(join39(folder, entry.name)).mtimeMs);
|
|
47514
47619
|
}
|
|
47515
47620
|
}
|
|
47516
47621
|
} catch {}
|
|
@@ -47550,7 +47655,7 @@ function unsavedInEditorNote(scripts) {
|
|
|
47550
47655
|
return `${named} ${one ? "is" : "are"} open in the editor's script ${one ? "tab" : "tabs"} with unsaved edits, and saving ${one ? "that tab" : "them"} there writes ${one ? "its" : "their"} text over what was written here: close ${one ? "it" : "them"} without saving, or save first and edit again.`;
|
|
47551
47656
|
}
|
|
47552
47657
|
function editorAddonMissing(projectPath) {
|
|
47553
|
-
if (!existsSync30(
|
|
47658
|
+
if (!existsSync30(join39(projectPath, "project.godot"))) {
|
|
47554
47659
|
return null;
|
|
47555
47660
|
}
|
|
47556
47661
|
const report = inspectProject(projectPath);
|
|
@@ -47578,7 +47683,7 @@ function rewriteBusNamed(projectPath, from, to) {
|
|
|
47578
47683
|
const giving = [`bus = &"${quoted(from)}"`, `bus = "${quoted(from)}"`];
|
|
47579
47684
|
const rewritten = [];
|
|
47580
47685
|
for (const file of projectFiles(projectPath, [".tscn", ".tres"])) {
|
|
47581
|
-
const absolute =
|
|
47686
|
+
const absolute = join39(projectPath, file);
|
|
47582
47687
|
const lines = readFileSync35(absolute, "utf8").split(`
|
|
47583
47688
|
`);
|
|
47584
47689
|
const at = [];
|
|
@@ -47591,14 +47696,14 @@ function rewriteBusNamed(projectPath, from, to) {
|
|
|
47591
47696
|
return `bus = &"${quoted(to)}"${ending}`;
|
|
47592
47697
|
});
|
|
47593
47698
|
if (at.length > 0) {
|
|
47594
|
-
|
|
47699
|
+
writeFileSync20(absolute, after.join(`
|
|
47595
47700
|
`));
|
|
47596
47701
|
rewritten.push({ file: `res://${file}`, lines: at });
|
|
47597
47702
|
}
|
|
47598
47703
|
}
|
|
47599
47704
|
const namedInScripts = [];
|
|
47600
47705
|
for (const file of projectFiles(projectPath, [".gd"])) {
|
|
47601
|
-
readFileSync35(
|
|
47706
|
+
readFileSync35(join39(projectPath, file), "utf8").split(`
|
|
47602
47707
|
`).forEach((line, index) => {
|
|
47603
47708
|
if (line.includes(`"${from}"`)) {
|
|
47604
47709
|
namedInScripts.push({ file: `res://${file}`, line: index + 1, text: line.trim() });
|
|
@@ -47873,7 +47978,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
47873
47978
|
let projectGodot = null;
|
|
47874
47979
|
if (projectPath !== null) {
|
|
47875
47980
|
try {
|
|
47876
|
-
projectGodot = readFileSync35(
|
|
47981
|
+
projectGodot = readFileSync35(join39(projectPath, "project.godot"), "utf8");
|
|
47877
47982
|
} catch {}
|
|
47878
47983
|
}
|
|
47879
47984
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -48056,7 +48161,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
48056
48161
|
}
|
|
48057
48162
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
48058
48163
|
try {
|
|
48059
|
-
return settingKeys(readFileSync35(
|
|
48164
|
+
return settingKeys(readFileSync35(join39(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
48060
48165
|
} catch {
|
|
48061
48166
|
return true;
|
|
48062
48167
|
}
|
|
@@ -48071,7 +48176,7 @@ function uncaughtNote(uncaught) {
|
|
|
48071
48176
|
}
|
|
48072
48177
|
function elsewhereIn(projectPath, asked) {
|
|
48073
48178
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
48074
|
-
if (wanted === "" || existsSync30(
|
|
48179
|
+
if (wanted === "" || existsSync30(join39(projectPath, wanted))) {
|
|
48075
48180
|
return "";
|
|
48076
48181
|
}
|
|
48077
48182
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -48094,7 +48199,7 @@ function windowNoteFor(needing, sharing) {
|
|
|
48094
48199
|
return `${named(needing)} ${one ? "needs" : "need"} a window, as gdharness test reads ${one ? "it" : "them"}, so the run had one: the engine ran without --headless, at the size project.godot gives its viewport, with no sound${process.platform === "win32" ? ", on a desktop of its own, where its window neither shows nor takes the keyboard" : ""}. ${why}${shared}`;
|
|
48095
48200
|
}
|
|
48096
48201
|
function testPathsIn(projectPath, given) {
|
|
48097
|
-
const defaults = TEST_DIRECTORIES.filter((directory) => existsSync30(
|
|
48202
|
+
const defaults = TEST_DIRECTORIES.filter((directory) => existsSync30(join39(projectPath, directory)));
|
|
48098
48203
|
const entries = given === undefined || given === null || given === "" ? defaults.length > 0 ? defaults : TEST_DIRECTORIES : given;
|
|
48099
48204
|
const listed = Array.isArray(entries) ? entries : [entries];
|
|
48100
48205
|
if (listed.length === 0) {
|
|
@@ -48206,7 +48311,7 @@ function needsItsOwnProcess(asked) {
|
|
|
48206
48311
|
class GodotServer {
|
|
48207
48312
|
stdio = null;
|
|
48208
48313
|
locator = new GodotLocator;
|
|
48209
|
-
operationsScript =
|
|
48314
|
+
operationsScript = join39(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
48210
48315
|
exportOptionsRead = new Map;
|
|
48211
48316
|
heldWhilePending = new Set;
|
|
48212
48317
|
evaluatedWhilePending = new Set;
|
|
@@ -48477,7 +48582,7 @@ class GodotServer {
|
|
|
48477
48582
|
}
|
|
48478
48583
|
noteTheStart() {
|
|
48479
48584
|
const cwd = process.cwd();
|
|
48480
|
-
const mcpJson =
|
|
48585
|
+
const mcpJson = join39(cwd, ".mcp.json");
|
|
48481
48586
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
48482
48587
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
48483
48588
|
writeLifecycle("started", {
|
|
@@ -48625,7 +48730,7 @@ class GodotServer {
|
|
|
48625
48730
|
return checked.response;
|
|
48626
48731
|
}
|
|
48627
48732
|
const named = args["projectPath"];
|
|
48628
|
-
if (typeof named === "string" && existsSync30(
|
|
48733
|
+
if (typeof named === "string" && existsSync30(join39(named, "project.godot"))) {
|
|
48629
48734
|
this.lastProjectPath = named;
|
|
48630
48735
|
}
|
|
48631
48736
|
this.updates.refresh();
|
|
@@ -48661,8 +48766,8 @@ class GodotServer {
|
|
|
48661
48766
|
let notSaved;
|
|
48662
48767
|
try {
|
|
48663
48768
|
savedAt = answer.content.map((item, index) => {
|
|
48664
|
-
const file =
|
|
48665
|
-
|
|
48769
|
+
const file = join39(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
48770
|
+
writeFileSync20(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
48666
48771
|
return file;
|
|
48667
48772
|
});
|
|
48668
48773
|
} catch (error) {
|
|
@@ -49345,7 +49450,7 @@ class GodotServer {
|
|
|
49345
49450
|
if (path === undefined) {
|
|
49346
49451
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
49347
49452
|
}
|
|
49348
|
-
const file =
|
|
49453
|
+
const file = join39(path, "project.godot");
|
|
49349
49454
|
if (!existsSync30(file)) {
|
|
49350
49455
|
return {
|
|
49351
49456
|
ok: false,
|
|
@@ -50214,7 +50319,7 @@ class GodotServer {
|
|
|
50214
50319
|
const platform = presetPlatform(project.value.path, preset);
|
|
50215
50320
|
if (platform !== null) {
|
|
50216
50321
|
const known = await this.exportOptions(platform, {
|
|
50217
|
-
presetsFile:
|
|
50322
|
+
presetsFile: join39(project.value.path, "export_presets.cfg"),
|
|
50218
50323
|
preset,
|
|
50219
50324
|
asked: options
|
|
50220
50325
|
});
|
|
@@ -50265,7 +50370,7 @@ ${platform}`;
|
|
|
50265
50370
|
}
|
|
50266
50371
|
const empty = scratchDirectory("export");
|
|
50267
50372
|
try {
|
|
50268
|
-
|
|
50373
|
+
writeFileSync20(join39(empty, "project.godot"), `config_version=5
|
|
50269
50374
|
`);
|
|
50270
50375
|
const outcome = await runOperation({ godotPath: engine.value, script: this.operationsScript, debug: DEBUG_MODE, editor: true }, "export_options", { platform, ...standing }, empty);
|
|
50271
50376
|
const options = outcome.ok ? outcome.payload["options"] : undefined;
|
|
@@ -50304,7 +50409,7 @@ ${platform}`;
|
|
|
50304
50409
|
}
|
|
50305
50410
|
const folder = dirname24(output.absolutePath);
|
|
50306
50411
|
try {
|
|
50307
|
-
|
|
50412
|
+
mkdirSync21(folder, { recursive: true });
|
|
50308
50413
|
} catch (error) {
|
|
50309
50414
|
return this.createErrorResponse(`Cannot create ${dirname24(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
50310
50415
|
}
|
|
@@ -50321,7 +50426,7 @@ ${platform}`;
|
|
|
50321
50426
|
const exportArgs = [
|
|
50322
50427
|
"--headless",
|
|
50323
50428
|
"--log-file",
|
|
50324
|
-
|
|
50429
|
+
join39(exportLogs, "engine.log"),
|
|
50325
50430
|
"--path",
|
|
50326
50431
|
project.value.path,
|
|
50327
50432
|
pack ? "--export-pack" : debug ? "--export-debug" : "--export-release",
|
|
@@ -50412,8 +50517,9 @@ ${platform}`;
|
|
|
50412
50517
|
if (movie !== null) {
|
|
50413
50518
|
return this.createErrorResponse(movie);
|
|
50414
50519
|
}
|
|
50415
|
-
const missing = paths.value.filter((path) => !existsSync30(
|
|
50416
|
-
const
|
|
50520
|
+
const missing = paths.value.filter((path) => !existsSync30(join39(project.value.path, path.slice("res://".length))));
|
|
50521
|
+
const projectScripts = new ProjectScripts(project.value.path);
|
|
50522
|
+
const asked = projectScripts.searchedAsTold(paths.value.filter((path) => !missing.includes(path)));
|
|
50417
50523
|
const unusable = gdUnit4Unusable(project.value.path);
|
|
50418
50524
|
if (asked.length === 0) {
|
|
50419
50525
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
@@ -50452,7 +50558,7 @@ ${platform}`;
|
|
|
50452
50558
|
return this.createErrorResponse(selection.reason);
|
|
50453
50559
|
}
|
|
50454
50560
|
const leftOut = new Set(readStringArray(args, "ignore") ?? []);
|
|
50455
|
-
const needs = windowNeedsOf(
|
|
50561
|
+
const needs = windowNeedsOf(projectScripts, selection.value?.suites ?? asked);
|
|
50456
50562
|
const needing = needs.needing.filter((path) => !leftOut.has(basename13(path, ".gd")));
|
|
50457
50563
|
const noDisplay = resolveHeadless(undefined, { platform: process.platform, variables: process.env });
|
|
50458
50564
|
const windowed = needing.length > 0 && !noDisplay;
|
|
@@ -50484,14 +50590,14 @@ ${platform}`;
|
|
|
50484
50590
|
const reports = `res://.godot/${ours}`;
|
|
50485
50591
|
const ignored = readStringArray(args, "ignore") ?? [];
|
|
50486
50592
|
const userData = scratchDirectory("tests");
|
|
50487
|
-
const suitesFile =
|
|
50488
|
-
const skippingFile =
|
|
50489
|
-
const engineLog =
|
|
50490
|
-
const expectedFile =
|
|
50491
|
-
|
|
50593
|
+
const suitesFile = join39(userData, "suites.txt");
|
|
50594
|
+
const skippingFile = join39(userData, "skipping.txt");
|
|
50595
|
+
const engineLog = join39(userData, "engine.log");
|
|
50596
|
+
const expectedFile = join39(userData, "expected.json");
|
|
50597
|
+
writeFileSync20(suitesFile, `${(selection.value?.suites ?? asked).join(`
|
|
50492
50598
|
`)}
|
|
50493
50599
|
`);
|
|
50494
|
-
|
|
50600
|
+
writeFileSync20(skippingFile, `${[...ignored, ...selection.value?.ignore ?? []].join(`
|
|
50495
50601
|
`)}
|
|
50496
50602
|
`);
|
|
50497
50603
|
const viewport = windowed ? viewportOf(project.value.path) : null;
|
|
@@ -50528,11 +50634,11 @@ ${platform}`;
|
|
|
50528
50634
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
|
|
50529
50635
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
50530
50636
|
const helperFiles = {
|
|
50531
|
-
spec:
|
|
50532
|
-
pid:
|
|
50533
|
-
error:
|
|
50534
|
-
stop:
|
|
50535
|
-
step:
|
|
50637
|
+
spec: join39(userData, "spec"),
|
|
50638
|
+
pid: join39(userData, "pid"),
|
|
50639
|
+
error: join39(userData, "error"),
|
|
50640
|
+
stop: join39(userData, "stop"),
|
|
50641
|
+
step: join39(userData, "step")
|
|
50536
50642
|
};
|
|
50537
50643
|
const onDesktop = windowed && process.platform === "win32";
|
|
50538
50644
|
const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData), onDesktop ? { directory: project.value.path, files: helperFiles } : undefined);
|
|
@@ -50583,12 +50689,12 @@ ${platform}`;
|
|
|
50583
50689
|
await run.settled;
|
|
50584
50690
|
const helperFailed = onDesktop ? helperFailure(helperFiles.error) ?? (existsSync30(helperFiles.pid) ? null : `it never started, and ${helperStep(helperFiles.step)}`) : null;
|
|
50585
50691
|
if (helperFailed !== null) {
|
|
50586
|
-
discard(
|
|
50692
|
+
discard(join39(project.value.path, ".godot", ...ours.split("/")), userData);
|
|
50587
50693
|
return this.createErrorResponse(`No tests ran: ${named(needing)} ${needing.length === 1 ? "needs" : "need"} a window, and the engine could not be started on a desktop of its own: ${helperFailed}`);
|
|
50588
50694
|
}
|
|
50589
50695
|
const tookMs = Date.now() - began;
|
|
50590
50696
|
const notCompiled = stopped.for ?? run.log.all.map((entry) => autoloadNotCompiled(entry, autoloaded)).find((found) => found !== null) ?? null;
|
|
50591
|
-
const reportsDir =
|
|
50697
|
+
const reportsDir = join39(project.value.path, ".godot", ...ours.split("/"));
|
|
50592
50698
|
let report = null;
|
|
50593
50699
|
let reportProblem = null;
|
|
50594
50700
|
const casePrinted = withNestedErrors(readRun(existsSync30(engineLog) ? readFileSync35(engineLog, "utf8") : ""), run.log.all);
|
|
@@ -50597,7 +50703,7 @@ ${platform}`;
|
|
|
50597
50703
|
const written = existsSync30(reportsDir) && notCompiled === null ? readdirSync17(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
50598
50704
|
const newest = written.at(-1);
|
|
50599
50705
|
if (newest !== undefined) {
|
|
50600
|
-
report = parseJUnit(readFileSync35(
|
|
50706
|
+
report = parseJUnit(readFileSync35(join39(reportsDir, newest, "results.xml"), "utf8"));
|
|
50601
50707
|
}
|
|
50602
50708
|
} catch (error) {
|
|
50603
50709
|
reportProblem = errorMessage(error);
|
|
@@ -51339,7 +51445,7 @@ ${error.text}`;
|
|
|
51339
51445
|
...Array.isArray(stack) ? {
|
|
51340
51446
|
stack: stack.map((frame) => ({
|
|
51341
51447
|
...frame,
|
|
51342
|
-
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path:
|
|
51448
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join39(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
51343
51449
|
}))
|
|
51344
51450
|
} : {}
|
|
51345
51451
|
});
|
|
@@ -51668,7 +51774,7 @@ ${error.text}`;
|
|
|
51668
51774
|
return null;
|
|
51669
51775
|
}
|
|
51670
51776
|
try {
|
|
51671
|
-
return settingKeys(readFileSync35(
|
|
51777
|
+
return settingKeys(readFileSync35(join39(projectPath, "project.godot"), "utf8"));
|
|
51672
51778
|
} catch {
|
|
51673
51779
|
return null;
|
|
51674
51780
|
}
|
|
@@ -52120,7 +52226,7 @@ ${error.text}`;
|
|
|
52120
52226
|
}
|
|
52121
52227
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
52122
52228
|
const budgetMs = budget.ms;
|
|
52123
|
-
if (!existsSync30(
|
|
52229
|
+
if (!existsSync30(join39(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
52124
52230
|
return runtimeVerdict(null, {
|
|
52125
52231
|
addon: false,
|
|
52126
52232
|
budgetMs,
|
|
@@ -52592,7 +52698,7 @@ ${error.text}`;
|
|
|
52592
52698
|
if (onDesktop === undefined) {
|
|
52593
52699
|
child.kill();
|
|
52594
52700
|
} else {
|
|
52595
|
-
|
|
52701
|
+
writeFileSync20(onDesktop.files.stop, "");
|
|
52596
52702
|
}
|
|
52597
52703
|
};
|
|
52598
52704
|
const cancelled = callSignal();
|
|
@@ -53119,7 +53225,7 @@ ${error.text}`;
|
|
|
53119
53225
|
};
|
|
53120
53226
|
const going = await this.runStillGoing(run);
|
|
53121
53227
|
const projectPath = run.projectPath;
|
|
53122
|
-
const projectGone = projectPath !== null && !existsSync30(
|
|
53228
|
+
const projectGone = projectPath !== null && !existsSync30(join39(projectPath, "project.godot"));
|
|
53123
53229
|
if (projectGone && callersName !== undefined) {
|
|
53124
53230
|
return this.createErrorResponse(`callersOf reads the project's scripts, and the run's project at ${projectPath} has no project.godot any more.`, ["editor_run profile without callersOf answers with the profile itself"]);
|
|
53125
53231
|
}
|
|
@@ -53504,7 +53610,7 @@ ${error.text}`;
|
|
|
53504
53610
|
notes.push(endedWithoutACode({
|
|
53505
53611
|
throughEditor: run.throughEditor,
|
|
53506
53612
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
53507
|
-
couldAnnounce: run.projectPath !== null && existsSync30(
|
|
53613
|
+
couldAnnounce: run.projectPath !== null && existsSync30(join39(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
53508
53614
|
endedHere: typeof run.endedHere === "string",
|
|
53509
53615
|
pickedUp: run.pickedUp === true
|
|
53510
53616
|
}));
|
|
@@ -54921,7 +55027,7 @@ ${error.text}`;
|
|
|
54921
55027
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
54922
55028
|
const run = await this.currentRun();
|
|
54923
55029
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
54924
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync30(
|
|
55030
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync30(join39(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
54925
55031
|
return null;
|
|
54926
55032
|
}
|
|
54927
55033
|
const already = runtimesAnnounced().running;
|
|
@@ -55031,7 +55137,7 @@ ${error.text}`;
|
|
|
55031
55137
|
return this.createErrorResponse(staleNote);
|
|
55032
55138
|
}
|
|
55033
55139
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
55034
|
-
const screenshotPath = screenshotDir ?
|
|
55140
|
+
const screenshotPath = screenshotDir ? join39(screenshotDir, "capture.png") : null;
|
|
55035
55141
|
const refused = (message) => this.createErrorResponse(message);
|
|
55036
55142
|
const onGame = {
|
|
55037
55143
|
pid: choice.endpoint.pid,
|
|
@@ -55366,7 +55472,7 @@ var init_server = __esm(() => {
|
|
|
55366
55472
|
// src/cli.ts
|
|
55367
55473
|
init_bridge_announce();
|
|
55368
55474
|
import { existsSync as existsSync31 } from "node:fs";
|
|
55369
|
-
import { join as
|
|
55475
|
+
import { join as join40, resolve as resolve10 } from "node:path";
|
|
55370
55476
|
|
|
55371
55477
|
// src/error-mode.ts
|
|
55372
55478
|
import process3 from "node:process";
|
|
@@ -55391,9 +55497,6 @@ async function letWindowsReportCrashes() {
|
|
|
55391
55497
|
}
|
|
55392
55498
|
} catch {}
|
|
55393
55499
|
}
|
|
55394
|
-
function crashesReported() {
|
|
55395
|
-
return cleared;
|
|
55396
|
-
}
|
|
55397
55500
|
|
|
55398
55501
|
// src/cli.ts
|
|
55399
55502
|
init_errors();
|
|
@@ -56363,7 +56466,7 @@ init_setup();
|
|
|
56363
56466
|
init_setup();
|
|
56364
56467
|
init_tool_definitions();
|
|
56365
56468
|
init_tool_reference();
|
|
56366
|
-
import { existsSync as existsSync15, mkdirSync as
|
|
56469
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readdirSync as readdirSync10, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "node:fs";
|
|
56367
56470
|
import { basename as basename8, dirname as dirname9, join as join17 } from "node:path";
|
|
56368
56471
|
var SKILL_NAME = "gdharness";
|
|
56369
56472
|
var SHARED_SKILLS = join17(".agents", "skills");
|
|
@@ -56658,8 +56761,8 @@ function writeSkill(directories, version) {
|
|
|
56658
56761
|
rmSync6(directory, { recursive: true, force: true });
|
|
56659
56762
|
for (const [name, contents] of files) {
|
|
56660
56763
|
const path = join17(directory, name);
|
|
56661
|
-
|
|
56662
|
-
|
|
56764
|
+
mkdirSync6(dirname9(path), { recursive: true });
|
|
56765
|
+
writeFileSync8(path, contents, "utf8");
|
|
56663
56766
|
}
|
|
56664
56767
|
return { path: directory, replaced };
|
|
56665
56768
|
});
|
|
@@ -56667,30 +56770,31 @@ function writeSkill(directories, version) {
|
|
|
56667
56770
|
|
|
56668
56771
|
// src/suite-run.ts
|
|
56669
56772
|
init_alive();
|
|
56773
|
+
init_class_cache();
|
|
56670
56774
|
init_desktop();
|
|
56671
56775
|
init_engine_leaks();
|
|
56672
56776
|
import {
|
|
56673
56777
|
closeSync as closeSync3,
|
|
56674
56778
|
existsSync as existsSync19,
|
|
56675
56779
|
fstatSync,
|
|
56676
|
-
mkdirSync as
|
|
56780
|
+
mkdirSync as mkdirSync9,
|
|
56677
56781
|
openSync as openSync3,
|
|
56678
56782
|
readdirSync as readdirSync12,
|
|
56679
56783
|
readFileSync as readFileSync20,
|
|
56680
56784
|
readSync,
|
|
56681
56785
|
rmSync as rmSync10,
|
|
56682
56786
|
statSync as statSync13,
|
|
56683
|
-
writeFileSync as
|
|
56787
|
+
writeFileSync as writeFileSync11
|
|
56684
56788
|
} from "node:fs";
|
|
56685
56789
|
import { availableParallelism, freemem, totalmem } from "node:os";
|
|
56686
|
-
import { isAbsolute as isAbsolute6, join as
|
|
56790
|
+
import { isAbsolute as isAbsolute6, join as join23, relative as relative3, resolve as resolve5, sep as sep2 } from "node:path";
|
|
56687
56791
|
import process12 from "node:process";
|
|
56688
56792
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
56689
56793
|
|
|
56690
56794
|
// src/engine-slots.ts
|
|
56691
56795
|
init_alive();
|
|
56692
56796
|
init_process_children();
|
|
56693
|
-
import { closeSync as closeSync2, mkdirSync as
|
|
56797
|
+
import { closeSync as closeSync2, mkdirSync as mkdirSync7, openSync as openSync2, readFileSync as readFileSync15, rmSync as rmSync8, statSync as statSync11, writeSync as writeSync2 } from "node:fs";
|
|
56694
56798
|
import { join as join19 } from "node:path";
|
|
56695
56799
|
import process11 from "node:process";
|
|
56696
56800
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
@@ -56738,7 +56842,7 @@ class EngineSlots {
|
|
|
56738
56842
|
this.holder = holder;
|
|
56739
56843
|
}
|
|
56740
56844
|
async take(signal) {
|
|
56741
|
-
|
|
56845
|
+
mkdirSync7(this.directory, { recursive: true });
|
|
56742
56846
|
for (;; ) {
|
|
56743
56847
|
signal?.throwIfAborted();
|
|
56744
56848
|
for (let slot = 0;slot < this.count; slot += 1) {
|
|
@@ -56832,6 +56936,8 @@ class EngineSlots {
|
|
|
56832
56936
|
|
|
56833
56937
|
// src/suite-run.ts
|
|
56834
56938
|
init_errors();
|
|
56939
|
+
init_import_freshness();
|
|
56940
|
+
init_library_copies();
|
|
56835
56941
|
init_setup();
|
|
56836
56942
|
init_spawn();
|
|
56837
56943
|
|
|
@@ -56977,13 +57083,13 @@ init_suite_plan();
|
|
|
56977
57083
|
|
|
56978
57084
|
// src/version.ts
|
|
56979
57085
|
import { readFileSync as readFileSync19 } from "node:fs";
|
|
56980
|
-
import { dirname as dirname11, join as
|
|
57086
|
+
import { dirname as dirname11, join as join22 } from "node:path";
|
|
56981
57087
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
56982
57088
|
function getLocalVersion() {
|
|
56983
57089
|
const currentDirectory = dirname11(fileURLToPath2(import.meta.url));
|
|
56984
57090
|
const packageCandidates = [
|
|
56985
|
-
|
|
56986
|
-
|
|
57091
|
+
join22(currentDirectory, "..", "package.json"),
|
|
57092
|
+
join22(currentDirectory, "..", "..", "package.json")
|
|
56987
57093
|
];
|
|
56988
57094
|
for (const packagePath of packageCandidates) {
|
|
56989
57095
|
try {
|
|
@@ -57001,6 +57107,8 @@ init_windows_ask();
|
|
|
57001
57107
|
|
|
57002
57108
|
class TestUsageError extends Refusal {
|
|
57003
57109
|
}
|
|
57110
|
+
var WINDOWS_EXCEPTION = 3221225472;
|
|
57111
|
+
var SLOWEST_NAMED = 5;
|
|
57004
57112
|
var MOST_ENGINES = 4;
|
|
57005
57113
|
var MEMORY_KEPT = 4 * 1024 ** 3;
|
|
57006
57114
|
var MEMORY_PER_ENGINE = 1.5 * 1024 ** 3;
|
|
@@ -57015,6 +57123,10 @@ var VALUED = new Set([
|
|
|
57015
57123
|
"--slots",
|
|
57016
57124
|
"--slot-count",
|
|
57017
57125
|
"--silence",
|
|
57126
|
+
"--deadline",
|
|
57127
|
+
"--godot",
|
|
57128
|
+
"--godot-version",
|
|
57129
|
+
"--cores",
|
|
57018
57130
|
"--tests",
|
|
57019
57131
|
"--reach-also",
|
|
57020
57132
|
"--order"
|
|
@@ -57043,9 +57155,9 @@ function testOptionsOf(args, cwd) {
|
|
|
57043
57155
|
}
|
|
57044
57156
|
const first = positional[0];
|
|
57045
57157
|
const named = first === undefined ? null : resolve5(cwd, first);
|
|
57046
|
-
const projectNamed = named !== null && existsSync19(
|
|
57158
|
+
const projectNamed = named !== null && existsSync19(join23(named, "project.godot"));
|
|
57047
57159
|
const projectPath = projectNamed ? named : resolve5(cwd);
|
|
57048
|
-
if (!existsSync19(
|
|
57160
|
+
if (!existsSync19(join23(projectPath, "project.godot"))) {
|
|
57049
57161
|
throw new TestUsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
57050
57162
|
}
|
|
57051
57163
|
const last = (flag) => valued.get(flag)?.at(-1);
|
|
@@ -57057,6 +57169,10 @@ function testOptionsOf(args, cwd) {
|
|
|
57057
57169
|
slotsDirectory: last("--slots") === undefined ? null : resolve5(cwd, last("--slots") ?? ""),
|
|
57058
57170
|
slotCount: whole(last("--slot-count"), "--slot-count"),
|
|
57059
57171
|
silenceSeconds: whole(last("--silence"), "--silence") ?? SILENCE_FLOOR_SECONDS,
|
|
57172
|
+
deadlineSeconds: whole(last("--deadline"), "--deadline"),
|
|
57173
|
+
godotPath: godotNamed(last("--godot"), cwd),
|
|
57174
|
+
godotVersion: versionNamed(last("--godot-version")),
|
|
57175
|
+
cores: coresNamed(last("--cores")),
|
|
57060
57176
|
roots: (valued.get("--tests") ?? []).map((root) => resPathOf(root, projectPath, cwd).replace(/\/$/, "")),
|
|
57061
57177
|
changedOnly: switches.has("--changed-only"),
|
|
57062
57178
|
reachAlso: (valued.get("--reach-also") ?? []).map((path) => resolve5(projectPath, path)),
|
|
@@ -57075,6 +57191,34 @@ function whole(value, flag) {
|
|
|
57075
57191
|
}
|
|
57076
57192
|
return number;
|
|
57077
57193
|
}
|
|
57194
|
+
function godotNamed(value, cwd) {
|
|
57195
|
+
if (value === undefined) {
|
|
57196
|
+
return null;
|
|
57197
|
+
}
|
|
57198
|
+
const path = resolve5(cwd, value);
|
|
57199
|
+
if (!existsSync19(path) || statSync13(path).isDirectory()) {
|
|
57200
|
+
throw new TestUsageError(`--godot names ${path}, which is not an engine executable.`);
|
|
57201
|
+
}
|
|
57202
|
+
return path;
|
|
57203
|
+
}
|
|
57204
|
+
function coresNamed(value) {
|
|
57205
|
+
const cores = whole(value, "--cores");
|
|
57206
|
+
const most = Math.min(availableParallelism(), 64);
|
|
57207
|
+
if (cores !== null && cores > most) {
|
|
57208
|
+
throw new TestUsageError(`--cores ${String(cores)} is more than the ${String(most)} processors a run can be held to here.`);
|
|
57209
|
+
}
|
|
57210
|
+
return cores;
|
|
57211
|
+
}
|
|
57212
|
+
function versionNamed(value) {
|
|
57213
|
+
if (value === undefined) {
|
|
57214
|
+
return null;
|
|
57215
|
+
}
|
|
57216
|
+
const version = value.replace(/^v/, "");
|
|
57217
|
+
if (!/^\d+\.\d+(?:\.\d+)?(?:\.[\w-]+)*$/.test(version)) {
|
|
57218
|
+
throw new TestUsageError(`--godot-version takes a version such as 4.7.2 or 4.7.2.stable, not ${value}.`);
|
|
57219
|
+
}
|
|
57220
|
+
return version;
|
|
57221
|
+
}
|
|
57078
57222
|
function seedOf(value) {
|
|
57079
57223
|
const seed = Number(value);
|
|
57080
57224
|
if (!Number.isInteger(seed) || seed < 0) {
|
|
@@ -57102,7 +57246,7 @@ function homeOf(projectPath) {
|
|
|
57102
57246
|
return mainCheckoutOf(projectPath) ?? projectPath;
|
|
57103
57247
|
}
|
|
57104
57248
|
function selectedSuites(scripts, options) {
|
|
57105
|
-
const roots = options.roots.length > 0 ? options.roots : TEST_DIRECTORIES.filter((root) => existsSync19(
|
|
57249
|
+
const roots = options.roots.length > 0 ? options.roots : TEST_DIRECTORIES.filter((root) => existsSync19(join23(options.projectPath, root))).map((root) => `res://${root}`);
|
|
57106
57250
|
if (options.selected.length === 0) {
|
|
57107
57251
|
const found = scripts.suitesUnder(roots);
|
|
57108
57252
|
if (found.length === 0) {
|
|
@@ -57114,7 +57258,7 @@ function selectedSuites(scripts, options) {
|
|
|
57114
57258
|
for (const entry of options.selected) {
|
|
57115
57259
|
const caseAt = /^(res:\/\/.*\.gd):(\w+)$/.exec(entry);
|
|
57116
57260
|
const path = caseAt?.[1] ?? entry;
|
|
57117
|
-
const directory =
|
|
57261
|
+
const directory = join23(options.projectPath, path.slice(6));
|
|
57118
57262
|
const suites = existsSync19(directory) && statSync13(directory).isDirectory() ? scripts.suitesUnder([path.replace(/\/$/, "")]) : [scripts.suite(path)].filter((suite) => suite !== null);
|
|
57119
57263
|
if (suites.length === 0) {
|
|
57120
57264
|
throw new TestUsageError(existsSync19(directory) ? `${path} holds no gdUnit4 suite: nothing there extends GdUnitTestSuite and declares a test_ function.` : `Nothing at ${path} in the project at ${options.projectPath}.`);
|
|
@@ -57136,7 +57280,7 @@ function selectedSuites(scripts, options) {
|
|
|
57136
57280
|
cases: suite.cases.filter((c) => cases.has(c))
|
|
57137
57281
|
}));
|
|
57138
57282
|
}
|
|
57139
|
-
function roomFor(most, processors, available, said = "of memory free") {
|
|
57283
|
+
function roomFor(most, processors, available, said = "of memory free", processorsSaid = `the machine has ${plural(processors, "processor")}`) {
|
|
57140
57284
|
const byMemory = Math.floor((available - MEMORY_KEPT) / MEMORY_PER_ENGINE);
|
|
57141
57285
|
const engines = Math.max(1, Math.min(most, processors, byMemory));
|
|
57142
57286
|
if (engines >= most) {
|
|
@@ -57145,7 +57289,7 @@ function roomFor(most, processors, available, said = "of memory free") {
|
|
|
57145
57289
|
const gigabytes = (bytes) => `${(bytes / 1024 ** 3).toFixed(1)} GB`;
|
|
57146
57290
|
return {
|
|
57147
57291
|
engines,
|
|
57148
|
-
held: byMemory < processors ? `the machine has ${gigabytes(Math.max(0, available))} ${said}, ${gigabytes(MEMORY_KEPT)} of it kept for everything else and ${gigabytes(MEMORY_PER_ENGINE)} taken for each engine` :
|
|
57292
|
+
held: byMemory < processors ? `the machine has ${gigabytes(Math.max(0, available))} ${said}, ${gigabytes(MEMORY_KEPT)} of it kept for everything else and ${gigabytes(MEMORY_PER_ENGINE)} taken for each engine` : processorsSaid
|
|
57149
57293
|
};
|
|
57150
57294
|
}
|
|
57151
57295
|
async function memoryAvailable() {
|
|
@@ -57207,6 +57351,9 @@ function ordered(units, order) {
|
|
|
57207
57351
|
}
|
|
57208
57352
|
return sorted;
|
|
57209
57353
|
}
|
|
57354
|
+
function heldToCores(cores, command, args, platform = process12.platform) {
|
|
57355
|
+
return cores === null || platform !== "linux" ? { command, args: [...args] } : { command: "taskset", args: ["-c", `0-${String(cores - 1)}`, command, ...args] };
|
|
57356
|
+
}
|
|
57210
57357
|
async function runTestCommand(options, godotPath) {
|
|
57211
57358
|
const unusable = gdUnit4Unusable(options.projectPath);
|
|
57212
57359
|
if (unusable !== null) {
|
|
@@ -57215,17 +57362,17 @@ async function runTestCommand(options, godotPath) {
|
|
|
57215
57362
|
const startedAt = Date.now();
|
|
57216
57363
|
const scripts = new ProjectScripts(options.projectPath);
|
|
57217
57364
|
const home = homeOf(options.projectPath);
|
|
57218
|
-
const state =
|
|
57219
|
-
const timesFile =
|
|
57220
|
-
const passedFile =
|
|
57365
|
+
const state = join23(home, ".godot", "gdharness");
|
|
57366
|
+
const timesFile = join23(state, "suite-times.json");
|
|
57367
|
+
const passedFile = join23(state, "suite-passed.json");
|
|
57221
57368
|
let suites = selectedSuites(scripts, options);
|
|
57222
57369
|
const wholeSuites = suites.map((suite) => scripts.suite(suite.path) ?? suite);
|
|
57223
57370
|
const declared = new Map(wholeSuites.map((suite) => [suite.path, suite.cases]));
|
|
57224
|
-
const testRuns =
|
|
57371
|
+
const testRuns = join23(options.projectPath, ".godot", "gdharness", "test-runs");
|
|
57225
57372
|
sweepRunDirectories(testRuns);
|
|
57226
|
-
const runDirectory =
|
|
57227
|
-
|
|
57228
|
-
const slots = new EngineSlots(options.slotsDirectory ??
|
|
57373
|
+
const runDirectory = join23(testRuns, `${String(startedAt)}-${String(process12.pid)}`);
|
|
57374
|
+
mkdirSync9(runDirectory, { recursive: true });
|
|
57375
|
+
const slots = new EngineSlots(options.slotsDirectory ?? join23(home, ".godot", "gate", "slots"), options.slotCount ?? Math.min(MOST_ENGINES, availableParallelism()), { pid: process12.pid, started: await ownStart() });
|
|
57229
57376
|
const running = new Set;
|
|
57230
57377
|
const interrupted = () => {
|
|
57231
57378
|
for (const end of running) {
|
|
@@ -57242,8 +57389,11 @@ async function runTestCommand(options, godotPath) {
|
|
|
57242
57389
|
runDirectory,
|
|
57243
57390
|
slots,
|
|
57244
57391
|
silenceFloorSeconds: options.silenceSeconds,
|
|
57392
|
+
deadlineSeconds: options.deadlineSeconds,
|
|
57245
57393
|
running,
|
|
57246
|
-
selfTyped: selfTypedExports(scripts.sources)
|
|
57394
|
+
selfTyped: selfTypedExports(scripts.sources),
|
|
57395
|
+
godotVersion: options.godotVersion,
|
|
57396
|
+
cores: options.cores
|
|
57247
57397
|
};
|
|
57248
57398
|
try {
|
|
57249
57399
|
if (options.importFirst) {
|
|
@@ -57282,7 +57432,10 @@ ${existsSync19(path) ? readFileSync20(path, "latin1") : "missing"}`)
|
|
|
57282
57432
|
if (memory.unread !== undefined) {
|
|
57283
57433
|
console.log(`Windows did not say how much memory it can still commit (${memory.unread}), so engines are sized by the physical memory free.`);
|
|
57284
57434
|
}
|
|
57285
|
-
const room = roomFor(options.engines, availableParallelism(), memory.bytes, memory.said);
|
|
57435
|
+
const room = options.cores === null ? roomFor(options.engines, availableParallelism(), memory.bytes, memory.said) : roomFor(options.engines, options.cores, memory.bytes, memory.said, `--cores holds it to ${plural(options.cores, "processor")}`);
|
|
57436
|
+
if (options.cores !== null && process12.platform === "darwin") {
|
|
57437
|
+
console.log("macOS offers no way to hold a process to some processors, so --cores holds only the number of engines here.");
|
|
57438
|
+
}
|
|
57286
57439
|
const plans = options.order === null ? dealt(units, declared, room.engines) : [inOneEngine(ordered(units, options.order), declared)];
|
|
57287
57440
|
const functions = suites.reduce((sum, suite) => sum + suite.cases.length, 0);
|
|
57288
57441
|
const how = options.order === null ? `${windowedSaid(plans)}${roomSaid(plans.length, lanesOf(plans), lanesOf(dealt(units, declared, options.engines)), room.held, options.engines)}` : options.order === "reversed" ? ", in reverse order of path" : `, shuffled by ${String(options.order)}`;
|
|
@@ -57378,19 +57531,38 @@ function sweepRunDirectories(testRuns) {
|
|
|
57378
57531
|
for (const entry of readdirSync12(testRuns)) {
|
|
57379
57532
|
const pid = Number(/^\d+-(\d+)$/.exec(entry)?.[1]);
|
|
57380
57533
|
if (!Number.isInteger(pid) || !alive(pid)) {
|
|
57381
|
-
rmSync10(
|
|
57534
|
+
rmSync10(join23(testRuns, entry), { recursive: true, force: true });
|
|
57382
57535
|
}
|
|
57383
57536
|
}
|
|
57384
57537
|
}
|
|
57385
57538
|
async function importProject(launch) {
|
|
57386
|
-
const
|
|
57387
|
-
|
|
57388
|
-
|
|
57539
|
+
const seeded = seedExtensionList(launch.projectPath);
|
|
57540
|
+
if (seeded !== null && seeded.length > 0) {
|
|
57541
|
+
console.log(`Wrote .godot/extension_list.cfg naming ${named(seeded)}, so the import loads them.`);
|
|
57542
|
+
}
|
|
57543
|
+
const cached = cachedClasses(launch.projectPath);
|
|
57544
|
+
if ((seeded === null || seeded.length === 0) && cached !== null && staleAgainst(cached, launch.projectPath).length === 0 && staleImports(launch.projectPath).length === 0) {
|
|
57545
|
+
console.log("The import was skipped: every imported file and the class cache are current.");
|
|
57546
|
+
return null;
|
|
57547
|
+
}
|
|
57548
|
+
const directory = join23(launch.runDirectory, "import");
|
|
57549
|
+
mkdirSync9(join23(directory, "user"), { recursive: true });
|
|
57550
|
+
const log = join23(directory, "import.log");
|
|
57389
57551
|
const lock = await launch.slots.take();
|
|
57390
57552
|
try {
|
|
57391
|
-
|
|
57553
|
+
freeLibraryCopies(launch.projectPath);
|
|
57554
|
+
const ended = await watched(launch, ["--headless", "--audio-driver", "Dummy", "--path", launch.projectPath, "--import"], directory, log, launch.silenceFloorSeconds);
|
|
57392
57555
|
if (ended.ended !== null || ended.code !== 0) {
|
|
57393
|
-
return `${ended.ended ?? `the engine exited ${
|
|
57556
|
+
return `${ended.ended ?? `the engine exited ${exitNamed(ended.code ?? 0)}`}; everything it printed is in ${log}`;
|
|
57557
|
+
}
|
|
57558
|
+
const printed = readRun(readFileSync20(log, "utf8"));
|
|
57559
|
+
const wrongEngine = launch.godotVersion === null ? null : versionFault(printed.engineVersion, launch.godotVersion);
|
|
57560
|
+
if (wrongEngine !== null) {
|
|
57561
|
+
return `${wrongEngine}; everything it printed is in ${log}`;
|
|
57562
|
+
}
|
|
57563
|
+
const errors = printed.errors.filter((error) => !error.warning);
|
|
57564
|
+
if (errors.length > 0) {
|
|
57565
|
+
return `it printed ${errors.length === 1 ? "an engine error" : `${String(errors.length)} engine errors`}, the first ${JSON.stringify(errors[0]?.text ?? "")}; everything it printed is in ${log}`;
|
|
57394
57566
|
}
|
|
57395
57567
|
return null;
|
|
57396
57568
|
} finally {
|
|
@@ -57410,19 +57582,19 @@ async function runLane(launch, lane, units) {
|
|
|
57410
57582
|
}
|
|
57411
57583
|
}
|
|
57412
57584
|
async function runEngine2(launch, plan, index, units, again = "", slot = null) {
|
|
57413
|
-
const directory =
|
|
57414
|
-
|
|
57415
|
-
const suitesFile =
|
|
57416
|
-
const skippingFile =
|
|
57417
|
-
const rowsFile =
|
|
57418
|
-
const expectedFile =
|
|
57419
|
-
|
|
57585
|
+
const directory = join23(launch.runDirectory, `engine-${String(index)}${again === "" ? "" : `-${again}`}`);
|
|
57586
|
+
mkdirSync9(join23(directory, "user"), { recursive: true });
|
|
57587
|
+
const suitesFile = join23(directory, "suites.txt");
|
|
57588
|
+
const skippingFile = join23(directory, "skipping.txt");
|
|
57589
|
+
const rowsFile = join23(directory, "rows.txt");
|
|
57590
|
+
const expectedFile = join23(directory, "expected.json");
|
|
57591
|
+
writeFileSync11(suitesFile, `${plan.suites.map((suite) => suite.path).join(`
|
|
57420
57592
|
`)}
|
|
57421
57593
|
`);
|
|
57422
|
-
|
|
57594
|
+
writeFileSync11(skippingFile, `${plan.skipping.join(`
|
|
57423
57595
|
`)}
|
|
57424
57596
|
`);
|
|
57425
|
-
|
|
57597
|
+
writeFileSync11(rowsFile, `${plan.suites.flatMap((suite) => Object.entries(suite.rows ?? {}).map(([func, share]) => [
|
|
57426
57598
|
`${suite.path}:${func}`,
|
|
57427
57599
|
..."only" in share ? ["only", ...share.only] : ["besides", ...share.besides]
|
|
57428
57600
|
].join("\t"))).join(`
|
|
@@ -57441,7 +57613,7 @@ async function runEngine2(launch, plan, index, units, again = "", slot = null) {
|
|
|
57441
57613
|
...plan.windowed ? [] : ["--ignoreHeadlessMode"],
|
|
57442
57614
|
"-c",
|
|
57443
57615
|
"-rd",
|
|
57444
|
-
`res://${relative3(launch.projectPath,
|
|
57616
|
+
`res://${relative3(launch.projectPath, join23(directory, "reports")).split(sep2).join("/")}`,
|
|
57445
57617
|
"-rc",
|
|
57446
57618
|
"1",
|
|
57447
57619
|
"--",
|
|
@@ -57460,12 +57632,12 @@ async function runEngine2(launch, plan, index, units, again = "", slot = null) {
|
|
|
57460
57632
|
const held = new Set(plan.suites.map((suite) => suite.path));
|
|
57461
57633
|
const slowest = Math.max(0, ...units.filter((unit) => held.has(unit.suite)).map((unit) => unit.cost));
|
|
57462
57634
|
const silence = Math.max(launch.silenceFloorSeconds, SILENCE_TIMES_SLOWEST * slowest);
|
|
57463
|
-
const log =
|
|
57635
|
+
const log = join23(directory, "engine.log");
|
|
57464
57636
|
const lock = slot ?? await launch.slots.take();
|
|
57465
57637
|
const began = Date.now();
|
|
57466
57638
|
let end;
|
|
57467
57639
|
try {
|
|
57468
|
-
end = await watched(launch, args, directory, log,
|
|
57640
|
+
end = await watched(launch, args, directory, log, silence);
|
|
57469
57641
|
} finally {
|
|
57470
57642
|
if (slot === null) {
|
|
57471
57643
|
launch.slots.release(lock);
|
|
@@ -57475,7 +57647,8 @@ async function runEngine2(launch, plan, index, units, again = "", slot = null) {
|
|
|
57475
57647
|
const assignment = {
|
|
57476
57648
|
suites: plan.suites,
|
|
57477
57649
|
expected: readExpected(expectedFile),
|
|
57478
|
-
selfTyped: launch.selfTyped
|
|
57650
|
+
selfTyped: launch.selfTyped,
|
|
57651
|
+
godotVersion: launch.godotVersion
|
|
57479
57652
|
};
|
|
57480
57653
|
return {
|
|
57481
57654
|
index,
|
|
@@ -57483,29 +57656,34 @@ async function runEngine2(launch, plan, index, units, again = "", slot = null) {
|
|
|
57483
57656
|
printed,
|
|
57484
57657
|
verdict: judgeEngine(printed, assignment, end),
|
|
57485
57658
|
log,
|
|
57486
|
-
seconds: (Date.now() - began) / 1000
|
|
57659
|
+
seconds: (Date.now() - began) / 1000,
|
|
57660
|
+
again: again !== ""
|
|
57487
57661
|
};
|
|
57488
57662
|
}
|
|
57489
|
-
async function watched(launch, args, directory, log,
|
|
57490
|
-
const user =
|
|
57663
|
+
async function watched(launch, args, directory, log, silenceSeconds) {
|
|
57664
|
+
const user = join23(directory, "user");
|
|
57491
57665
|
const env = { ...process12.env, APPDATA: user, XDG_DATA_HOME: user };
|
|
57492
57666
|
const output = openSync3(log, "w");
|
|
57493
57667
|
let child;
|
|
57494
57668
|
let stop;
|
|
57495
57669
|
let helper = null;
|
|
57496
57670
|
try {
|
|
57497
|
-
if (process12.platform === "win32"
|
|
57498
|
-
const where =
|
|
57671
|
+
if (process12.platform === "win32") {
|
|
57672
|
+
const where = "on a desktop of its own";
|
|
57499
57673
|
const compiled = await ensureHelperCompiled();
|
|
57500
57674
|
if (compiled !== null) {
|
|
57501
57675
|
return { code: null, signal: null, ended: `the engine could not be started ${where}: ${compiled}` };
|
|
57502
57676
|
}
|
|
57503
|
-
const stopFile =
|
|
57504
|
-
const error =
|
|
57505
|
-
helper = { error, how: where, pid:
|
|
57506
|
-
const how =
|
|
57677
|
+
const stopFile = join23(directory, "stop");
|
|
57678
|
+
const error = join23(directory, "error");
|
|
57679
|
+
helper = { error, how: where, pid: join23(directory, "pid"), step: join23(directory, "step") };
|
|
57680
|
+
const how = {
|
|
57681
|
+
desktop: HIDDEN_DESKTOP,
|
|
57682
|
+
directory: launch.projectPath,
|
|
57683
|
+
...launch.cores === null ? {} : { affinity: (1n << BigInt(launch.cores)) - 1n }
|
|
57684
|
+
};
|
|
57507
57685
|
const wrapper = throughHelper(how, launch.godotPath, args, {
|
|
57508
|
-
spec:
|
|
57686
|
+
spec: join23(directory, "spec"),
|
|
57509
57687
|
pid: helper.pid,
|
|
57510
57688
|
error,
|
|
57511
57689
|
stop: stopFile,
|
|
@@ -57513,18 +57691,19 @@ async function watched(launch, args, directory, log, windowed, silenceSeconds) {
|
|
|
57513
57691
|
});
|
|
57514
57692
|
child = spawnHidden(wrapper.command, wrapper.args, {
|
|
57515
57693
|
stdio: ["ignore", output, output],
|
|
57516
|
-
env:
|
|
57694
|
+
env: { ...env, [DESKTOP_VARIABLE]: HIDDEN_DESKTOP },
|
|
57517
57695
|
cwd: directory
|
|
57518
57696
|
});
|
|
57519
57697
|
stop = () => {
|
|
57520
|
-
|
|
57698
|
+
writeFileSync11(stopFile, "");
|
|
57521
57699
|
};
|
|
57522
57700
|
} else {
|
|
57523
|
-
|
|
57701
|
+
const held = heldToCores(launch.cores, launch.godotPath, args);
|
|
57702
|
+
child = spawnHidden(held.command, held.args, {
|
|
57524
57703
|
stdio: ["ignore", output, output],
|
|
57525
57704
|
env,
|
|
57526
57705
|
cwd: launch.projectPath,
|
|
57527
|
-
detached:
|
|
57706
|
+
detached: true
|
|
57528
57707
|
});
|
|
57529
57708
|
stop = () => {
|
|
57530
57709
|
endTree(child);
|
|
@@ -57551,10 +57730,17 @@ async function watched(launch, args, directory, log, windowed, silenceSeconds) {
|
|
|
57551
57730
|
const reading = openSync3(log, "r");
|
|
57552
57731
|
try {
|
|
57553
57732
|
let size = 0;
|
|
57554
|
-
|
|
57733
|
+
const started = Date.now();
|
|
57734
|
+
let lastGrowth = started;
|
|
57555
57735
|
let text = "";
|
|
57556
57736
|
while (!finished.now) {
|
|
57557
57737
|
await delay3(LOOK_MS);
|
|
57738
|
+
const deadline = launch.deadlineSeconds;
|
|
57739
|
+
if (ended === null && deadline !== null && Date.now() - started > deadline * 1000) {
|
|
57740
|
+
const stoppedIn = readRun(text).stoppedIn;
|
|
57741
|
+
ended = `the engine ran past the ${String(deadline)} s deadline${stoppedIn === null ? "" : ` while running ${stoppedIn}`}, so it was ended`;
|
|
57742
|
+
stop();
|
|
57743
|
+
}
|
|
57558
57744
|
const now = fstatSync(reading).size;
|
|
57559
57745
|
if (now > size) {
|
|
57560
57746
|
const chunk = Buffer.alloc(now - size);
|
|
@@ -57578,11 +57764,12 @@ async function watched(launch, args, directory, log, windowed, silenceSeconds) {
|
|
|
57578
57764
|
launch.running.delete(stop);
|
|
57579
57765
|
}
|
|
57580
57766
|
const { code, signal } = await exited;
|
|
57767
|
+
const freeBytes = code !== null && code >>> 0 >= WINDOWS_EXCEPTION ? freemem() : undefined;
|
|
57581
57768
|
const failed = helper === null ? null : helperFailure(helper.error);
|
|
57582
57769
|
if (failed !== null && helper !== null) {
|
|
57583
57770
|
ended ??= `the engine could not be started ${helper.how}: ${failed}`;
|
|
57584
57771
|
}
|
|
57585
|
-
return { code, signal, ended };
|
|
57772
|
+
return { code, signal, ended, ...freeBytes === undefined ? {} : { freeBytes } };
|
|
57586
57773
|
}
|
|
57587
57774
|
function endTree(child) {
|
|
57588
57775
|
if (child.pid === undefined) {
|
|
@@ -57606,11 +57793,22 @@ function report(ran, across, startedAt, runDirectory, printOutput) {
|
|
|
57606
57793
|
}
|
|
57607
57794
|
const failed = ran.filter((engine) => !engine.verdict.passed);
|
|
57608
57795
|
for (const engine of failed) {
|
|
57796
|
+
const heading = `--- engine ${String(engine.index)}: ${plural(engine.plan.suites.length, "suite")}, ${engine.seconds.toFixed(1)} s${engine.again ? ", run again alone after an engine ran out of memory, and failed again" : ""}`;
|
|
57609
57797
|
console.log("");
|
|
57610
|
-
console.log(
|
|
57798
|
+
console.log(heading);
|
|
57611
57799
|
for (const fault of engine.verdict.faults) {
|
|
57612
57800
|
console.log(` ${fault}`);
|
|
57613
57801
|
}
|
|
57802
|
+
if (existsSync19(engine.log)) {
|
|
57803
|
+
const verdict = [
|
|
57804
|
+
`gdharness: ${heading.slice(4)}`,
|
|
57805
|
+
...engine.verdict.faults.map((fault) => ` ${fault}`)
|
|
57806
|
+
];
|
|
57807
|
+
writeFileSync11(engine.log, `${verdict.join(`
|
|
57808
|
+
`)}
|
|
57809
|
+
---
|
|
57810
|
+
${readFileSync20(engine.log, "utf8")}`);
|
|
57811
|
+
}
|
|
57614
57812
|
if (engine.verdict.shown.length > 0) {
|
|
57615
57813
|
console.log("");
|
|
57616
57814
|
for (const line of engine.verdict.shown) {
|
|
@@ -57630,6 +57828,14 @@ function report(ran, across, startedAt, runDirectory, printOutput) {
|
|
|
57630
57828
|
const suites = new Set(ran.flatMap((engine) => engine.plan.suites.map((suite) => suite.path))).size;
|
|
57631
57829
|
const longest = Math.max(0, ...ran.map((engine) => engine.seconds));
|
|
57632
57830
|
const took = (Date.now() - startedAt) / 1000;
|
|
57831
|
+
if (ran.length > 1) {
|
|
57832
|
+
console.log("");
|
|
57833
|
+
console.log(`The engines took ${ran.map((engine) => `${engine.seconds.toFixed(1)} s`).join(", ")}, dealt ${ran.map((engine) => `${engine.plan.cost.toFixed(1)} s`).join(", ")}.`);
|
|
57834
|
+
}
|
|
57835
|
+
const slowest = ran.flatMap((engine) => [...engine.printed.suites.values()].flatMap((suite) => suite.closed === null ? [] : [{ path: suite.path, seconds: suite.closed.seconds }])).sort((a, b) => b.seconds - a.seconds).slice(0, SLOWEST_NAMED);
|
|
57836
|
+
if (slowest.length > 1) {
|
|
57837
|
+
console.log(`The slowest suites: ${slowest.map((suite) => `${suite.path} ${suite.seconds.toFixed(1)} s`).join(", ")}.`);
|
|
57838
|
+
}
|
|
57633
57839
|
const totals = `${plural(cases, "case")} in ${plural(suites, "suite")} across ${plural(ran.length, "engine")}, ${took.toFixed(1)} s; the longest engine took ${longest.toFixed(1)} s.`;
|
|
57634
57840
|
console.log("");
|
|
57635
57841
|
if (failed.length === 0 && across.length === 0) {
|
|
@@ -57652,7 +57858,7 @@ class UsageError extends Refusal {
|
|
|
57652
57858
|
function projectArgument(at) {
|
|
57653
57859
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
57654
57860
|
const projectPath = resolve10(named ?? ".");
|
|
57655
|
-
if (!existsSync31(
|
|
57861
|
+
if (!existsSync31(join40(projectPath, "project.godot"))) {
|
|
57656
57862
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
57657
57863
|
}
|
|
57658
57864
|
return projectPath;
|
|
@@ -57697,7 +57903,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
57697
57903
|
return;
|
|
57698
57904
|
}
|
|
57699
57905
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
57700
|
-
if (!existsSync31(
|
|
57906
|
+
if (!existsSync31(join40(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
57701
57907
|
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}`);
|
|
57702
57908
|
}
|
|
57703
57909
|
}
|
|
@@ -58069,7 +58275,8 @@ Usage:
|
|
|
58069
58275
|
an export if it is left on
|
|
58070
58276
|
gdharness classes [project] Rebuild .godot/global_script_class_cache.cfg from disk
|
|
58071
58277
|
gdharness test [project] [suite ...] [--engines N] [--slots DIR] [--slot-count N]
|
|
58072
|
-
[--silence S] [--tests DIR] [--changed-only] [--reach-also PATH]
|
|
58278
|
+
[--silence S] [--deadline S] [--tests DIR] [--changed-only] [--reach-also PATH]
|
|
58279
|
+
[--import] [--godot PATH] [--godot-version V] [--cores N]
|
|
58073
58280
|
[--order reversed|SEED] [--print-output] [--runner]
|
|
58074
58281
|
Run the project's gdUnit4 suites over several engines and
|
|
58075
58282
|
judge every line they print: exit 1 on a failing case, an
|
|
@@ -58103,7 +58310,7 @@ async function test() {
|
|
|
58103
58310
|
return;
|
|
58104
58311
|
}
|
|
58105
58312
|
const options = testOptionsOf(rest, process.cwd());
|
|
58106
|
-
const
|
|
58313
|
+
const godotPath = options.godotPath ?? (await engine(options.projectPath)).godotPath;
|
|
58107
58314
|
process.exitCode = await runTestCommand(options, godotPath);
|
|
58108
58315
|
}
|
|
58109
58316
|
async function main() {
|