@hominis/fireforge 0.19.2 → 0.19.3

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/CHANGELOG.md CHANGED
@@ -10,6 +10,11 @@
10
10
  ### Hardening
11
11
 
12
12
  - **`modified-file-missing-header` — standard Mozilla MPL-2.0 block headers with wrapped line breaks.** The upstream fallback scan required a contiguous `Mozilla Public License` substring in the first few lines, so files that follow Mozilla’s usual `/* … Mozilla Public` / ` * License, v. 2.0 … */` wrap (including after Emacs/vim directive blocks) were warned despite a valid notice. `containsUpstreamLicenseText` in `src/core/license-headers.ts` now normalizes common block-comment continuation prefixes before matching, so forks need not add SPDX solely to satisfy patch lint.
13
+ - **`fireforge test` — `--marionette-port` auto-forward matches toolkit mochitests and mixed suites.** Auto-forward of `--setpref=marionette.port=<n>` previously keyed off a path heuristic that missed `toolkit/content/tests/**` widget HTML tests (no `/mochitest/` segment), so the preflight could use the operator’s port while mach still defaulted to **2828**. Forwarding now runs whenever `--marionette-port` is set unless `--mach-arg` explicitly includes `--flavor=xpcshell` / `xpcshell-tests` (the pref is unused there). `isMarionetteFlavor` also treats `toolkit/content/tests/` paths as Marionette-relevant unless they sit under `/tests/xpcshell/`, for consistency with other callers of that helper.
14
+
15
+ ### Documentation
16
+
17
+ - **README — mochitest timeouts vs Marionette.** The Test harness section documents long idle timeouts (~370s, `TEST_END: TIMEOUT`) on fork custom chrome, `--marionette-port` behaviour with xpcshell flavor, and pointers to fork-side prefs and investigation (for example Hominis `AGENT_RULES.md`).
13
18
 
14
19
  ## 0.18.0
15
20
 
package/README.md CHANGED
@@ -638,6 +638,12 @@ xpcshell has a chrome-URI boundary that is worth knowing before writing assertio
638
638
 
639
639
  The two flags can be combined — `--with-tests --xpcshell` writes both harnesses.
640
640
 
641
+ ### Mochitest stalls and `--marionette-port`
642
+
643
+ When you pass `--marionette-port <n>`, FireForge uses that port for the stale-listener probe and for `--doctor`, and it forwards `--setpref=marionette.port=<n>` to `mach test` so the harness binds the same port. The only exception is an explicit `--mach-arg --flavor=xpcshell` (or `--flavor=xpcshell-tests`): that harness ignores the pref, so FireForge skips auto-forward and logs a short notice instead. Toolkit widget mochitests under `toolkit/content/tests/` (for example `test_*.html` next to `browser_*.js` suites) therefore stay aligned with the probe without duplicating `--mach-arg=--setpref=marionette.port=…`.
644
+
645
+ Some forks see mochitests end with **`TEST_END: TIMEOUT`** after on the order of **370 seconds** with **no harness output** — including runs where mozinfo reports `headless: false`, so the failure is not explained by SWGL headless alone. When tests wait on custom chrome (for example a fork tile shell that never sets a `_readyForTesting` gate), the hang is **engine-side**; use your fork’s test docs and `browser.toml` defaults (for Hominis-style trees, see the fork’s `AGENT_RULES.md` for `hominis.testing.*` prefs). Operationally: ensure Marionette port **2828** is free, pass **`--marionette-port`** when you use a non-default port (FireForge keeps preflight and mach consistent as above), and narrow the failure with `--doctor` or by splitting a lighter smoke test that does not depend on full chrome init.
646
+
641
647
  ### Stale-build preflight on `fireforge test`
642
648
 
643
649
  `fireforge test <path>` (without `--build`) now runs a preflight that diffs engine HEAD and the workdir against the last successful `fireforge build` (recorded at `.fireforge/last-build.json`). When packageable engine files have changed since that baseline, the command prints a single up-front warning naming the paths and pointing at `fireforge test --build`. This catches the class of failure where a newly scaffolded chrome resource or pref file is registered correctly but `obj-*/dist/` still holds the pre-edit bundle, so the test reads stale packaged artifacts and errors out with a cryptic `NS_ERROR_FILE_NOT_FOUND` inside xpcshell / mach test. The preflight is warn-only — a fork that rebuilt out-of-band (direct `./mach build`, IDE plugin, separate CI stage) is not blocked. Passing `--build` skips the preflight because the rebuild just refreshed the bundle.
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { prepareBuildEnvironment } from '../core/build-prepare.js';
4
4
  import { getProjectPaths, loadConfig } from '../core/config.js';
5
5
  import { buildArtifactMismatchMessage, buildUI, hasBuildArtifacts, hasRunnableBundle, testWithOutput, } from '../core/mach.js';
6
- import { assertMarionettePortAvailable, extractForwardedMarionettePort, isMarionetteFlavor, } from '../core/marionette-port.js';
6
+ import { assertMarionettePortAvailable, extractForwardedMarionettePort, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
7
7
  import { formatMarionettePreflightLine, reportMarionettePreflight, runMarionettePreflight, } from '../core/marionette-preflight.js';
8
8
  import { checkStaleBuildForTest, formatStaleBuildWarning } from '../core/test-stale-check.js';
9
9
  import { operatorAlreadySetAppPath, resolveXpcshellAppdirArg, } from '../core/xpcshell-appdir.js';
@@ -326,21 +326,22 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
326
326
  //
327
327
  // Skip forwarding when the operator already supplied an equivalent arg
328
328
  // via `--mach-arg` — duplicates would be confusing without changing
329
- // semantics. Skip with a notice for clearly-non-marionette flavours
330
- // (xpcshell, or paths that don't look browser-chrome/mochitest) so the
331
- // operator knows the preflight took the override but mach was not
332
- // auto-configured. Same escape valve applies: any mach arg can still
333
- // be supplied via `--mach-arg`.
329
+ // semantics. Skip when mach args explicitly request `--flavor=xpcshell`
330
+ // (or `xpcshell-tests`): the preflight still honours `--marionette-port`,
331
+ // but mach does not use the marionette.port pref on that harness. Any
332
+ // other arg shape still forwards so toolkit widget paths and mixed suites
333
+ // stay aligned with the probe without duplicate `--mach-arg` flags.
334
334
  if (options.marionettePort !== undefined) {
335
335
  const operatorAlreadyForwarded = forwardedPort !== undefined;
336
+ const machArgs = options.machArg ?? [];
336
337
  if (operatorAlreadyForwarded) {
337
338
  info(`--marionette-port=${options.marionettePort} set, but the same port is already forwarded via --mach-arg; skipping auto-forward.`);
338
339
  }
339
- else if (isMarionetteFlavor(normalizedPaths, options.machArg ?? [])) {
340
+ else if (shouldAutoForwardMarionettePortToMach(machArgs)) {
340
341
  extraArgs.push(`--setpref=marionette.port=${options.marionettePort}`);
341
342
  }
342
343
  else {
343
- info(`--marionette-port=${options.marionettePort} applied to the preflight probe, but the test paths do not look browser-chrome/mochitest — mach is not auto-configured. Pass --mach-arg --setpref=marionette.port=${options.marionettePort} explicitly if mach should also use this port.`);
344
+ info(`--marionette-port=${options.marionettePort} applied to the preflight probe, but --flavor=xpcshell is set — mach is not auto-configured with --setpref=marionette.port (xpcshell ignores that pref). Pass --mach-arg --setpref=marionette.port=${options.marionettePort} explicitly if you still need mach to see the port.`);
344
345
  }
345
346
  }
346
347
  // xpcshell appdir auto-injection — see src/core/xpcshell-appdir.ts for the
@@ -408,7 +409,7 @@ export function registerTest(program, { getProjectRoot, withErrorHandling }) {
408
409
  acc.push(value);
409
410
  return acc;
410
411
  }, [])
411
- .option('--marionette-port <port>', 'Override the Marionette control port (default 2828) for the stale-browser probe, the --doctor preflight, and the auto-forwarded --setpref=marionette.port=<n> arg passed to mach. Use this when a stale process holds 2828 or a CI runner reserves a different port.', (raw) => {
412
+ .option('--marionette-port <port>', 'Override the Marionette control port (default 2828) for the stale-browser probe, the --doctor preflight, and (unless --mach-arg includes --flavor=xpcshell) the auto-forwarded --setpref=marionette.port=<n> passed to mach. Use this when a stale process holds 2828 or a CI runner reserves a different port.', (raw) => {
412
413
  const n = Number.parseInt(raw, 10);
413
414
  if (!Number.isFinite(n) || n < 1 || n > 65535) {
414
415
  throw new GeneralError(`--marionette-port must be an integer in 1..65535 (got "${raw}")`);
@@ -64,13 +64,31 @@ export declare function assertMarionettePortAvailable(port?: number, options?: {
64
64
  * `undefined`.
65
65
  */
66
66
  export declare function extractForwardedMarionettePort(machArgs: string[]): number | undefined;
67
+ /**
68
+ * True when forwarded mach args explicitly select the xpcshell harness.
69
+ * Used so `--marionette-port` auto-forward skips `--setpref=marionette.port`
70
+ * for runs where the pref is ignored anyway.
71
+ */
72
+ export declare function hasExplicitXpcshellFlavor(machArgs: string[]): boolean;
73
+ /**
74
+ * Whether `fireforge test` should append `--setpref=marionette.port=<n>` when
75
+ * the operator passed `--marionette-port`. Forwards for every harness except
76
+ * an explicit `--flavor=xpcshell` / `xpcshell-tests` (toolkit widget mochitests
77
+ * under `toolkit/content/tests/` do not match the older path-only heuristic
78
+ * but still launch a Marionette-driven browser).
79
+ */
80
+ export declare function shouldAutoForwardMarionettePortToMach(machArgs: string[]): boolean;
67
81
  /**
68
82
  * Heuristic: do the test paths or forwarded mach args indicate a flavour
69
83
  * that actually launches a Marionette-driven browser? Browser-chrome and
70
- * mochitest do; xpcshell does not. Used to decide whether to auto-forward
71
- * `--setpref=marionette.port=<n>` to mach when the operator passed
72
- * `--marionette-port`. A no-paths invocation (the default "run all tests"
73
- * shape) is treated as marionette-relevant since it includes browser-chrome.
84
+ * mochitest do; xpcshell does not. A no-paths invocation (the default "run
85
+ * all tests" shape) is treated as marionette-relevant since it includes
86
+ * browser-chrome.
87
+ *
88
+ * Note: `fireforge test` auto-forward of `--marionette-port` to mach uses
89
+ * {@link shouldAutoForwardMarionettePortToMach} (mach-arg flavor gate) rather
90
+ * than this function alone, so toolkit paths without `/mochitest/` still get
91
+ * the pref when appropriate.
74
92
  *
75
93
  * @param testPaths - Engine-relative paths after `stripEnginePrefix`.
76
94
  * @param machArgs - Forwarded mach args (post-`--mach-arg`).
@@ -260,23 +260,47 @@ export function extractForwardedMarionettePort(machArgs) {
260
260
  }
261
261
  return undefined;
262
262
  }
263
+ /**
264
+ * True when forwarded mach args explicitly select the xpcshell harness.
265
+ * Used so `--marionette-port` auto-forward skips `--setpref=marionette.port`
266
+ * for runs where the pref is ignored anyway.
267
+ */
268
+ export function hasExplicitXpcshellFlavor(machArgs) {
269
+ for (const arg of machArgs) {
270
+ if (/^--flavor=xpcshell\b/.test(arg) || arg === '--flavor=xpcshell-tests')
271
+ return true;
272
+ }
273
+ return false;
274
+ }
275
+ /**
276
+ * Whether `fireforge test` should append `--setpref=marionette.port=<n>` when
277
+ * the operator passed `--marionette-port`. Forwards for every harness except
278
+ * an explicit `--flavor=xpcshell` / `xpcshell-tests` (toolkit widget mochitests
279
+ * under `toolkit/content/tests/` do not match the older path-only heuristic
280
+ * but still launch a Marionette-driven browser).
281
+ */
282
+ export function shouldAutoForwardMarionettePortToMach(machArgs) {
283
+ return !hasExplicitXpcshellFlavor(machArgs);
284
+ }
263
285
  /**
264
286
  * Heuristic: do the test paths or forwarded mach args indicate a flavour
265
287
  * that actually launches a Marionette-driven browser? Browser-chrome and
266
- * mochitest do; xpcshell does not. Used to decide whether to auto-forward
267
- * `--setpref=marionette.port=<n>` to mach when the operator passed
268
- * `--marionette-port`. A no-paths invocation (the default "run all tests"
269
- * shape) is treated as marionette-relevant since it includes browser-chrome.
288
+ * mochitest do; xpcshell does not. A no-paths invocation (the default "run
289
+ * all tests" shape) is treated as marionette-relevant since it includes
290
+ * browser-chrome.
291
+ *
292
+ * Note: `fireforge test` auto-forward of `--marionette-port` to mach uses
293
+ * {@link shouldAutoForwardMarionettePortToMach} (mach-arg flavor gate) rather
294
+ * than this function alone, so toolkit paths without `/mochitest/` still get
295
+ * the pref when appropriate.
270
296
  *
271
297
  * @param testPaths - Engine-relative paths after `stripEnginePrefix`.
272
298
  * @param machArgs - Forwarded mach args (post-`--mach-arg`).
273
299
  * @returns `true` when the run is likely to bind a Marionette listener.
274
300
  */
275
301
  export function isMarionetteFlavor(testPaths, machArgs) {
276
- for (const arg of machArgs) {
277
- if (/^--flavor=xpcshell\b/.test(arg) || arg === '--flavor=xpcshell-tests')
278
- return false;
279
- }
302
+ if (hasExplicitXpcshellFlavor(machArgs))
303
+ return false;
280
304
  for (const arg of machArgs) {
281
305
  if (/^--flavor=(browser-chrome|mochitest|chrome|a11y)\b/.test(arg))
282
306
  return true;
@@ -291,6 +315,9 @@ export function isMarionetteFlavor(testPaths, machArgs) {
291
315
  return true;
292
316
  if (path.includes('/browser-chrome/') || path.startsWith('browser-chrome/'))
293
317
  return true;
318
+ if (path.includes('toolkit/content/tests/') && !path.includes('/tests/xpcshell/')) {
319
+ return true;
320
+ }
294
321
  }
295
322
  return false;
296
323
  }
@@ -319,9 +319,10 @@ export interface TestOptions {
319
319
  /**
320
320
  * Override the Marionette control port (default 2828) used by the
321
321
  * stale-browser probe, the `--doctor` preflight, and the auto-forwarded
322
- * `--setpref=marionette.port=<n>` arg passed to mach. Set this when a
323
- * stale process holds the default port and `kill` is not an option, or
324
- * when a CI runner reserves a different port for parallel test runs.
322
+ * `--setpref=marionette.port=<n>` arg passed to mach (omitted when mach
323
+ * args explicitly set `--flavor=xpcshell` / `xpcshell-tests`). Set this
324
+ * when a stale process holds the default port and `kill` is not an option,
325
+ * or when a CI runner reserves a different port for parallel test runs.
325
326
  */
326
327
  marionettePort?: number;
327
328
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hominis/fireforge",
3
- "version": "0.19.2",
3
+ "version": "0.19.3",
4
4
  "description": "FireForge — a build tool for customizing Firefox",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",