@rehearsal-labs/retest 0.2.0 → 0.3.0
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/README.md +6 -5
- package/dist/api/source-location.d.ts.map +1 -1
- package/dist/api/source-location.js +3 -1
- package/dist/browser/builds.d.ts +35 -6
- package/dist/browser/builds.d.ts.map +1 -1
- package/dist/browser/builds.js +115 -16
- package/dist/browser/cdp/transport.d.ts.map +1 -1
- package/dist/browser/cdp/transport.js +4 -4
- package/dist/browser/chromium-process.d.ts +23 -0
- package/dist/browser/chromium-process.d.ts.map +1 -1
- package/dist/browser/chromium-process.js +78 -13
- package/dist/browser/electron.d.ts +1 -1
- package/dist/browser/electron.d.ts.map +1 -1
- package/dist/browser/electron.js +6 -3
- package/dist/browser/executables.d.ts +16 -1
- package/dist/browser/executables.d.ts.map +1 -1
- package/dist/browser/executables.js +50 -18
- package/dist/browser/firefox/accessible-names.d.ts +5 -4
- package/dist/browser/firefox/accessible-names.d.ts.map +1 -1
- package/dist/browser/firefox/accessible-names.js +11 -6
- package/dist/browser/firefox/bridge.js +3 -2
- package/dist/browser/firefox/executable.d.ts +14 -14
- package/dist/browser/firefox/executable.d.ts.map +1 -1
- package/dist/browser/firefox/executable.js +29 -21
- package/dist/browser/firefox/launch.js +1 -1
- package/dist/browser/firefox/navigation.d.ts +5 -2
- package/dist/browser/firefox/navigation.d.ts.map +1 -1
- package/dist/browser/firefox/navigation.js +48 -14
- package/dist/browser/firefox/orphans.d.ts.map +1 -1
- package/dist/browser/firefox/orphans.js +34 -2
- package/dist/browser/firefox/page.d.ts.map +1 -1
- package/dist/browser/firefox/page.js +113 -19
- package/dist/browser/firefox/process.d.ts +20 -1
- package/dist/browser/firefox/process.d.ts.map +1 -1
- package/dist/browser/firefox/process.js +124 -22
- package/dist/browser/firefox/profile.d.ts +5 -0
- package/dist/browser/firefox/profile.d.ts.map +1 -1
- package/dist/browser/firefox/profile.js +17 -1
- package/dist/browser/firefox/storage.d.ts.map +1 -1
- package/dist/browser/firefox/storage.js +4 -2
- package/dist/browser/firefox/subscriptions.d.ts +26 -0
- package/dist/browser/firefox/subscriptions.d.ts.map +1 -0
- package/dist/browser/firefox/subscriptions.js +10 -0
- package/dist/browser/launch.d.ts +6 -0
- package/dist/browser/launch.d.ts.map +1 -1
- package/dist/browser/launch.js +14 -1
- package/dist/cli/commands/doctor.js +3 -3
- package/dist/cli/commands/inspect.js +3 -2
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +26 -13
- package/dist/cli/commands/licences.d.ts +22 -0
- package/dist/cli/commands/licences.d.ts.map +1 -1
- package/dist/cli/commands/licences.js +62 -20
- package/dist/cli/commands/run.js +2 -2
- package/dist/cli/doctor/checks.js +4 -1
- package/dist/cli/inspect/diagnostics.js +3 -3
- package/dist/cli/inspect/test-timeline.js +10 -10
- package/dist/cli/install/doctor-rows.d.ts.map +1 -1
- package/dist/cli/install/doctor-rows.js +5 -4
- package/dist/cli/install/file-changes.d.ts +44 -0
- package/dist/cli/install/file-changes.d.ts.map +1 -0
- package/dist/cli/install/file-changes.js +81 -0
- package/dist/cli/install/install-archive.d.ts +3 -1
- package/dist/cli/install/install-archive.d.ts.map +1 -1
- package/dist/cli/install/install-archive.js +80 -33
- package/dist/cli/install/lock.d.ts.map +1 -1
- package/dist/cli/install/lock.js +108 -21
- package/dist/cli/install/media-doctor.d.ts.map +1 -1
- package/dist/cli/install/media-doctor.js +2 -1
- package/dist/cli/install/media-install.d.ts.map +1 -1
- package/dist/cli/install/media-install.js +38 -19
- package/dist/cli/install/media-pins.d.ts +7 -1
- package/dist/cli/install/media-pins.d.ts.map +1 -1
- package/dist/cli/install/media-pins.js +23 -12
- package/dist/cli/install/media-record.d.ts.map +1 -1
- package/dist/cli/install/media-record.js +10 -9
- package/dist/cli/install/media-report.d.ts +3 -1
- package/dist/cli/install/media-report.d.ts.map +1 -1
- package/dist/cli/install/media-report.js +7 -5
- package/dist/cli/install/media-tools.d.ts.map +1 -1
- package/dist/cli/install/media-tools.js +6 -4
- package/dist/cli/install/process-start.d.ts +3 -2
- package/dist/cli/install/process-start.d.ts.map +1 -1
- package/dist/cli/install/process-start.js +4 -0
- package/dist/cli/install/report.d.ts.map +1 -1
- package/dist/cli/install/report.js +11 -7
- package/dist/cli/install/supervisor-install.d.ts +33 -0
- package/dist/cli/install/supervisor-install.d.ts.map +1 -0
- package/dist/cli/install/supervisor-install.js +158 -0
- package/dist/cli/install/supervisor-locate.d.ts +47 -0
- package/dist/cli/install/supervisor-locate.d.ts.map +1 -0
- package/dist/cli/install/supervisor-locate.js +113 -0
- package/dist/cli/install/supervisor-pins.d.ts +21 -0
- package/dist/cli/install/supervisor-pins.d.ts.map +1 -0
- package/dist/cli/install/supervisor-pins.js +28 -0
- package/dist/cli/install/supervisor-record.d.ts +31 -0
- package/dist/cli/install/supervisor-record.d.ts.map +1 -0
- package/dist/cli/install/supervisor-record.js +104 -0
- package/dist/cli/install/supervisor-report.d.ts +11 -0
- package/dist/cli/install/supervisor-report.d.ts.map +1 -0
- package/dist/cli/install/supervisor-report.js +33 -0
- package/dist/cli/install/supervisor-tools.d.ts +43 -0
- package/dist/cli/install/supervisor-tools.d.ts.map +1 -0
- package/dist/cli/install/supervisor-tools.js +114 -0
- package/dist/cli/install/unpack.d.ts +23 -3
- package/dist/cli/install/unpack.d.ts.map +1 -1
- package/dist/cli/install/unpack.js +124 -10
- package/dist/cli/install/zip-member.d.ts +15 -0
- package/dist/cli/install/zip-member.d.ts.map +1 -0
- package/dist/cli/install/zip-member.js +77 -0
- package/dist/cli/interrupt.d.ts +1 -1
- package/dist/cli/interrupt.d.ts.map +1 -1
- package/dist/cli/interrupt.js +15 -10
- package/dist/cli/terminal.d.ts +9 -1
- package/dist/cli/terminal.d.ts.map +1 -1
- package/dist/cli/terminal.js +14 -0
- package/dist/config/define.d.ts +2 -2
- package/dist/config/define.js +2 -2
- package/dist/diagnostics/firefox-collector.d.ts +11 -10
- package/dist/diagnostics/firefox-collector.d.ts.map +1 -1
- package/dist/diagnostics/firefox-collector.js +16 -11
- package/dist/media/client.d.ts +2 -1
- package/dist/media/client.d.ts.map +1 -1
- package/dist/media/client.js +16 -5
- package/dist/media/host.d.ts +32 -0
- package/dist/media/host.d.ts.map +1 -0
- package/dist/media/host.js +50 -0
- package/dist/media/locate.d.ts.map +1 -1
- package/dist/media/locate.js +4 -3
- package/dist/native/processes.d.ts.map +1 -1
- package/dist/native/processes.js +7 -3
- package/dist/protocol/files.d.ts +1 -1
- package/dist/protocol/files.d.ts.map +1 -1
- package/dist/protocol/files.js +10 -5
- package/dist/reporters/commands.d.ts +12 -3
- package/dist/reporters/commands.d.ts.map +1 -1
- package/dist/reporters/commands.js +29 -11
- package/dist/reporters/human-card.js +1 -1
- package/dist/reporters/human.d.ts +3 -1
- package/dist/reporters/human.d.ts.map +1 -1
- package/dist/reporters/human.js +8 -8
- package/dist/reporters/style.d.ts +21 -2
- package/dist/reporters/style.d.ts.map +1 -1
- package/dist/reporters/style.js +11 -2
- package/dist/runner/app-server.d.ts +27 -7
- package/dist/runner/app-server.d.ts.map +1 -1
- package/dist/runner/app-server.js +74 -24
- package/dist/runner/child.js +6 -2
- package/dist/runner/media-leftovers.d.ts +7 -3
- package/dist/runner/media-leftovers.d.ts.map +1 -1
- package/dist/runner/media-leftovers.js +27 -6
- package/dist/runner/resources.d.ts +8 -0
- package/dist/runner/resources.d.ts.map +1 -1
- package/dist/runner/resources.js +12 -1
- package/dist/runner/run-session.d.ts.map +1 -1
- package/dist/runner/run-session.js +9 -4
- package/dist/runner/run.d.ts.map +1 -1
- package/dist/runner/run.js +2 -1
- package/dist/runner/target-drivers.d.ts +5 -2
- package/dist/runner/target-drivers.d.ts.map +1 -1
- package/dist/runner/target-drivers.js +10 -1
- package/dist/runner/test-file-process.d.ts +8 -0
- package/dist/runner/test-file-process.d.ts.map +1 -1
- package/dist/runner/test-file-process.js +26 -3
- package/dist/runner/test-pages.d.ts.map +1 -1
- package/dist/runner/test-pages.js +5 -2
- package/dist/shared/environment.d.ts +17 -0
- package/dist/shared/environment.d.ts.map +1 -0
- package/dist/shared/environment.js +27 -0
- package/dist/shared/metadata-process-worker.js +15 -7
- package/dist/shared/metadata-process.d.ts +5 -0
- package/dist/shared/metadata-process.d.ts.map +1 -1
- package/dist/shared/metadata-process.js +36 -6
- package/dist/shared/posix-path.d.ts +12 -0
- package/dist/shared/posix-path.d.ts.map +1 -1
- package/dist/shared/posix-path.js +16 -0
- package/dist/shared/private-file.d.ts +38 -0
- package/dist/shared/private-file.d.ts.map +1 -0
- package/dist/shared/private-file.js +132 -0
- package/dist/shared/process-ownership.d.ts +52 -2
- package/dist/shared/process-ownership.d.ts.map +1 -1
- package/dist/shared/process-ownership.js +104 -11
- package/dist/shared/process-supervisor.d.ts +309 -0
- package/dist/shared/process-supervisor.d.ts.map +1 -0
- package/dist/shared/process-supervisor.js +737 -0
- package/dist/shared/process-system.d.ts +113 -0
- package/dist/shared/process-system.d.ts.map +1 -0
- package/dist/shared/process-system.js +254 -0
- package/dist/shared/regular-file.d.ts +2 -2
- package/dist/shared/regular-file.d.ts.map +1 -1
- package/dist/shared/regular-file.js +19 -2
- package/dist/store/artifacts.d.ts +5 -4
- package/dist/store/artifacts.d.ts.map +1 -1
- package/dist/store/artifacts.js +18 -5
- package/dist/store/run-store.d.ts +5 -2
- package/dist/store/run-store.d.ts.map +1 -1
- package/dist/store/run-store.js +48 -4
- package/media/Cargo.lock +16 -0
- package/media/Cargo.toml +16 -1
- package/media/src/encoder.rs +56 -12
- package/media/src/frame.rs +7 -1
- package/media/src/main.rs +20 -2
- package/media/src/place.rs +230 -11
- package/media/src/process_ownership_windows.rs +1058 -0
- package/media/src/recording.rs +15 -7
- package/media/src/replies.rs +55 -0
- package/media/src/server.rs +72 -2
- package/media/src/store.rs +71 -2
- package/package.json +9 -4
- package/src/cli/install/media-notices.txt +465 -1
- package/supervisor/Cargo.lock +25 -0
- package/supervisor/Cargo.toml +22 -0
- package/supervisor/NOTICES +464 -0
- package/supervisor/build.rs +44 -0
- package/supervisor/src/command.rs +612 -0
- package/supervisor/src/jobs.rs +1650 -0
- package/supervisor/src/main.rs +71 -0
- package/supervisor/src/reply.rs +367 -0
|
@@ -16,8 +16,9 @@ export type AppServerOptions = {
|
|
|
16
16
|
hiddenVariables?: readonly string[];
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
|
-
* A server `start` made ready. One that already
|
|
20
|
-
* the process id of its shell. `stop` ends only processes whose launch ancestry and
|
|
19
|
+
* A server `start` made ready. One that was already there, whose port took Retest's connection, is `reused` and never
|
|
20
|
+
* stopped. One Retest `started` has the process id of its shell. `stop` ends only processes whose launch ancestry and
|
|
21
|
+
* exact identity Retest recorded.
|
|
21
22
|
*/
|
|
22
23
|
export type AppServerHandle = {
|
|
23
24
|
readonly status: 'reused';
|
|
@@ -35,6 +36,21 @@ export declare class AppServerError extends Error {
|
|
|
35
36
|
readonly failure: Failure;
|
|
36
37
|
constructor(problem: Failure);
|
|
37
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* What one look at a server's `ready` address found. `answered`: an HTTP answer came, whatever its status.
|
|
41
|
+
* `accepted`: the port took the connection, so a server is there, but no HTTP answer came. `refused`: the connection
|
|
42
|
+
* was refused, or the host's name resolves to nothing, so no server is there. `unreached`: the connection neither opened
|
|
43
|
+
* nor was refused, or failed in another way first, or the address is not HTTP, so nothing is known.
|
|
44
|
+
*/
|
|
45
|
+
export type ReadyLook = 'answered' | 'accepted' | 'refused' | 'unreached';
|
|
46
|
+
/**
|
|
47
|
+
* Asks `url` once with an HTTP GET and says what it found within `timeoutMs`, or before `signal` aborts. What came in
|
|
48
|
+
* while this process was held busy counts, as does a connection asked for just before: a limit that passed while the
|
|
49
|
+
* process was held is judged a moment after it wakes, once those events have been read.
|
|
50
|
+
*
|
|
51
|
+
* @example await lookAtReady('http://127.0.0.1:3000/health', 1000) // 'answered'
|
|
52
|
+
*/
|
|
53
|
+
export declare function lookAtReady(url: string, timeoutMs: number, signal?: AbortSignal): Promise<ReadyLook>;
|
|
38
54
|
/**
|
|
39
55
|
* Asks `url` once, within `timeoutMs`, whether a server answers there. Any HTTP answer counts, whatever its
|
|
40
56
|
* status; a refused connection, a timeout or anything that is not HTTP does not.
|
|
@@ -43,17 +59,21 @@ export declare class AppServerError extends Error {
|
|
|
43
59
|
*/
|
|
44
60
|
export declare function probeReady(url: string, timeoutMs: number): Promise<boolean>;
|
|
45
61
|
/**
|
|
46
|
-
* Makes an app's server ready within `timeoutMs`.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
62
|
+
* Makes an app's server ready within `timeoutMs`. Whether a server is already there is a fact about the connection,
|
|
63
|
+
* not about how soon it answers. When the port at `ready` takes the connection, that server is reused: Retest waits
|
|
64
|
+
* for it to answer within `timeoutMs`, as it waits for one it starts, and one that never answers throws
|
|
65
|
+
* `AppServerError` and is left running. When the connection is refused, or neither opens nor is refused within a
|
|
66
|
+
* second, `command` runs in a shell, as its own process group, with its output in `logFile`, until `ready` answers.
|
|
67
|
+
* A server that exits first, or never answers, is stopped and throws `AppServerError`. The parent's exit also ends
|
|
49
68
|
* every recorded process still running, after checking its exact identity again.
|
|
50
69
|
*
|
|
51
70
|
* @example const server = await startAppServer({ name: 'web', start, logFile }, 60_000); await server.stop(1000)
|
|
52
71
|
*/
|
|
53
72
|
export declare function startAppServer(options: AppServerOptions, timeoutMs: number, dependencies?: AppServerDependencies): Promise<AppServerHandle>;
|
|
54
|
-
|
|
73
|
+
type ReadyLooker = (url: string, timeoutMs: number, signal?: AbortSignal) => Promise<ReadyLook>;
|
|
74
|
+
/** Internal readiness seams; production uses the owned server and the HTTP look. */
|
|
55
75
|
export type AppServerDependencies = {
|
|
56
|
-
readonly probe?:
|
|
76
|
+
readonly probe?: ReadyLooker;
|
|
57
77
|
readonly launch?: (options: AppServerOptions) => Promise<Launched>;
|
|
58
78
|
};
|
|
59
79
|
type Launched = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-server.d.ts","sourceRoot":"","sources":["../../src/runner/app-server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"app-server.d.ts","sourceRoot":"","sources":["../../src/runner/app-server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAmB7C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,WAAW,CAAA;IAClB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,wEAAwE;IACxE,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,4GAA4G;IAC5G,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,GAClG;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AAE7H,uGAAuG;AACvG,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAkB,IAAI,oBAAmB;IACzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;gBAEb,OAAO,EAAE,OAAO;CAI7B;AAOD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,WAAW,CAAA;AAMzE;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CA4BpG;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEjF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,qBAA0B,GAAG,OAAO,CAAC,eAAe,CAAC,CA6BrJ;AA2BD,KAAK,WAAW,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,CAAA;AAE/F,oFAAoF;AACpF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CACnE,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,CAAA;IACtC,8FAA8F;IAC9F,MAAM,IAAI,IAAI,CAAA;IACd,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,+CAA+C;IAC/C,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;CACvC,CAAA"}
|
|
@@ -9,9 +9,11 @@ import { closeGraceMs } from "../browser/contract.js";
|
|
|
9
9
|
import { Deadline, elapsedMs, monotonicClock, smallestBudget } from "../protocol/deadline.js";
|
|
10
10
|
import { errorMessage, failure, withAlso } from "../protocol/failures.js";
|
|
11
11
|
import { isWebUrl, withoutCredentials } from "../protocol/url.js";
|
|
12
|
+
import { withoutVariables } from "../shared/environment.js";
|
|
12
13
|
import { errorCode } from "../shared/error-code.js";
|
|
13
14
|
import { describeExit } from "../shared/process-exit.js";
|
|
14
15
|
import { OwnedProcessGroup } from "../shared/process-ownership.js";
|
|
16
|
+
import { consolesDetached, holdLaunch, launchOwnership, prepareOwnedLaunch, serverStopSignal } from "../shared/process-system.js";
|
|
15
17
|
import { bounded } from "./bounded.js";
|
|
16
18
|
/** Thrown when a server does not become ready. `failure.class` is `setup_failed`, or `interrupted`. */
|
|
17
19
|
export class AppServerError extends Error {
|
|
@@ -26,46 +28,77 @@ const probeLimitMs = 1000;
|
|
|
26
28
|
const pollIntervalMs = 100;
|
|
27
29
|
const liveServers = new Set();
|
|
28
30
|
let exitHookInstalled = false;
|
|
31
|
+
// The errors that say no server is there: nothing listens on the port, or the name is no host's.
|
|
32
|
+
const noServerCodes = new Set(['ECONNREFUSED', 'ENOTFOUND']);
|
|
33
|
+
const wakeGraceMs = 50;
|
|
29
34
|
/**
|
|
30
|
-
* Asks `url` once
|
|
31
|
-
*
|
|
35
|
+
* Asks `url` once with an HTTP GET and says what it found within `timeoutMs`, or before `signal` aborts. What came in
|
|
36
|
+
* while this process was held busy counts, as does a connection asked for just before: a limit that passed while the
|
|
37
|
+
* process was held is judged a moment after it wakes, once those events have been read.
|
|
32
38
|
*
|
|
33
|
-
* @example await
|
|
39
|
+
* @example await lookAtReady('http://127.0.0.1:3000/health', 1000) // 'answered'
|
|
34
40
|
*/
|
|
35
|
-
export function
|
|
41
|
+
export function lookAtReady(url, timeoutMs, signal) {
|
|
36
42
|
const target = URL.parse(url);
|
|
37
43
|
if (!isWebUrl(target))
|
|
38
|
-
return Promise.resolve(
|
|
44
|
+
return Promise.resolve('unreached');
|
|
39
45
|
const get = target.protocol === 'https:' ? httpsGet : httpGet;
|
|
40
46
|
const { promise, resolve } = Promise.withResolvers();
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
let connected = false;
|
|
48
|
+
const request = get(target, { agent: false, ...(signal === undefined ? {} : { signal }) }, (response) => {
|
|
49
|
+
resolve('answered');
|
|
43
50
|
response.destroy();
|
|
44
51
|
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
request.on('socket', (socket) => {
|
|
53
|
+
if (!socket.connecting) {
|
|
54
|
+
connected = socket.readyState === 'open';
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
socket.once('connect', () => {
|
|
58
|
+
connected = true;
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
// A timer that came due while the process was held runs before the events that came in meanwhile, and Node sends a
|
|
62
|
+
// connection it was asked for only once the process is free; so a late timer gives the look a moment more.
|
|
63
|
+
const dueAt = monotonicClock() + timeoutMs;
|
|
64
|
+
let timer = setTimeout(() => {
|
|
65
|
+
timer = setTimeout(() => request.destroy(), monotonicClock() - dueAt > wakeGraceMs ? wakeGraceMs : 0);
|
|
66
|
+
}, timeoutMs);
|
|
67
|
+
request.on('error', (error) => resolve(connected ? 'accepted' : noServerCodes.has(errorCode(error) ?? '') ? 'refused' : 'unreached'));
|
|
68
|
+
request.on('close', () => resolve(connected ? 'accepted' : 'unreached'));
|
|
48
69
|
return promise.finally(() => clearTimeout(timer));
|
|
49
70
|
}
|
|
50
71
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
72
|
+
* Asks `url` once, within `timeoutMs`, whether a server answers there. Any HTTP answer counts, whatever its
|
|
73
|
+
* status; a refused connection, a timeout or anything that is not HTTP does not.
|
|
74
|
+
*
|
|
75
|
+
* @example await probeReady('http://127.0.0.1:3000/health', 1000) // true
|
|
76
|
+
*/
|
|
77
|
+
export async function probeReady(url, timeoutMs) {
|
|
78
|
+
return (await lookAtReady(url, timeoutMs)) === 'answered';
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Makes an app's server ready within `timeoutMs`. Whether a server is already there is a fact about the connection,
|
|
82
|
+
* not about how soon it answers. When the port at `ready` takes the connection, that server is reused: Retest waits
|
|
83
|
+
* for it to answer within `timeoutMs`, as it waits for one it starts, and one that never answers throws
|
|
84
|
+
* `AppServerError` and is left running. When the connection is refused, or neither opens nor is refused within a
|
|
85
|
+
* second, `command` runs in a shell, as its own process group, with its output in `logFile`, until `ready` answers.
|
|
86
|
+
* A server that exits first, or never answers, is stopped and throws `AppServerError`. The parent's exit also ends
|
|
54
87
|
* every recorded process still running, after checking its exact identity again.
|
|
55
88
|
*
|
|
56
89
|
* @example const server = await startAppServer({ name: 'web', start, logFile }, 60_000); await server.stop(1000)
|
|
57
90
|
*/
|
|
58
91
|
export async function startAppServer(options, timeoutMs, dependencies = {}) {
|
|
59
92
|
const interrupted = () => options.signal?.aborted === true;
|
|
60
|
-
const
|
|
93
|
+
const look = dependencies.probe ?? lookAtReady;
|
|
61
94
|
const launchServer = dependencies.launch ?? launch;
|
|
62
95
|
const startedAt = monotonicClock();
|
|
63
96
|
const deadline = new Deadline(timeoutMs, { startedAt });
|
|
64
97
|
const { name, start } = options;
|
|
65
98
|
const ready = withoutCredentials(start.ready);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
99
|
+
const found = await look(start.ready, smallestBudget(probeLimitMs, deadline.commandTimeoutMs), options.signal);
|
|
100
|
+
if (found === 'answered' || found === 'accepted')
|
|
101
|
+
return reuse({ options, timeoutMs, deadline, startedAt, look, found });
|
|
69
102
|
const server = await launchServer(options);
|
|
70
103
|
for (;;) {
|
|
71
104
|
if (interrupted())
|
|
@@ -73,7 +106,7 @@ export async function startAppServer(options, timeoutMs, dependencies = {}) {
|
|
|
73
106
|
if (server.exit !== undefined) {
|
|
74
107
|
return server.fail(failure('setup_failed', `The server for ${name} exited with ${describeExit(server.exit)} before ${ready} answered. Its output is in ${options.logFile}.`));
|
|
75
108
|
}
|
|
76
|
-
if (await
|
|
109
|
+
if ((await look(start.ready, smallestBudget(probeLimitMs, deadline.commandTimeoutMs))) === 'answered') {
|
|
77
110
|
server.settle();
|
|
78
111
|
return { status: 'started', pid: server.pid, durationMs: elapsedMs(startedAt), stop: (budget) => server.stop(budget) };
|
|
79
112
|
}
|
|
@@ -89,6 +122,21 @@ export async function startAppServer(options, timeoutMs, dependencies = {}) {
|
|
|
89
122
|
}
|
|
90
123
|
return server.fail(failure('setup_failed', `The server for ${name} did not answer at ${ready} within ${timeoutMs} ms. Its output is in ${options.logFile}.`));
|
|
91
124
|
}
|
|
125
|
+
// A server already there is waited for as long as one Retest starts, each look held open for the time left, so a
|
|
126
|
+
// server slower than a look's second still answers. Retest started nothing, so it stops nothing, whatever the outcome.
|
|
127
|
+
async function reuse({ options, timeoutMs, deadline, startedAt, look, found }) {
|
|
128
|
+
const { name, start, signal } = options;
|
|
129
|
+
for (let last = found; last !== 'answered'; last = await look(start.ready, deadline.commandTimeoutMs, signal)) {
|
|
130
|
+
await sleep(smallestBudget(pollIntervalMs, deadline.waitToEndMs));
|
|
131
|
+
if (signal?.aborted === true)
|
|
132
|
+
throw new AppServerError(failure('interrupted', `Waiting for the server for ${name} was interrupted.`));
|
|
133
|
+
if (deadline.reached) {
|
|
134
|
+
const ready = withoutCredentials(start.ready);
|
|
135
|
+
throw new AppServerError(failure('setup_failed', `Something was already listening at ${ready}, so Retest used it as the server for ${name} and started nothing, but it did not answer within ${timeoutMs} ms.`));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { status: 'reused', durationMs: elapsedMs(startedAt), stop: async () => undefined };
|
|
139
|
+
}
|
|
92
140
|
/**
|
|
93
141
|
* The server's processes as Retest recorded them. A shell may replace itself with the command it runs, as `sh -c
|
|
94
142
|
* "node server.js"` does, keeping its pid and start under a new command line, and the ownership rule refuses a record
|
|
@@ -105,7 +153,7 @@ class ServerOwnership {
|
|
|
105
153
|
constructor(child, pid) {
|
|
106
154
|
this.#child = child;
|
|
107
155
|
this.#pid = pid;
|
|
108
|
-
this.#group =
|
|
156
|
+
this.#group = holdLaunch(child);
|
|
109
157
|
}
|
|
110
158
|
get group() {
|
|
111
159
|
return this.#group;
|
|
@@ -117,7 +165,7 @@ class ServerOwnership {
|
|
|
117
165
|
this.#settled = true;
|
|
118
166
|
if (this.#child.exitCode !== null || this.#child.signalCode !== null)
|
|
119
167
|
return;
|
|
120
|
-
const settled =
|
|
168
|
+
const settled = launchOwnership(this.#pid);
|
|
121
169
|
if (settled.capture().length === 0)
|
|
122
170
|
this.#group = settled;
|
|
123
171
|
}
|
|
@@ -126,11 +174,13 @@ async function launch({ name, start, logFile, redactor, hiddenVariables }) {
|
|
|
126
174
|
const folder = checkFolder(name, start.cwd);
|
|
127
175
|
mkdirSync(dirname(logFile), { recursive: true });
|
|
128
176
|
const log = openSync(logFile, 'a');
|
|
129
|
-
const
|
|
130
|
-
const env = Object.fromEntries(Object.entries(process.env).filter(([variable]) => !hidden.has(variable)));
|
|
177
|
+
const env = withoutVariables(process.env, hiddenVariables);
|
|
131
178
|
let child;
|
|
132
179
|
try {
|
|
133
|
-
|
|
180
|
+
prepareOwnedLaunch();
|
|
181
|
+
// windowsHide gives the server, on Windows, a console of its own with no window, which Retest can send Ctrl+C to
|
|
182
|
+
// without reaching its own; Node ignores it elsewhere.
|
|
183
|
+
child = spawn(start.command, { shell: true, cwd: folder, env, detached: consolesDetached, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
134
184
|
}
|
|
135
185
|
catch (error) {
|
|
136
186
|
const problem = failure('setup_failed', `The server for ${name} could not start: ${outputErrorMessage(error, redactor)}`);
|
|
@@ -291,7 +341,7 @@ function outputErrorMessage(error, redactor) {
|
|
|
291
341
|
async function stopGroup(server, timeoutMs) {
|
|
292
342
|
server.settle();
|
|
293
343
|
const ownership = server.group;
|
|
294
|
-
const problems = ownership.signal(
|
|
344
|
+
const problems = ownership.signal(serverStopSignal);
|
|
295
345
|
let gone = await groupGoneWithin(ownership, timeoutMs);
|
|
296
346
|
if (!gone) {
|
|
297
347
|
problems.push(...ownership.signal('SIGKILL'));
|
package/dist/runner/child.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { enableCompileCache } from 'node:module';
|
|
2
2
|
// The test file's process. Node compiles every module of an import graph before any of them runs, so the cache goes
|
|
3
3
|
// on here, in a file that imports nothing, and the program is loaded only after. The parent shares the cache: both
|
|
4
|
-
// use Node's default folder unless NODE_COMPILE_CACHE names one.
|
|
5
|
-
|
|
4
|
+
// use Node's default folder unless NODE_COMPILE_CACHE names one. On Windows it stays off: Node writes the cache as the
|
|
5
|
+
// process exits, and from an empty cache that write took longer there than the second the runner gives a process it
|
|
6
|
+
// asked to close, and moved earlier, longer than collection's budget. NODE_COMPILE_CACHE would turn it on before this
|
|
7
|
+
// line runs, so the runner leaves it out of this process's environment there (`testFileEnvironment`).
|
|
8
|
+
if (process.platform !== 'win32')
|
|
9
|
+
enableCompileCache();
|
|
6
10
|
await import("./child-program.js");
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* A Retest process, by pid and start time as `ps` prints it, or on Windows as its creation time in 100 ns intervals, so a
|
|
3
|
+
* pid used again by another process is never taken for it.
|
|
4
|
+
*/
|
|
2
5
|
export type ProcessMark = {
|
|
3
6
|
readonly startTimeVersion?: number;
|
|
4
7
|
readonly pid: number;
|
|
@@ -24,8 +27,9 @@ export type UnfinishedRun = {
|
|
|
24
27
|
*/
|
|
25
28
|
export declare function unfinishedRuns(runFolder: string): UnfinishedRun[];
|
|
26
29
|
/**
|
|
27
|
-
* Whether the process a mark names still runs: true for a confirmed current start, false only when a whole table
|
|
28
|
-
*
|
|
30
|
+
* Whether the process a mark names still runs: true for a confirmed current start, false only when a whole table, or on
|
|
31
|
+
* Windows retest-supervisor's reading of the pid, shows it absent; undefined for a present legacy or mismatched start, or
|
|
32
|
+
* an unreadable host. Unknowns stay alone.
|
|
29
33
|
*
|
|
30
34
|
* @example await processRuns({ pid: 4242, startedAt: 'Mon Oct 6 10:00:01 2026' }) // false
|
|
31
35
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media-leftovers.d.ts","sourceRoot":"","sources":["../../src/runner/media-leftovers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"media-leftovers.d.ts","sourceRoot":"","sources":["../../src/runner/media-leftovers.ts"],"names":[],"mappings":"AAaA;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAE/H;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAA;AAQjJ;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE,CAsBjE;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAWjF;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQhE;AAgFD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKhF"}
|
|
@@ -5,6 +5,7 @@ import { eventsFile, resultFile } from "../protocol/run-folder.js";
|
|
|
5
5
|
import { parse } from "../protocol/schema.js";
|
|
6
6
|
import { readMetadataProcessAsync } from "../shared/metadata-process.js";
|
|
7
7
|
import { readProcessTableAsync } from "../shared/process-ownership.js";
|
|
8
|
+
import { heldLaunches } from "../shared/process-system.js";
|
|
8
9
|
import { createArtifactFolder, readArtifactFile, referenceProblem, resolveReference } from "../store/artifacts.js";
|
|
9
10
|
// How many run folders beside this one are read, newest first, and how much of each one's events.
|
|
10
11
|
const maxFolders = 16;
|
|
@@ -44,19 +45,22 @@ export function unfinishedRuns(runFolder) {
|
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
47
|
/**
|
|
47
|
-
* Whether the process a mark names still runs: true for a confirmed current start, false only when a whole table
|
|
48
|
-
*
|
|
48
|
+
* Whether the process a mark names still runs: true for a confirmed current start, false only when a whole table, or on
|
|
49
|
+
* Windows retest-supervisor's reading of the pid, shows it absent; undefined for a present legacy or mismatched start, or
|
|
50
|
+
* an unreadable host. Unknowns stay alone.
|
|
49
51
|
*
|
|
50
52
|
* @example await processRuns({ pid: 4242, startedAt: 'Mon Oct 6 10:00:01 2026' }) // false
|
|
51
53
|
*/
|
|
52
54
|
export async function processRuns(mark) {
|
|
53
55
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
+
const launches = heldLaunches();
|
|
57
|
+
// Windows has no process table to read; retest-supervisor reads the one pid, and answers none when nothing runs under it.
|
|
58
|
+
const startedAt = launches !== undefined ? await heldStart(launches, mark.pid) : (await readProcessTableAsync()).find(entry => entry.pid === mark.pid)?.startedAt;
|
|
59
|
+
if (startedAt === undefined)
|
|
56
60
|
return false;
|
|
57
61
|
if (mark.startTimeVersion !== 1)
|
|
58
62
|
return undefined;
|
|
59
|
-
return mark.startedAt === undefined || mark.startedAt ===
|
|
63
|
+
return mark.startedAt === undefined || mark.startedAt === startedAt.replace(/\s+/g, ' ') ? true : undefined;
|
|
60
64
|
}
|
|
61
65
|
catch {
|
|
62
66
|
return undefined;
|
|
@@ -67,7 +71,24 @@ export async function processRuns(mark) {
|
|
|
67
71
|
* Undefined when the host could not be read.
|
|
68
72
|
*/
|
|
69
73
|
export async function ownStartTime() {
|
|
70
|
-
|
|
74
|
+
const launches = heldLaunches();
|
|
75
|
+
if (launches === undefined)
|
|
76
|
+
return (await processStartTimes([process.pid]))?.get(process.pid);
|
|
77
|
+
try {
|
|
78
|
+
return await heldStart(launches, process.pid);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// When the process under `pid` started, as retest-supervisor reads it without holding this thread, or undefined when
|
|
85
|
+
// nothing runs under the pid. Throws when the runner is not held or the reading fails.
|
|
86
|
+
async function heldStart(launches, pid) {
|
|
87
|
+
launches.prepare();
|
|
88
|
+
const supervisor = launches.supervisor;
|
|
89
|
+
if (supervisor === undefined)
|
|
90
|
+
throw new Error('The runner was not held, so no process could be read.');
|
|
91
|
+
return (await supervisor.processAsync(pid)).process?.startedAt;
|
|
71
92
|
}
|
|
72
93
|
// One reading for the pids asked; this process is always among those read, so `ps` succeeds when the others are gone.
|
|
73
94
|
async function processStartTimes(pids) {
|
|
@@ -213,6 +213,14 @@ export declare class ResourceLease {
|
|
|
213
213
|
*/
|
|
214
214
|
finish(timeoutMs: number): Promise<void>;
|
|
215
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* A part as an event of the attempt records it: a data folder's name with forward slashes on every platform, so a run on
|
|
218
|
+
* Windows records what a run on macOS records; any other part as it is. It takes the part once its name is redacted, so
|
|
219
|
+
* a secret is looked for in the name as the platform wrote it.
|
|
220
|
+
*
|
|
221
|
+
* @example recordedPart({ kind: 'data-folder', name: 'C:\\work\\.data\\desk', apps: ['desk'] }) // name 'C:/work/.data/desk' on Windows
|
|
222
|
+
*/
|
|
223
|
+
export declare function recordedPart(part: LeasePart): LeasePart;
|
|
216
224
|
/** A need as a part of a lease record. */
|
|
217
225
|
export declare function leasePart(need: ResourceNeed): LeasePart;
|
|
218
226
|
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/runner/resources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,YAAY,CAAA;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAkB,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/runner/resources.ts"],"names":[],"mappings":"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"}
|
package/dist/runner/resources.js
CHANGED
|
@@ -5,6 +5,7 @@ import { failure } from "../protocol/failures.js";
|
|
|
5
5
|
import { maxTimeout } from "../protocol/timeouts.js";
|
|
6
6
|
import { variantKey } from "../protocol/variant.js";
|
|
7
7
|
import { listWords } from "../shared/list-words.js";
|
|
8
|
+
import { platformPath, posixPath } from "../shared/posix-path.js";
|
|
8
9
|
import { bounded } from "./bounded.js";
|
|
9
10
|
import { SharedLocks } from "./locks.js";
|
|
10
11
|
import { sessionRefusal } from "./sessions.js";
|
|
@@ -109,7 +110,7 @@ export function describeLeasePart(part, variant) {
|
|
|
109
110
|
case 'device':
|
|
110
111
|
return `the simulator ${part.name}${of}`;
|
|
111
112
|
case 'data-folder':
|
|
112
|
-
return `the data folder ${part.name}${of}`;
|
|
113
|
+
return `the data folder ${platformPath(part.name)}${of}`;
|
|
113
114
|
case 'sessions': {
|
|
114
115
|
const count = part.count ?? 1;
|
|
115
116
|
return `${count} ${count === 1 ? 'session' : 'sessions'} of ${part.name}`;
|
|
@@ -304,6 +305,16 @@ export class ResourceLease {
|
|
|
304
305
|
listener(this.#expiry);
|
|
305
306
|
}
|
|
306
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* A part as an event of the attempt records it: a data folder's name with forward slashes on every platform, so a run on
|
|
310
|
+
* Windows records what a run on macOS records; any other part as it is. It takes the part once its name is redacted, so
|
|
311
|
+
* a secret is looked for in the name as the platform wrote it.
|
|
312
|
+
*
|
|
313
|
+
* @example recordedPart({ kind: 'data-folder', name: 'C:\\work\\.data\\desk', apps: ['desk'] }) // name 'C:/work/.data/desk' on Windows
|
|
314
|
+
*/
|
|
315
|
+
export function recordedPart(part) {
|
|
316
|
+
return part.kind === 'data-folder' ? { ...part, name: posixPath(part.name) } : part;
|
|
317
|
+
}
|
|
307
318
|
/** A need as a part of a lease record. */
|
|
308
319
|
export function leasePart(need) {
|
|
309
320
|
return { kind: need.kind, name: need.name, ...(need.apps.length === 0 ? {} : { apps: [...need.apps] }), ...(need.count === undefined ? {} : { count: need.count }) };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-session.d.ts","sourceRoot":"","sources":["../../src/runner/run-session.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAkC,SAAS,EAAc,MAAM,uBAAuB,CAAA;AAGlG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAGxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAiC,MAAM,mBAAmB,CAAA;AACrH,OAAO,KAAK,EAAe,UAAU,EAAyB,MAAM,eAAe,CAAA;AAWnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"run-session.d.ts","sourceRoot":"","sources":["../../src/runner/run-session.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAkC,SAAS,EAAc,MAAM,uBAAuB,CAAA;AAGlG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAGxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,cAAc,EAAiC,MAAM,mBAAmB,CAAA;AACrH,OAAO,KAAK,EAAe,UAAU,EAAyB,MAAM,eAAe,CAAA;AAWnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAkChD,OAAO,KAAK,EAAE,cAAc,EAAqB,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAqCtF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,UAAU,CAAA;IACnB,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC9B,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,cAAc,CAAA;IAC9B,KAAK,EAAE,QAAQ,CAAA;IACf,oFAAoF;IACpF,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,wIAAwI;IACxI,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;KAAE,CAAA;IACnF,sHAAsH;IACtH,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,aAAa,CAAC,EAAE,OAAO,gBAAgB,EAAE,aAAa,CAAA;CACvD,CAAA;AAgGD;;;;;;GAMG;AACH,qBAAa,UAAU;;gBAmGT,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,iBAAiB;IAkHjI,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC;CAg9ChC"}
|
|
@@ -11,6 +11,7 @@ import { defaultAppPixelRules, PixelCapturePolicy } from "../media/policy.js";
|
|
|
11
11
|
import { elapsedMs, monotonicClock } from "../protocol/deadline.js";
|
|
12
12
|
import { retestEventSchema } from "../protocol/events.js";
|
|
13
13
|
import { formatSessionId } from "../protocol/evidence.js";
|
|
14
|
+
import { withoutVariables } from "../shared/environment.js";
|
|
14
15
|
import { sha256Hex } from "../shared/sha256.js";
|
|
15
16
|
import { attemptEnding, canonicalJson, withFinalBundle } from "../protocol/execution.js";
|
|
16
17
|
import { errorMessage, failure, withAlso } from "../protocol/failures.js";
|
|
@@ -19,6 +20,7 @@ import { appLogFile, browserLogFile, childLogFile, stateFile, targetBrowserLogFi
|
|
|
19
20
|
import { originOf, withoutCredentials } from "../protocol/url.js";
|
|
20
21
|
import { variantKey } from "../protocol/variant.js";
|
|
21
22
|
import { relativePosixPath } from "../shared/posix-path.js";
|
|
23
|
+
import { runnerStrayProblems } from "../shared/process-system.js";
|
|
22
24
|
import { applyRetention, recordingArtifactReferences, ArtifactSequences, downloadsFolder, inventoryArtifacts, planRetention } from "../store/artifacts.js";
|
|
23
25
|
import { retestVersion } from "../version.js";
|
|
24
26
|
import { AppServers } from "./app-servers.js";
|
|
@@ -51,7 +53,7 @@ import { attemptKey, phasesOf, scheduleRun } from "./schedule.js";
|
|
|
51
53
|
import { AttemptScreenshots, pictureApp, ScreenshotBaselines } from "./screenshot-baselines.js";
|
|
52
54
|
import { SecretFiller, secretValuesProblem, secretVariables } from "./secrets.js";
|
|
53
55
|
import { emptySelectionFailure, selectionProblem, testSelected, variantSelected } from "./selection.js";
|
|
54
|
-
import { acquireResources, hostResources, leasePart, partFree, resourceNeeds, sessionResource } from "./resources.js";
|
|
56
|
+
import { acquireResources, hostResources, leasePart, partFree, recordedPart, resourceNeeds, sessionResource } from "./resources.js";
|
|
55
57
|
import { sessionOptionsProblem } from "./sessions.js";
|
|
56
58
|
import { searchSetups } from "./setup-search.js";
|
|
57
59
|
import { skippedCheckFailures } from "./skipped-checks.js";
|
|
@@ -285,7 +287,7 @@ export class RunSession {
|
|
|
285
287
|
? new RunMedia({
|
|
286
288
|
location: startMedia !== undefined && discoverMedia === undefined && options.media !== undefined ? options.media : signal => (discoverMedia ?? discoverMediaLocation)(options.media, process.env, timeouts.setup, signal),
|
|
287
289
|
start: startMedia ?? startMediaProcess,
|
|
288
|
-
env:
|
|
290
|
+
env: withoutVariables(process.env, this.#hiddenVariables),
|
|
289
291
|
signal: options.signal,
|
|
290
292
|
emit: (body) => void this.#events.emit(body),
|
|
291
293
|
timeouts: { start: timeouts.setup, close: timeouts.cleanup, leftovers: timeouts.cleanup },
|
|
@@ -702,7 +704,7 @@ export class RunSession {
|
|
|
702
704
|
return;
|
|
703
705
|
}
|
|
704
706
|
if (stage.kind === 'resources') {
|
|
705
|
-
const resources = stage.needs.map((need) => ({ ...leasePart(need), name: redact(need.name) }));
|
|
707
|
+
const resources = stage.needs.map((need) => recordedPart({ ...leasePart(need), name: redact(need.name) }));
|
|
706
708
|
const holders = { ...(stage.heldBy.length === 0 ? {} : { heldBy: [...stage.heldBy] }), ...(stage.heldElsewhere === 0 ? {} : { heldElsewhere: stage.heldElsewhere }) };
|
|
707
709
|
this.#emitFor(described, { type: 'resource.acquired', testId, attemptId, resources, waitedMs: stage.waitedMs, ...holders });
|
|
708
710
|
return;
|
|
@@ -721,7 +723,7 @@ export class RunSession {
|
|
|
721
723
|
const { testId, attemptId } = described;
|
|
722
724
|
this.#emitFor(described, { type: 'lease.taken', testId, attemptId, lease: record });
|
|
723
725
|
lease.onExpired((expiry) => {
|
|
724
|
-
const parts = (needs) => needs.map((need) => ({ ...leasePart(need), name: redact(need.name) }));
|
|
726
|
+
const parts = (needs) => needs.map((need) => recordedPart({ ...leasePart(need), name: redact(need.name) }));
|
|
725
727
|
this.#emitFor(described, { type: 'lease.expired', testId, attemptId, lease: record, held: parts(expiry.held), released: parts(expiry.released) });
|
|
726
728
|
});
|
|
727
729
|
}
|
|
@@ -1776,6 +1778,9 @@ export class RunSession {
|
|
|
1776
1778
|
// reporter failure is part of the event record as well as result.json.
|
|
1777
1779
|
async #finish(files) {
|
|
1778
1780
|
await this.#events.flush();
|
|
1781
|
+
// On Windows, what Retest's own job still holds that no launch's job does, by pid and name; empty elsewhere.
|
|
1782
|
+
for (const problem of await runnerStrayProblems())
|
|
1783
|
+
this.#events.reportFailure(failure('cleanup_failed', problem));
|
|
1779
1784
|
const browsers = this.#browsers.started.map((started) => started.info);
|
|
1780
1785
|
const evidence = this.#recordsApps ? { evidenceStatus: runEvidence(files.flatMap((file) => file.tests), [], this.#recording.required) } : {};
|
|
1781
1786
|
const facts = {
|
package/dist/runner/run.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/runner/run.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,KAAK,EAAiB,cAAc,EAAE,aAAa,EAAE,UAAU,EAAa,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/runner/run.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,KAAK,EAAiB,cAAc,EAAE,aAAa,EAAE,UAAU,EAAa,MAAM,eAAe,CAAA;AAwBxG,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,SAAS,QAAQ,EAAE,EAC9B,MAAM,GAAE,aAA6B,EACrC,cAAc,GAAE,cAAqC,GACpD,OAAO,CAAC,SAAS,CAAC,CAOpB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAwBlF"}
|
package/dist/runner/run.js
CHANGED
|
@@ -5,6 +5,7 @@ import { judgeVariables } from "../evaluation/judges.js";
|
|
|
5
5
|
import { withAlso } from "../protocol/failures.js";
|
|
6
6
|
import { variantKey } from "../protocol/variant.js";
|
|
7
7
|
import { relativePosixPath } from "../shared/posix-path.js";
|
|
8
|
+
import { asOneRun } from "../shared/process-system.js";
|
|
8
9
|
import { RunStore } from "../store/run-store.js";
|
|
9
10
|
import { loadTests, missingFileFailure } from "./load-tests.js";
|
|
10
11
|
import { collectedTest, planTests } from "./plan.js";
|
|
@@ -29,7 +30,7 @@ export { RunFolderError } from "../store/run-store.js";
|
|
|
29
30
|
export async function runFiles(options, reporters, launch = launchBrowser, findExecutable = findTargetExecutable) {
|
|
30
31
|
const store = RunStore.create(options.outputDir);
|
|
31
32
|
try {
|
|
32
|
-
return await new RunSession({ options, reporters, launch, findExecutable, store }).run();
|
|
33
|
+
return await asOneRun(() => new RunSession({ options, reporters, launch, findExecutable, store }).run());
|
|
33
34
|
}
|
|
34
35
|
finally {
|
|
35
36
|
store.close();
|
|
@@ -35,14 +35,16 @@ export type TargetDriver = WebTargetDriver | {
|
|
|
35
35
|
* The driver a target needs. Chromium, Chrome and Edge run on Retest's Chromium driver, and an Electron app on the
|
|
36
36
|
* same driver over the app's own debugging pipe. Firefox runs on Retest's Firefox driver over WebDriver BiDi, and WebKit
|
|
37
37
|
* on Retest's WebKit driver over the inspector pipe. An iOS simulator or macOS target is accepted in the config and
|
|
38
|
-
* refused here, naming its app and target, when its driver is not given
|
|
39
|
-
* browser stands in for an Electron app.
|
|
38
|
+
* refused here, naming its app and target, when its driver is not given, and on Windows, where its drivers do not run, by
|
|
39
|
+
* its key. No other driver runs it in its place, and no browser stands in for an Electron app. `platform` is the host
|
|
40
|
+
* the target would run on, this machine unless a caller asks as another would.
|
|
40
41
|
*
|
|
41
42
|
* @example targetDriver('web', { name: 'mac', platform: 'macos', appPath: '/Applications/Tasks.app' }).ok // false
|
|
42
43
|
*/
|
|
43
44
|
export declare function targetDriver(app: string, target: LoadedTarget): WebTargetDriver;
|
|
44
45
|
export declare function targetDriver(app: string, target: LoadedTarget, options: {
|
|
45
46
|
native: true;
|
|
47
|
+
platform?: NodeJS.Platform;
|
|
46
48
|
}): TargetDriver;
|
|
47
49
|
/**
|
|
48
50
|
* Why an attempt cannot run, decided before anything starts for it, or undefined when it can: a target with no
|
|
@@ -55,5 +57,6 @@ export declare function targetDriver(app: string, target: LoadedTarget, options:
|
|
|
55
57
|
*/
|
|
56
58
|
export declare function attemptRefusal(targets: Variant, apps: ReadonlyMap<string, LoadedApp>, test?: {
|
|
57
59
|
setup?: boolean;
|
|
60
|
+
platform?: NodeJS.Platform;
|
|
58
61
|
}): Failure | undefined;
|
|
59
62
|
//# sourceMappingURL=target-drivers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"target-drivers.d.ts","sourceRoot":"","sources":["../../src/runner/target-drivers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC3K,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAErD;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,GAC5D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC1D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAEnC,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAUxH
|
|
1
|
+
{"version":3,"file":"target-drivers.d.ts","sourceRoot":"","sources":["../../src/runner/target-drivers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAC3K,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAErD;;;;GAIG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,GAC5D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,GAC1D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAEnC,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAA;CAAE,CAAA;AAUxH;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,eAAe,CAAA;AAChF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAA;CAAE,GAAG,YAAY,CAAA;AAUpI;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAA;CAAO,GAAG,OAAO,GAAG,SAAS,CAoBtK"}
|
|
@@ -6,6 +6,8 @@ const driverNames = {
|
|
|
6
6
|
macos: 'macOS apps',
|
|
7
7
|
};
|
|
8
8
|
export function targetDriver(app, target, options) {
|
|
9
|
+
if ('platform' in target && options?.native === true && (options.platform ?? process.platform) === 'win32')
|
|
10
|
+
return nativeOnWindows(app, target);
|
|
9
11
|
if ('platform' in target)
|
|
10
12
|
return options?.native === true ? { ok: true, driver: target.platform, target } : noDriver(app, target.name, target.platform);
|
|
11
13
|
if (target.browser === 'firefox')
|
|
@@ -33,7 +35,7 @@ export function attemptRefusal(targets, apps, test = {}) {
|
|
|
33
35
|
const target = apps.get(app)?.targets.get(name);
|
|
34
36
|
if (target === undefined)
|
|
35
37
|
continue;
|
|
36
|
-
const driver = targetDriver(app, target, { native: true });
|
|
38
|
+
const driver = targetDriver(app, target, { native: true, ...(test.platform === undefined ? {} : { platform: test.platform }) });
|
|
37
39
|
if (!driver.ok)
|
|
38
40
|
return driver.failure;
|
|
39
41
|
if (test.setup === true) {
|
|
@@ -68,6 +70,13 @@ function setupRefusal(app, target) {
|
|
|
68
70
|
const reason = isElectron ? `the Electron app ${app} keeps its own storage. Set userDataDir to keep the app's data from one launch to the next` : `the native app ${app} has no browser storage`;
|
|
69
71
|
return { class: 'unsupported', message: `Not run: a setup saves a sign-in state from its app's browser storage, and ${reason}.`, details: { app, target: target.name } };
|
|
70
72
|
}
|
|
73
|
+
// The macOS and iOS drivers run on a Mac, as WebKit's does: on Windows a native target is refused at its own setup, before
|
|
74
|
+
// anything starts for it, and the rest of the config runs. Its key is named as the config writes it.
|
|
75
|
+
function nativeOnWindows(app, target) {
|
|
76
|
+
const key = target.name === target.platform ? `apps.${app}` : `apps.${app}.targets.${target.name}`;
|
|
77
|
+
const kind = target.platform === 'macos' ? 'a macOS app, which Retest runs only on a Mac' : 'an iOS simulator app, which Retest runs only on a Mac with Xcode';
|
|
78
|
+
return { ok: false, failure: { class: 'setup_failed', message: `${key} is ${kind}.`, details: { app, target: target.name, driver: target.platform } } };
|
|
79
|
+
}
|
|
71
80
|
function noDriver(app, target, driver) {
|
|
72
81
|
const message = `Retest has no driver for ${driverNames[driver]} yet, so it cannot start the target ${target} of the app ${app}.`;
|
|
73
82
|
return { ok: false, failure: { class: 'setup_failed', message, details: { app, target, driver } } };
|