@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.
Files changed (218) hide show
  1. package/README.md +6 -5
  2. package/dist/api/source-location.d.ts.map +1 -1
  3. package/dist/api/source-location.js +3 -1
  4. package/dist/browser/builds.d.ts +35 -6
  5. package/dist/browser/builds.d.ts.map +1 -1
  6. package/dist/browser/builds.js +115 -16
  7. package/dist/browser/cdp/transport.d.ts.map +1 -1
  8. package/dist/browser/cdp/transport.js +4 -4
  9. package/dist/browser/chromium-process.d.ts +23 -0
  10. package/dist/browser/chromium-process.d.ts.map +1 -1
  11. package/dist/browser/chromium-process.js +78 -13
  12. package/dist/browser/electron.d.ts +1 -1
  13. package/dist/browser/electron.d.ts.map +1 -1
  14. package/dist/browser/electron.js +6 -3
  15. package/dist/browser/executables.d.ts +16 -1
  16. package/dist/browser/executables.d.ts.map +1 -1
  17. package/dist/browser/executables.js +50 -18
  18. package/dist/browser/firefox/accessible-names.d.ts +5 -4
  19. package/dist/browser/firefox/accessible-names.d.ts.map +1 -1
  20. package/dist/browser/firefox/accessible-names.js +11 -6
  21. package/dist/browser/firefox/bridge.js +3 -2
  22. package/dist/browser/firefox/executable.d.ts +14 -14
  23. package/dist/browser/firefox/executable.d.ts.map +1 -1
  24. package/dist/browser/firefox/executable.js +29 -21
  25. package/dist/browser/firefox/launch.js +1 -1
  26. package/dist/browser/firefox/navigation.d.ts +5 -2
  27. package/dist/browser/firefox/navigation.d.ts.map +1 -1
  28. package/dist/browser/firefox/navigation.js +48 -14
  29. package/dist/browser/firefox/orphans.d.ts.map +1 -1
  30. package/dist/browser/firefox/orphans.js +34 -2
  31. package/dist/browser/firefox/page.d.ts.map +1 -1
  32. package/dist/browser/firefox/page.js +113 -19
  33. package/dist/browser/firefox/process.d.ts +20 -1
  34. package/dist/browser/firefox/process.d.ts.map +1 -1
  35. package/dist/browser/firefox/process.js +124 -22
  36. package/dist/browser/firefox/profile.d.ts +5 -0
  37. package/dist/browser/firefox/profile.d.ts.map +1 -1
  38. package/dist/browser/firefox/profile.js +17 -1
  39. package/dist/browser/firefox/storage.d.ts.map +1 -1
  40. package/dist/browser/firefox/storage.js +4 -2
  41. package/dist/browser/firefox/subscriptions.d.ts +26 -0
  42. package/dist/browser/firefox/subscriptions.d.ts.map +1 -0
  43. package/dist/browser/firefox/subscriptions.js +10 -0
  44. package/dist/browser/launch.d.ts +6 -0
  45. package/dist/browser/launch.d.ts.map +1 -1
  46. package/dist/browser/launch.js +14 -1
  47. package/dist/cli/commands/doctor.js +3 -3
  48. package/dist/cli/commands/inspect.js +3 -2
  49. package/dist/cli/commands/install.d.ts.map +1 -1
  50. package/dist/cli/commands/install.js +26 -13
  51. package/dist/cli/commands/licences.d.ts +22 -0
  52. package/dist/cli/commands/licences.d.ts.map +1 -1
  53. package/dist/cli/commands/licences.js +62 -20
  54. package/dist/cli/commands/run.js +2 -2
  55. package/dist/cli/doctor/checks.js +4 -1
  56. package/dist/cli/inspect/diagnostics.js +3 -3
  57. package/dist/cli/inspect/test-timeline.js +10 -10
  58. package/dist/cli/install/doctor-rows.d.ts.map +1 -1
  59. package/dist/cli/install/doctor-rows.js +5 -4
  60. package/dist/cli/install/file-changes.d.ts +44 -0
  61. package/dist/cli/install/file-changes.d.ts.map +1 -0
  62. package/dist/cli/install/file-changes.js +81 -0
  63. package/dist/cli/install/install-archive.d.ts +3 -1
  64. package/dist/cli/install/install-archive.d.ts.map +1 -1
  65. package/dist/cli/install/install-archive.js +80 -33
  66. package/dist/cli/install/lock.d.ts.map +1 -1
  67. package/dist/cli/install/lock.js +108 -21
  68. package/dist/cli/install/media-doctor.d.ts.map +1 -1
  69. package/dist/cli/install/media-doctor.js +2 -1
  70. package/dist/cli/install/media-install.d.ts.map +1 -1
  71. package/dist/cli/install/media-install.js +38 -19
  72. package/dist/cli/install/media-pins.d.ts +7 -1
  73. package/dist/cli/install/media-pins.d.ts.map +1 -1
  74. package/dist/cli/install/media-pins.js +23 -12
  75. package/dist/cli/install/media-record.d.ts.map +1 -1
  76. package/dist/cli/install/media-record.js +10 -9
  77. package/dist/cli/install/media-report.d.ts +3 -1
  78. package/dist/cli/install/media-report.d.ts.map +1 -1
  79. package/dist/cli/install/media-report.js +7 -5
  80. package/dist/cli/install/media-tools.d.ts.map +1 -1
  81. package/dist/cli/install/media-tools.js +6 -4
  82. package/dist/cli/install/process-start.d.ts +3 -2
  83. package/dist/cli/install/process-start.d.ts.map +1 -1
  84. package/dist/cli/install/process-start.js +4 -0
  85. package/dist/cli/install/report.d.ts.map +1 -1
  86. package/dist/cli/install/report.js +11 -7
  87. package/dist/cli/install/supervisor-install.d.ts +33 -0
  88. package/dist/cli/install/supervisor-install.d.ts.map +1 -0
  89. package/dist/cli/install/supervisor-install.js +158 -0
  90. package/dist/cli/install/supervisor-locate.d.ts +47 -0
  91. package/dist/cli/install/supervisor-locate.d.ts.map +1 -0
  92. package/dist/cli/install/supervisor-locate.js +113 -0
  93. package/dist/cli/install/supervisor-pins.d.ts +21 -0
  94. package/dist/cli/install/supervisor-pins.d.ts.map +1 -0
  95. package/dist/cli/install/supervisor-pins.js +28 -0
  96. package/dist/cli/install/supervisor-record.d.ts +31 -0
  97. package/dist/cli/install/supervisor-record.d.ts.map +1 -0
  98. package/dist/cli/install/supervisor-record.js +104 -0
  99. package/dist/cli/install/supervisor-report.d.ts +11 -0
  100. package/dist/cli/install/supervisor-report.d.ts.map +1 -0
  101. package/dist/cli/install/supervisor-report.js +33 -0
  102. package/dist/cli/install/supervisor-tools.d.ts +43 -0
  103. package/dist/cli/install/supervisor-tools.d.ts.map +1 -0
  104. package/dist/cli/install/supervisor-tools.js +114 -0
  105. package/dist/cli/install/unpack.d.ts +23 -3
  106. package/dist/cli/install/unpack.d.ts.map +1 -1
  107. package/dist/cli/install/unpack.js +124 -10
  108. package/dist/cli/install/zip-member.d.ts +15 -0
  109. package/dist/cli/install/zip-member.d.ts.map +1 -0
  110. package/dist/cli/install/zip-member.js +77 -0
  111. package/dist/cli/interrupt.d.ts +1 -1
  112. package/dist/cli/interrupt.d.ts.map +1 -1
  113. package/dist/cli/interrupt.js +15 -10
  114. package/dist/cli/terminal.d.ts +9 -1
  115. package/dist/cli/terminal.d.ts.map +1 -1
  116. package/dist/cli/terminal.js +14 -0
  117. package/dist/config/define.d.ts +2 -2
  118. package/dist/config/define.js +2 -2
  119. package/dist/diagnostics/firefox-collector.d.ts +11 -10
  120. package/dist/diagnostics/firefox-collector.d.ts.map +1 -1
  121. package/dist/diagnostics/firefox-collector.js +16 -11
  122. package/dist/media/client.d.ts +2 -1
  123. package/dist/media/client.d.ts.map +1 -1
  124. package/dist/media/client.js +16 -5
  125. package/dist/media/host.d.ts +32 -0
  126. package/dist/media/host.d.ts.map +1 -0
  127. package/dist/media/host.js +50 -0
  128. package/dist/media/locate.d.ts.map +1 -1
  129. package/dist/media/locate.js +4 -3
  130. package/dist/native/processes.d.ts.map +1 -1
  131. package/dist/native/processes.js +7 -3
  132. package/dist/protocol/files.d.ts +1 -1
  133. package/dist/protocol/files.d.ts.map +1 -1
  134. package/dist/protocol/files.js +10 -5
  135. package/dist/reporters/commands.d.ts +12 -3
  136. package/dist/reporters/commands.d.ts.map +1 -1
  137. package/dist/reporters/commands.js +29 -11
  138. package/dist/reporters/human-card.js +1 -1
  139. package/dist/reporters/human.d.ts +3 -1
  140. package/dist/reporters/human.d.ts.map +1 -1
  141. package/dist/reporters/human.js +8 -8
  142. package/dist/reporters/style.d.ts +21 -2
  143. package/dist/reporters/style.d.ts.map +1 -1
  144. package/dist/reporters/style.js +11 -2
  145. package/dist/runner/app-server.d.ts +27 -7
  146. package/dist/runner/app-server.d.ts.map +1 -1
  147. package/dist/runner/app-server.js +74 -24
  148. package/dist/runner/child.js +6 -2
  149. package/dist/runner/media-leftovers.d.ts +7 -3
  150. package/dist/runner/media-leftovers.d.ts.map +1 -1
  151. package/dist/runner/media-leftovers.js +27 -6
  152. package/dist/runner/resources.d.ts +8 -0
  153. package/dist/runner/resources.d.ts.map +1 -1
  154. package/dist/runner/resources.js +12 -1
  155. package/dist/runner/run-session.d.ts.map +1 -1
  156. package/dist/runner/run-session.js +9 -4
  157. package/dist/runner/run.d.ts.map +1 -1
  158. package/dist/runner/run.js +2 -1
  159. package/dist/runner/target-drivers.d.ts +5 -2
  160. package/dist/runner/target-drivers.d.ts.map +1 -1
  161. package/dist/runner/target-drivers.js +10 -1
  162. package/dist/runner/test-file-process.d.ts +8 -0
  163. package/dist/runner/test-file-process.d.ts.map +1 -1
  164. package/dist/runner/test-file-process.js +26 -3
  165. package/dist/runner/test-pages.d.ts.map +1 -1
  166. package/dist/runner/test-pages.js +5 -2
  167. package/dist/shared/environment.d.ts +17 -0
  168. package/dist/shared/environment.d.ts.map +1 -0
  169. package/dist/shared/environment.js +27 -0
  170. package/dist/shared/metadata-process-worker.js +15 -7
  171. package/dist/shared/metadata-process.d.ts +5 -0
  172. package/dist/shared/metadata-process.d.ts.map +1 -1
  173. package/dist/shared/metadata-process.js +36 -6
  174. package/dist/shared/posix-path.d.ts +12 -0
  175. package/dist/shared/posix-path.d.ts.map +1 -1
  176. package/dist/shared/posix-path.js +16 -0
  177. package/dist/shared/private-file.d.ts +38 -0
  178. package/dist/shared/private-file.d.ts.map +1 -0
  179. package/dist/shared/private-file.js +132 -0
  180. package/dist/shared/process-ownership.d.ts +52 -2
  181. package/dist/shared/process-ownership.d.ts.map +1 -1
  182. package/dist/shared/process-ownership.js +104 -11
  183. package/dist/shared/process-supervisor.d.ts +309 -0
  184. package/dist/shared/process-supervisor.d.ts.map +1 -0
  185. package/dist/shared/process-supervisor.js +737 -0
  186. package/dist/shared/process-system.d.ts +113 -0
  187. package/dist/shared/process-system.d.ts.map +1 -0
  188. package/dist/shared/process-system.js +254 -0
  189. package/dist/shared/regular-file.d.ts +2 -2
  190. package/dist/shared/regular-file.d.ts.map +1 -1
  191. package/dist/shared/regular-file.js +19 -2
  192. package/dist/store/artifacts.d.ts +5 -4
  193. package/dist/store/artifacts.d.ts.map +1 -1
  194. package/dist/store/artifacts.js +18 -5
  195. package/dist/store/run-store.d.ts +5 -2
  196. package/dist/store/run-store.d.ts.map +1 -1
  197. package/dist/store/run-store.js +48 -4
  198. package/media/Cargo.lock +16 -0
  199. package/media/Cargo.toml +16 -1
  200. package/media/src/encoder.rs +56 -12
  201. package/media/src/frame.rs +7 -1
  202. package/media/src/main.rs +20 -2
  203. package/media/src/place.rs +230 -11
  204. package/media/src/process_ownership_windows.rs +1058 -0
  205. package/media/src/recording.rs +15 -7
  206. package/media/src/replies.rs +55 -0
  207. package/media/src/server.rs +72 -2
  208. package/media/src/store.rs +71 -2
  209. package/package.json +9 -4
  210. package/src/cli/install/media-notices.txt +465 -1
  211. package/supervisor/Cargo.lock +25 -0
  212. package/supervisor/Cargo.toml +22 -0
  213. package/supervisor/NOTICES +464 -0
  214. package/supervisor/build.rs +44 -0
  215. package/supervisor/src/command.rs +612 -0
  216. package/supervisor/src/jobs.rs +1650 -0
  217. package/supervisor/src/main.rs +71 -0
  218. 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 answered is `reused` and never stopped. One Retest `started` has
20
- * the process id of its shell. `stop` ends only processes whose launch ancestry and exact identity Retest recorded.
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`. When `ready` already answers, that server is reused. Otherwise
47
- * `command` runs in a shell, as its own process group, with its output in `logFile`, until `ready` answers. A
48
- * server that exits first, or never answers, is stopped and throws `AppServerError`. The parent's exit also ends
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
- /** Internal readiness seams; production uses the owned server and HTTP probe. */
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?: (url: string, timeoutMs: number) => Promise<boolean>;
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;AAiB7C,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;;;GAGG;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,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa3E;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAE,qBAA0B,GAAG,OAAO,CAAC,eAAe,CAAC,CA8BrJ;AAED,iFAAiF;AACjF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IACrE,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"}
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, within `timeoutMs`, whether a server answers there. Any HTTP answer counts, whatever its
31
- * status; a refused connection, a timeout or anything that is not HTTP does not.
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 probeReady('http://127.0.0.1:3000/health', 1000) // true
39
+ * @example await lookAtReady('http://127.0.0.1:3000/health', 1000) // 'answered'
34
40
  */
35
- export function probeReady(url, timeoutMs) {
41
+ export function lookAtReady(url, timeoutMs, signal) {
36
42
  const target = URL.parse(url);
37
43
  if (!isWebUrl(target))
38
- return Promise.resolve(false);
44
+ return Promise.resolve('unreached');
39
45
  const get = target.protocol === 'https:' ? httpsGet : httpGet;
40
46
  const { promise, resolve } = Promise.withResolvers();
41
- const request = get(target, { agent: false }, (response) => {
42
- resolve(true);
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
- const timer = setTimeout(() => request.destroy(), timeoutMs);
46
- request.on('error', () => resolve(false));
47
- request.on('close', () => resolve(false));
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
- * Makes an app's server ready within `timeoutMs`. When `ready` already answers, that server is reused. Otherwise
52
- * `command` runs in a shell, as its own process group, with its output in `logFile`, until `ready` answers. A
53
- * server that exits first, or never answers, is stopped and throws `AppServerError`. The parent's exit also ends
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 probe = dependencies.probe ?? probeReady;
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
- if (await probe(start.ready, smallestBudget(probeLimitMs, deadline.commandTimeoutMs))) {
67
- return { status: 'reused', durationMs: elapsedMs(startedAt), stop: async () => undefined };
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 probe(start.ready, smallestBudget(probeLimitMs, deadline.commandTimeoutMs))) {
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 = new OwnedProcessGroup(pid);
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 = new OwnedProcessGroup(this.#pid);
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 hidden = new Set(hiddenVariables);
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
- child = spawn(start.command, { shell: true, cwd: folder, env, detached: true, stdio: ['ignore', 'pipe', 'pipe'] });
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('SIGTERM');
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'));
@@ -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
- enableCompileCache();
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
- /** A Retest process, by pid and start time as `ps` prints it, so a pid used again by another process is never taken for it. */
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
- * shows its pid absent; undefined for a present legacy or mismatched start, or an unreadable host. Unknowns stay alone.
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":"AAWA,+HAA+H;AAC/H,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;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CASjF;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAEhE;AAuED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKhF"}
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
- * shows its pid absent; undefined for a present legacy or mismatched start, or an unreadable host. Unknowns stay alone.
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 found = (await readProcessTableAsync()).find(entry => entry.pid === mark.pid);
55
- if (found === undefined)
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 === found.startedAt.replace(/\s+/g, ' ') ? true : undefined;
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
- return (await processStartTimes([process.pid]))?.get(process.pid);
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":"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"}
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;AAUhF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAWxC,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,EAAE,SAAS,YAAY,EAA6D,CAAA;AAEjH;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,WAA+B,CAAA;AAE3D;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAElK,qKAAqK;AACrK,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACpC,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,EAAE,CAcjE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,CAAC,GAAG,MAAM,CAI1H;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU9C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,GAAG,eAAe,CAKjF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAiB5E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAKzF;AAED,uFAAuF;AACvF,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE7G,kIAAkI;AAClI,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAClI;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACtK;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAA;AAE/D;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAA;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IAC3C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;CACnE,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAA;AAE/J;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,CAmClH;AAED;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAA;CAAE,CAAA;AAE/E;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAQ1G;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAA;IACpE,QAAQ,CAAC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,OAAO,CAAA;IAC9D,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,2IAA2I;AAC3I,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,YAAY,EAAE,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAA;CAAE,CAAA;AAEhH,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,EAAE,CAE3E;AAED;;;;;GAKG;AACH,qBAAa,aAAa;;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;IACxC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAA;IACtB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAA;gBAU/B,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE;QAAE,OAAO,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;QAAC,SAAS,EAAE,SAAS,GAAG,SAAS,CAAC;QAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAA;KAAE;IAWrL,oHAAoH;IACpH,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,wCAAwC;IACxC,IAAI,MAAM,IAAI,WAAW,GAAG,SAAS,CAEpC;IAED,8EAA8E;IAC9E,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,WAAW;IAIrD,yFAAyF;IACzF,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI;IAK9D;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/C;;;;OAIG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA6C/C;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAEvD;AAED,0CAA0C;AAC1C,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,CAEvD"}
@@ -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;AAgChD,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;CA88ChC"}
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: Object.fromEntries(Object.entries(process.env).filter(([name]) => !this.#hiddenVariables.includes(name))),
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 = {
@@ -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;AAuBxG,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"}
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"}
@@ -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. No other driver runs it in its place, and no
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;;;;;;;;GAQG;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,CAAA;CAAE,GAAG,YAAY,CAAA;AASxG;;;;;;;;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,CAAA;CAAO,GAAG,OAAO,GAAG,SAAS,CAoB1I"}
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 } } };