@hominis/fireforge 0.35.0 → 0.37.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/CHANGELOG.md +42 -0
- package/README.md +34 -9
- package/dist/bin/fireforge.js +21 -6
- package/dist/src/cli.js +22 -5
- package/dist/src/commands/bootstrap-checks.js +0 -1
- package/dist/src/commands/bootstrap.d.ts +2 -1
- package/dist/src/commands/bootstrap.js +16 -7
- package/dist/src/commands/build.js +5 -4
- package/dist/src/commands/config.js +30 -10
- package/dist/src/commands/discard.js +18 -3
- package/dist/src/commands/doctor/post-rebase-audit.js +0 -1
- package/dist/src/commands/doctor-check-core.js +0 -1
- package/dist/src/commands/doctor-external-toolchains.d.ts +3 -0
- package/dist/src/commands/doctor-external-toolchains.js +54 -0
- package/dist/src/commands/doctor-furnace-jar.js +0 -1
- package/dist/src/commands/doctor-furnace-manifest-sync.js +0 -1
- package/dist/src/commands/doctor-furnace.js +2 -2
- package/dist/src/commands/doctor-orphaned-harness.d.ts +52 -0
- package/dist/src/commands/doctor-orphaned-harness.js +132 -0
- package/dist/src/commands/doctor-working-tree.js +0 -1
- package/dist/src/commands/doctor.js +6 -1
- package/dist/src/commands/download.js +28 -13
- package/dist/src/commands/export-all.js +5 -3
- package/dist/src/commands/export-flow.js +12 -7
- package/dist/src/commands/export-placement-gate.js +0 -1
- package/dist/src/commands/export-placement-policy.js +0 -1
- package/dist/src/commands/export-shared.d.ts +5 -1
- package/dist/src/commands/export-shared.js +18 -6
- package/dist/src/commands/export.js +22 -10
- package/dist/src/commands/furnace/apply.js +22 -6
- package/dist/src/commands/furnace/chrome-doc-remove.js +0 -1
- package/dist/src/commands/furnace/chrome-doc-templates.js +0 -1
- package/dist/src/commands/furnace/chrome-doc-tests.js +0 -1
- package/dist/src/commands/furnace/chrome-doc.js +0 -1
- package/dist/src/commands/furnace/create-browser-test.js +0 -1
- package/dist/src/commands/furnace/create-dry-run.js +0 -1
- package/dist/src/commands/furnace/create-features.js +0 -1
- package/dist/src/commands/furnace/create-mochikit.js +0 -1
- package/dist/src/commands/furnace/create-readback.js +0 -1
- package/dist/src/commands/furnace/create-templates.js +0 -1
- package/dist/src/commands/furnace/create-validation.js +0 -1
- package/dist/src/commands/furnace/create-xpcshell.js +0 -1
- package/dist/src/commands/furnace/create.js +0 -1
- package/dist/src/commands/furnace/deploy.js +7 -83
- package/dist/src/commands/furnace/diff.js +0 -1
- package/dist/src/commands/furnace/index.js +59 -25
- package/dist/src/commands/furnace/init.js +0 -1
- package/dist/src/commands/furnace/list.js +0 -1
- package/dist/src/commands/furnace/override.js +0 -1
- package/dist/src/commands/furnace/preview.js +3 -3
- package/dist/src/commands/furnace/refresh.js +0 -1
- package/dist/src/commands/furnace/remove-state.d.ts +15 -0
- package/dist/src/commands/furnace/remove-state.js +47 -1
- package/dist/src/commands/furnace/remove.js +3 -9
- package/dist/src/commands/furnace/rename-browser-test.js +1 -5
- package/dist/src/commands/furnace/rename-helpers.d.ts +6 -0
- package/dist/src/commands/furnace/rename-helpers.js +30 -1
- package/dist/src/commands/furnace/rename-xpcshell.js +7 -6
- package/dist/src/commands/furnace/rename.js +15 -36
- package/dist/src/commands/furnace/scan.js +0 -1
- package/dist/src/commands/furnace/status.js +0 -1
- package/dist/src/commands/furnace/sync.js +76 -4
- package/dist/src/commands/furnace/validate.js +0 -1
- package/dist/src/commands/furnace/validation-output.js +0 -1
- package/dist/src/commands/import.js +35 -9
- package/dist/src/commands/lint-per-patch.js +19 -7
- package/dist/src/commands/lint.js +13 -3
- package/dist/src/commands/manifest.js +1 -2
- package/dist/src/commands/package.js +2 -1
- package/dist/src/commands/patch/compact.js +1 -1
- package/dist/src/commands/patch/delete.js +1 -1
- package/dist/src/commands/patch/index.js +1 -1
- package/dist/src/commands/patch/lint-ignore.js +21 -6
- package/dist/src/commands/patch/move-files.js +1 -1
- package/dist/src/commands/patch/patch-context.js +0 -1
- package/dist/src/commands/patch/rename.js +4 -4
- package/dist/src/commands/patch/reorder.js +3 -4
- package/dist/src/commands/patch/split-plan.js +2 -1
- package/dist/src/commands/patch/split.js +4 -4
- package/dist/src/commands/patch/staged-dependency.d.ts +1 -1
- package/dist/src/commands/patch/staged-dependency.js +114 -52
- package/dist/src/commands/patch/tier.js +18 -5
- package/dist/src/commands/re-export-bulk-scan.js +0 -1
- package/dist/src/commands/re-export-files.js +5 -1
- package/dist/src/commands/re-export-options.js +0 -1
- package/dist/src/commands/re-export-scan.js +0 -1
- package/dist/src/commands/re-export.js +14 -4
- package/dist/src/commands/rebase/abort.js +2 -9
- package/dist/src/commands/rebase/confirm.js +0 -1
- package/dist/src/commands/rebase/conflict-summary.js +0 -1
- package/dist/src/commands/rebase/continue.js +0 -1
- package/dist/src/commands/rebase/index.d.ts +1 -1
- package/dist/src/commands/rebase/index.js +10 -15
- package/dist/src/commands/rebase/patch-loop.js +38 -7
- package/dist/src/commands/rebase/summary.js +2 -3
- package/dist/src/commands/rebase.js +0 -1
- package/dist/src/commands/register.js +1 -1
- package/dist/src/commands/reset.js +16 -9
- package/dist/src/commands/resolve.js +2 -2
- package/dist/src/commands/run.js +4 -4
- package/dist/src/commands/setup-support.js +0 -1
- package/dist/src/commands/setup.js +1 -2
- package/dist/src/commands/source.js +1 -2
- package/dist/src/commands/status-output.js +1 -12
- package/dist/src/commands/status.js +147 -133
- package/dist/src/commands/test-appdir.js +0 -1
- package/dist/src/commands/test-diagnose.js +38 -3
- package/dist/src/commands/test-modes.d.ts +15 -0
- package/dist/src/commands/test-modes.js +61 -0
- package/dist/src/commands/test-register.d.ts +4 -0
- package/dist/src/commands/test-register.js +64 -0
- package/dist/src/commands/test-run.d.ts +23 -7
- package/dist/src/commands/test-run.js +15 -14
- package/dist/src/commands/test.d.ts +0 -4
- package/dist/src/commands/test.js +147 -96
- package/dist/src/commands/token-coverage.js +0 -1
- package/dist/src/commands/token.js +1 -2
- package/dist/src/commands/typecheck.js +1 -1
- package/dist/src/commands/verify.js +3 -9
- package/dist/src/commands/watch.js +5 -1
- package/dist/src/commands/wire.js +1 -1
- package/dist/src/core/ast-utils.js +0 -1
- package/dist/src/core/brand-validation.js +0 -1
- package/dist/src/core/branding.js +0 -1
- package/dist/src/core/browser-wire.js +0 -1
- package/dist/src/core/build-audit-platform.js +0 -1
- package/dist/src/core/build-audit-registration.js +0 -1
- package/dist/src/core/build-audit-resolve.js +0 -1
- package/dist/src/core/build-audit-transforms.js +0 -1
- package/dist/src/core/build-audit.js +0 -1
- package/dist/src/core/build-baseline-types.d.ts +24 -0
- package/dist/src/core/build-baseline-types.js +0 -1
- package/dist/src/core/build-baseline.d.ts +5 -2
- package/dist/src/core/build-baseline.js +5 -2
- package/dist/src/core/build-prepare.js +2 -2
- package/dist/src/core/config-mutate.js +0 -1
- package/dist/src/core/config-paths.d.ts +2 -2
- package/dist/src/core/config-paths.js +6 -1
- package/dist/src/core/config-state.js +0 -1
- package/dist/src/core/config-validate-patch-policy.js +0 -1
- package/dist/src/core/config-validate-test-toolchains.d.ts +8 -0
- package/dist/src/core/config-validate-test-toolchains.js +105 -0
- package/dist/src/core/config-validate.js +3 -1
- package/dist/src/core/config.js +0 -1
- package/dist/src/core/destructive.js +0 -1
- package/dist/src/core/diff-hunks.js +13 -7
- package/dist/src/core/engine-changes.js +0 -1
- package/dist/src/core/engine-session-lock.d.ts +12 -0
- package/dist/src/core/engine-session-lock.js +45 -0
- package/dist/src/core/file-lock.d.ts +8 -0
- package/dist/src/core/file-lock.js +157 -48
- package/dist/src/core/firefox-archive.d.ts +4 -0
- package/dist/src/core/firefox-archive.js +6 -1
- package/dist/src/core/firefox-cache.js +60 -4
- package/dist/src/core/firefox-download.js +0 -1
- package/dist/src/core/firefox-extract.js +75 -16
- package/dist/src/core/firefox-ignorefile.js +0 -1
- package/dist/src/core/firefox.d.ts +12 -0
- package/dist/src/core/firefox.js +64 -2
- package/dist/src/core/furnace-apply-dry-run.js +0 -1
- package/dist/src/core/furnace-apply-ftl.d.ts +7 -3
- package/dist/src/core/furnace-apply-ftl.js +10 -5
- package/dist/src/core/furnace-apply-helpers.js +0 -1
- package/dist/src/core/furnace-apply-output.js +2 -2
- package/dist/src/core/furnace-apply.d.ts +7 -1
- package/dist/src/core/furnace-apply.js +47 -13
- package/dist/src/core/furnace-checksum-utils.js +0 -1
- package/dist/src/core/furnace-config-array-utils.js +0 -1
- package/dist/src/core/furnace-config-custom.js +10 -1
- package/dist/src/core/furnace-config-order.js +0 -1
- package/dist/src/core/furnace-config-tokens.js +0 -1
- package/dist/src/core/furnace-config-validate.js +0 -1
- package/dist/src/core/furnace-config.d.ts +11 -0
- package/dist/src/core/furnace-config.js +37 -1
- package/dist/src/core/furnace-constants.js +0 -1
- package/dist/src/core/furnace-css-fragments.js +0 -1
- package/dist/src/core/furnace-graph-utils.js +0 -1
- package/dist/src/core/furnace-jsconfig.d.ts +2 -3
- package/dist/src/core/furnace-jsconfig.js +25 -18
- package/dist/src/core/furnace-marker.js +0 -1
- package/dist/src/core/furnace-operation.js +0 -1
- package/dist/src/core/furnace-refresh.js +0 -1
- package/dist/src/core/furnace-registration-ast.d.ts +14 -0
- package/dist/src/core/furnace-registration-ast.js +1 -2
- package/dist/src/core/furnace-registration-remove.js +0 -1
- package/dist/src/core/furnace-registration-validate.js +1 -5
- package/dist/src/core/furnace-registration.js +0 -1
- package/dist/src/core/furnace-rollback.js +0 -1
- package/dist/src/core/furnace-scanner.js +0 -1
- package/dist/src/core/furnace-stale-export.d.ts +59 -0
- package/dist/src/core/furnace-stale-export.js +123 -0
- package/dist/src/core/furnace-staleness.js +0 -1
- package/dist/src/core/furnace-state-persist.d.ts +72 -0
- package/dist/src/core/furnace-state-persist.js +118 -0
- package/dist/src/core/furnace-step-errors.d.ts +23 -0
- package/dist/src/core/furnace-step-errors.js +25 -0
- package/dist/src/core/furnace-stories.js +0 -1
- package/dist/src/core/furnace-validate-accessibility.js +0 -1
- package/dist/src/core/furnace-validate-checks.js +0 -1
- package/dist/src/core/furnace-validate-compatibility.js +9 -3
- package/dist/src/core/furnace-validate-helpers.js +0 -1
- package/dist/src/core/furnace-validate-registration.js +7 -7
- package/dist/src/core/furnace-validate-structure.d.ts +8 -0
- package/dist/src/core/furnace-validate-structure.js +14 -4
- package/dist/src/core/furnace-validate.js +0 -1
- package/dist/src/core/furnace-version-drift.d.ts +6 -3
- package/dist/src/core/furnace-version-drift.js +6 -4
- package/dist/src/core/git-base.js +0 -1
- package/dist/src/core/git-diff.js +0 -1
- package/dist/src/core/git-file-ops.js +0 -1
- package/dist/src/core/git-status.d.ts +13 -0
- package/dist/src/core/git-status.js +32 -2
- package/dist/src/core/git.d.ts +10 -1
- package/dist/src/core/git.js +63 -6
- package/dist/src/core/license-headers.js +0 -1
- package/dist/src/core/lint-cache.js +0 -1
- package/dist/src/core/lint-projection.js +0 -1
- package/dist/src/core/mach-build-artifacts.js +0 -1
- package/dist/src/core/mach-error-hints.js +0 -1
- package/dist/src/core/mach-known-noise-filter.d.ts +60 -0
- package/dist/src/core/mach-known-noise-filter.js +193 -0
- package/dist/src/core/mach-mozconfig.js +0 -1
- package/dist/src/core/mach-python.js +0 -1
- package/dist/src/core/mach-resource-shim.js +0 -1
- package/dist/src/core/mach.d.ts +8 -0
- package/dist/src/core/mach.js +36 -4
- package/dist/src/core/manifest-helpers.js +0 -1
- package/dist/src/core/manifest-register.js +0 -1
- package/dist/src/core/manifest-rules.js +0 -1
- package/dist/src/core/manifest-tokenizers.js +0 -1
- package/dist/src/core/marionette-port.d.ts +8 -0
- package/dist/src/core/marionette-port.js +34 -3
- package/dist/src/core/marionette-preflight.js +0 -1
- package/dist/src/core/markdown-table.js +0 -1
- package/dist/src/core/ownership-table.js +2 -9
- package/dist/src/core/parser-fallback.js +0 -1
- package/dist/src/core/patch-apply-fuzz.d.ts +27 -9
- package/dist/src/core/patch-apply-fuzz.js +71 -34
- package/dist/src/core/patch-apply.d.ts +30 -0
- package/dist/src/core/patch-apply.js +101 -18
- package/dist/src/core/patch-artifact-normalize.d.ts +18 -5
- package/dist/src/core/patch-artifact-normalize.js +19 -15
- package/dist/src/core/patch-export-coverage.js +0 -1
- package/dist/src/core/patch-export-metadata.js +0 -1
- package/dist/src/core/patch-export-update.js +2 -3
- package/dist/src/core/patch-export.d.ts +2 -6
- package/dist/src/core/patch-export.js +24 -48
- package/dist/src/core/patch-files.js +0 -1
- package/dist/src/core/patch-identifier-suggest.js +0 -1
- package/dist/src/core/patch-lint-checkjs.js +0 -1
- package/dist/src/core/patch-lint-chrome-jsdoc.js +0 -1
- package/dist/src/core/patch-lint-cross.js +80 -3
- package/dist/src/core/patch-lint-css.js +0 -1
- package/dist/src/core/patch-lint-diff-tag.js +0 -1
- package/dist/src/core/patch-lint-diff.d.ts +2 -1
- package/dist/src/core/patch-lint-diff.js +17 -32
- package/dist/src/core/patch-lint-imports.js +0 -1
- package/dist/src/core/patch-lint-jsdoc.js +0 -1
- package/dist/src/core/patch-lint-observer.js +0 -1
- package/dist/src/core/patch-lint-ownership.js +0 -1
- package/dist/src/core/patch-lint-reexports.js +0 -1
- package/dist/src/core/patch-lint.d.ts +37 -2
- package/dist/src/core/patch-lint.js +47 -34
- package/dist/src/core/patch-lock.js +0 -1
- package/dist/src/core/patch-manifest-consistency.js +9 -1
- package/dist/src/core/patch-manifest-io.d.ts +19 -2
- package/dist/src/core/patch-manifest-io.js +57 -21
- package/dist/src/core/patch-manifest-query.js +18 -10
- package/dist/src/core/patch-manifest-resolve.js +0 -1
- package/dist/src/core/patch-manifest-validate.js +24 -1
- package/dist/src/core/patch-manifest.d.ts +1 -1
- package/dist/src/core/patch-manifest.js +1 -2
- package/dist/src/core/patch-parse.d.ts +59 -0
- package/dist/src/core/patch-parse.js +209 -92
- package/dist/src/core/patch-policy.js +0 -1
- package/dist/src/core/patch-registration-refs.js +16 -26
- package/dist/src/core/patch-source-metadata.js +0 -1
- package/dist/src/core/patch-transform.d.ts +4 -0
- package/dist/src/core/patch-transform.js +30 -38
- package/dist/src/core/rebase-session.js +0 -1
- package/dist/src/core/register-browser-content.js +0 -1
- package/dist/src/core/register-module.js +0 -1
- package/dist/src/core/register-result.js +0 -1
- package/dist/src/core/register-scaffold.js +0 -1
- package/dist/src/core/register-shared-css.js +0 -1
- package/dist/src/core/register-test-manifest.js +0 -1
- package/dist/src/core/register-xpcshell-test.js +0 -1
- package/dist/src/core/shared-ftl.js +0 -1
- package/dist/src/core/signal-critical.js +0 -1
- package/dist/src/core/smoke-patterns.js +0 -1
- package/dist/src/core/state-file.js +0 -1
- package/dist/src/core/status-classify.d.ts +16 -0
- package/dist/src/core/status-classify.js +24 -16
- package/dist/src/core/test-harness-crash.d.ts +80 -7
- package/dist/src/core/test-harness-crash.js +251 -15
- package/dist/src/core/test-harness-output.js +0 -1
- package/dist/src/core/test-path-scope.d.ts +21 -13
- package/dist/src/core/test-path-scope.js +43 -28
- package/dist/src/core/test-stale-check.d.ts +29 -1
- package/dist/src/core/test-stale-check.js +59 -1
- package/dist/src/core/test-stale-symlink.js +0 -1
- package/dist/src/core/test-xpcshell-retry.js +0 -1
- package/dist/src/core/token-coverage.js +0 -1
- package/dist/src/core/token-dark-mode.js +0 -1
- package/dist/src/core/token-docs.js +0 -1
- package/dist/src/core/token-manager.js +0 -1
- package/dist/src/core/token-scaffold.js +0 -1
- package/dist/src/core/token-variant.js +0 -1
- package/dist/src/core/toolchain-preflight.d.ts +31 -10
- package/dist/src/core/toolchain-preflight.js +97 -36
- package/dist/src/core/typecheck-shim.js +13 -1
- package/dist/src/core/typecheck.js +0 -1
- package/dist/src/core/wire-destroy.js +0 -1
- package/dist/src/core/wire-dom-fragment.js +0 -1
- package/dist/src/core/wire-init.js +0 -1
- package/dist/src/core/wire-subscript.js +0 -1
- package/dist/src/core/wire-targets.js +0 -1
- package/dist/src/core/wire-utils.js +0 -1
- package/dist/src/core/xpcshell-appdir.js +1 -4
- package/dist/src/errors/base.d.ts +29 -0
- package/dist/src/errors/base.js +35 -1
- package/dist/src/errors/build.js +0 -1
- package/dist/src/errors/codes.d.ts +6 -0
- package/dist/src/errors/codes.js +6 -1
- package/dist/src/errors/config.js +0 -1
- package/dist/src/errors/download.js +0 -1
- package/dist/src/errors/furnace.d.ts +11 -1
- package/dist/src/errors/furnace.js +6 -5
- package/dist/src/errors/git.js +0 -1
- package/dist/src/errors/patch.d.ts +17 -0
- package/dist/src/errors/patch.js +29 -1
- package/dist/src/errors/rebase.js +0 -1
- package/dist/src/errors/run.js +1 -1
- package/dist/src/index.js +0 -1
- package/dist/src/types/cli.js +0 -1
- package/dist/src/types/commands/index.d.ts +1 -1
- package/dist/src/types/commands/index.js +0 -1
- package/dist/src/types/commands/options.d.ts +40 -9
- package/dist/src/types/commands/options.js +0 -1
- package/dist/src/types/commands/patches.d.ts +37 -0
- package/dist/src/types/commands/patches.js +0 -1
- package/dist/src/types/commands/project.d.ts +11 -2
- package/dist/src/types/commands/project.js +0 -1
- package/dist/src/types/config.d.ts +32 -0
- package/dist/src/types/config.js +0 -1
- package/dist/src/types/furnace.d.ts +23 -0
- package/dist/src/types/furnace.js +0 -1
- package/dist/src/types/index.js +0 -1
- package/dist/src/types/typecheck.js +0 -1
- package/dist/src/utils/elapsed.js +0 -1
- package/dist/src/utils/errors.js +0 -1
- package/dist/src/utils/fs.js +37 -5
- package/dist/src/utils/logger.d.ts +7 -0
- package/dist/src/utils/logger.js +85 -3
- package/dist/src/utils/options.d.ts +15 -0
- package/dist/src/utils/options.js +25 -1
- package/dist/src/utils/package-root.js +11 -4
- package/dist/src/utils/parse.js +0 -1
- package/dist/src/utils/paths.js +0 -1
- package/dist/src/utils/platform.js +0 -1
- package/dist/src/utils/process-group.d.ts +33 -0
- package/dist/src/utils/process-group.js +161 -0
- package/dist/src/utils/process.d.ts +48 -4
- package/dist/src/utils/process.js +258 -59
- package/dist/src/utils/regex.d.ts +13 -3
- package/dist/src/utils/regex.js +53 -4
- package/dist/src/utils/validation.d.ts +0 -12
- package/dist/src/utils/validation.js +13 -26
- package/package.json +9 -8
|
@@ -63,35 +63,36 @@ const SHARD_STATUS_LABEL = {
|
|
|
63
63
|
'no-tests': 'NO-TESTS',
|
|
64
64
|
};
|
|
65
65
|
/**
|
|
66
|
-
* Runs each requested path as its own sequential harness
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
66
|
+
* Runs each requested path argument as its own sequential harness
|
|
67
|
+
* invocation (a directory argument's enumerated files stay together in
|
|
68
|
+
* one invocation — see {@link ShardGroup}) and prints an aggregate
|
|
69
|
+
* report. Per-shard failures are diagnosed via `diagnoseShardFailure`
|
|
70
|
+
* (which receives the throwing diagnosis chain from the command layer)
|
|
71
|
+
* but downgraded to warnings so every shard runs; a single aggregate
|
|
72
|
+
* error is thrown at the end when any shard did not pass.
|
|
71
73
|
*/
|
|
72
|
-
export async function runShardedTests(ctx,
|
|
74
|
+
export async function runShardedTests(ctx, groups, diagnoseShardFailure) {
|
|
73
75
|
const shards = [];
|
|
74
|
-
for (const [index,
|
|
75
|
-
info(`— Shard ${index + 1}/${
|
|
76
|
-
const outcome = await runTestsWithRetries(ctx,
|
|
77
|
-
shards.push({
|
|
76
|
+
for (const [index, group] of groups.entries()) {
|
|
77
|
+
info(`— Shard ${index + 1}/${groups.length}: ${group.label}`);
|
|
78
|
+
const outcome = await runTestsWithRetries(ctx, group.paths);
|
|
79
|
+
shards.push({ label: group.label, outcome });
|
|
78
80
|
if (outcome.verdict.kind === 'harness-crash' && outcome.verdict.signature) {
|
|
79
81
|
warn(buildHarnessCrashMessage(outcome.verdict.signature, outcome.attempts));
|
|
80
82
|
}
|
|
81
83
|
else if (outcome.verdict.kind !== 'tests-ran-ok') {
|
|
82
|
-
const diagnosis = diagnoseShardFailure(outcome,
|
|
84
|
+
const diagnosis = diagnoseShardFailure(outcome, group.label);
|
|
83
85
|
if (diagnosis)
|
|
84
86
|
warn(diagnosis);
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
|
-
const lines = shards.map(({
|
|
89
|
+
const lines = shards.map(({ label, outcome }) => `${SHARD_STATUS_LABEL[outcome.verdict.kind].padEnd(8)} ${label}` +
|
|
88
90
|
(outcome.attempts > 1 ? ` (${outcome.attempts} attempts)` : ''));
|
|
89
91
|
const failing = shards.filter(({ outcome }) => outcome.verdict.kind !== 'tests-ran-ok');
|
|
90
92
|
note(`${lines.join('\n')}\n\n${shards.length - failing.length}/${shards.length} shard(s) passed`, 'Sharded Test Summary');
|
|
91
93
|
if (failing.length > 0) {
|
|
92
94
|
throw new BuildError(`${failing.length} of ${shards.length} sharded test run(s) did not pass: ` +
|
|
93
|
-
`${failing.map(({
|
|
95
|
+
`${failing.map(({ label }) => label).join(', ')}. ` +
|
|
94
96
|
'See the per-shard diagnosis above. Use --no-shard to reproduce the combined single-invocation behaviour.', 'mach test');
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
|
-
//# sourceMappingURL=test-run.js.map
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import type { CommandContext } from '../types/cli.js';
|
|
3
1
|
import type { TestOptions } from '../types/commands/index.js';
|
|
4
2
|
/**
|
|
5
3
|
* Runs the test command to execute mach tests.
|
|
@@ -8,5 +6,3 @@ import type { TestOptions } from '../types/commands/index.js';
|
|
|
8
6
|
* @param options - Test options
|
|
9
7
|
*/
|
|
10
8
|
export declare function testCommand(projectRoot: string, testPaths: string[], options?: TestOptions): Promise<void>;
|
|
11
|
-
/** Registers the test command on the CLI program. */
|
|
12
|
-
export declare function registerTest(program: Command, { getProjectRoot, withErrorHandling }: CommandContext): void;
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
|
-
import { readBuildBaseline } from '../core/build-baseline.js';
|
|
3
|
+
import { readBuildBaseline, writeBuildBaseline } from '../core/build-baseline.js';
|
|
4
4
|
import { prepareBuildEnvironment } from '../core/build-prepare.js';
|
|
5
5
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
6
|
+
import { assertEngineGenerationUnchanged, snapshotEngineGeneration, } from '../core/engine-session-lock.js';
|
|
6
7
|
import { buildArtifactMismatchMessage, hasBuildArtifacts, hasRunnableBundle, runProtectedMachBuild, withBuildLock, } from '../core/mach.js';
|
|
7
|
-
import { assertMarionettePortAvailable, extractForwardedMarionettePort, forwardedMachArgsIncludeMarionetteClient, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
|
|
8
|
+
import { assertMarionettePortAvailable, ensureMarionettePortAvailable, extractForwardedMarionettePort, forwardedMachArgsIncludeMarionetteClient, shouldAutoForwardMarionettePortToMach, } from '../core/marionette-port.js';
|
|
8
9
|
import { formatMarionettePreflightLine, reportMarionettePreflight, runMarionettePreflight, } from '../core/marionette-preflight.js';
|
|
9
10
|
import { buildHarnessCrashMessage } from '../core/test-harness-crash.js';
|
|
10
11
|
import { createPostRebuildFailureContext } from '../core/test-harness-output.js';
|
|
11
|
-
import { analyzeTestPathScopes, formatScopeNotice } from '../core/test-path-scope.js';
|
|
12
|
-
import { checkStaleBuildForTest, formatStaleBuildWarning } from '../core/test-stale-check.js';
|
|
12
|
+
import { analyzeTestPathScopes, formatScopeNotice, } from '../core/test-path-scope.js';
|
|
13
|
+
import { checkStaleBuildForTest, findUncoveredRequestPaths, formatStaleBuildWarning, formatTestCoverageRefusal, } from '../core/test-stale-check.js';
|
|
13
14
|
import { findNearestXpcshellManifest } from '../core/xpcshell-appdir.js';
|
|
14
15
|
import { GeneralError } from '../errors/base.js';
|
|
15
16
|
import { AmbiguousBuildArtifactsError, BuildError } from '../errors/build.js';
|
|
17
|
+
import { toError } from '../utils/errors.js';
|
|
16
18
|
import { pathExists } from '../utils/fs.js';
|
|
17
|
-
import { info, intro, outro, spinner, success, warn } from '../utils/logger.js';
|
|
18
|
-
import { pickDefined } from '../utils/options.js';
|
|
19
|
+
import { info, intro, outro, spinner, success, verbose, warn } from '../utils/logger.js';
|
|
19
20
|
import { stripEnginePrefix } from '../utils/paths.js';
|
|
20
21
|
import { diagnoseShardOutcome, finalizeSingleRunOutcome } from './test-diagnose.js';
|
|
22
|
+
import { assertPathlessTestMode, assertTestModeCombinations, canaryTimeoutSeconds, reportCanaryOutcome, resolveCanaryPath, } from './test-modes.js';
|
|
21
23
|
import { DEFAULT_HARNESS_RETRIES, runShardedTests, runTestsWithRetries, } from './test-run.js';
|
|
22
24
|
async function assertTestPathsExist(engineDir, testPaths) {
|
|
23
25
|
const missingPaths = [];
|
|
@@ -102,7 +104,7 @@ async function resolveLaunchablePathForTests(engineDir, binaryName, objDir) {
|
|
|
102
104
|
}
|
|
103
105
|
return bundleCheck.expectedPath;
|
|
104
106
|
}
|
|
105
|
-
async function runPreTestBuild(projectRoot, paths, projectConfig, harnessRetries) {
|
|
107
|
+
async function runPreTestBuild(projectRoot, paths, projectConfig, harnessRetries, testPackagingCoverage) {
|
|
106
108
|
await withBuildLock(projectRoot, async () => {
|
|
107
109
|
// Pass the previous baseline exactly like `fireforge build` does, so
|
|
108
110
|
// auto-configure runs under the same conditions on both paths. The
|
|
@@ -127,6 +129,19 @@ async function runPreTestBuild(projectRoot, paths, projectConfig, harnessRetries
|
|
|
127
129
|
});
|
|
128
130
|
if (result.exitCode === 0) {
|
|
129
131
|
s.stop('Build complete');
|
|
132
|
+
// Same record, same failure tolerance as `fireforge build` /
|
|
133
|
+
// `build --ui` (build.ts): a green pre-test build refreshes the
|
|
134
|
+
// stale-build baseline so a later plain `fireforge test` over the
|
|
135
|
+
// same files — in any invocation shape — is not refused. A failed
|
|
136
|
+
// write never fails the run. The coverage claim is scoped to the
|
|
137
|
+
// requested test paths, since a file-scoped `test --build` only
|
|
138
|
+
// guarantees packaging for those manifests.
|
|
139
|
+
try {
|
|
140
|
+
await writeBuildBaseline(projectRoot, paths.engine, projectConfig.binaryName, testPackagingCoverage);
|
|
141
|
+
}
|
|
142
|
+
catch (baselineError) {
|
|
143
|
+
verbose(`Could not persist build baseline: ${toError(baselineError).message}`);
|
|
144
|
+
}
|
|
130
145
|
return;
|
|
131
146
|
}
|
|
132
147
|
s.error('Pre-test build failed');
|
|
@@ -135,9 +150,57 @@ async function runPreTestBuild(projectRoot, paths, projectConfig, harnessRetries
|
|
|
135
150
|
: 'Pre-test build failed', 'mach build faster');
|
|
136
151
|
});
|
|
137
152
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Stale-build preflight — when `--build` was NOT requested, detect
|
|
155
|
+
* packageable engine edits since the last successful build and fail
|
|
156
|
+
* UP-FRONT unless the operator explicitly accepts the stale package risk.
|
|
157
|
+
*
|
|
158
|
+
* Packaging COVERAGE is checked first, on EVERY non-`--build` run and
|
|
159
|
+
* regardless of staleness or `--allow-stale-build`: a runtime packaged by
|
|
160
|
+
* a file-scoped `test --build` can lack support fixtures for OTHER
|
|
161
|
+
* manifests even when nothing changed since — dispatching such a run
|
|
162
|
+
* hangs on missing fixtures rather than failing, so the flag (which only
|
|
163
|
+
* accepts stale CONTENT) must not be the trigger. Field incident: a
|
|
164
|
+
* three-file scoped rebuild left `file_tiles_audio.html` unpackaged and a
|
|
165
|
+
* later run over different files timed out twice at 45s waiting on
|
|
166
|
+
* `DOMAudioPlaybackStarted`.
|
|
167
|
+
*
|
|
168
|
+
* Exception: a path-less `test --doctor` stops at the Marionette health
|
|
169
|
+
* check (`runDoctorPreflight` returns 'stop' when no test paths were
|
|
170
|
+
* given) and never dispatches a test, so it needs no packaging coverage —
|
|
171
|
+
* treating it as a full-suite request would refuse a probe that touches
|
|
172
|
+
* no fixtures. The stale-content refusal still applies to it unchanged.
|
|
173
|
+
*/
|
|
174
|
+
async function enforceStaleBuildGate(projectRoot, engineDir, options, normalizedPaths) {
|
|
175
|
+
const stale = await checkStaleBuildForTest(projectRoot, engineDir);
|
|
176
|
+
const dispatchesNoTests = options.doctor === true && normalizedPaths.length === 0;
|
|
177
|
+
if (!dispatchesNoTests) {
|
|
178
|
+
const recordedCoverage = stale.baseline?.testPackagingCoverage;
|
|
179
|
+
const uncovered = findUncoveredRequestPaths(recordedCoverage, normalizedPaths);
|
|
180
|
+
if (uncovered.length > 0) {
|
|
181
|
+
throw new GeneralError(formatTestCoverageRefusal(uncovered, Array.isArray(recordedCoverage) ? recordedCoverage : []));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const staleMessage = stale.stale
|
|
185
|
+
? `${formatStaleBuildWarning(stale)}\n\n` +
|
|
186
|
+
'Run `fireforge test --build` to refresh the packaged runtime first, or pass ' +
|
|
187
|
+
'`--allow-stale-build` if you intentionally rebuilt out-of-band and accept the risk.'
|
|
188
|
+
: undefined;
|
|
189
|
+
if (staleMessage !== undefined) {
|
|
190
|
+
if (options.allowStaleBuild === true) {
|
|
191
|
+
warn(staleMessage);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
throw new GeneralError(staleMessage);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function logTestSelection(scopes) {
|
|
199
|
+
if (scopes.length > 0) {
|
|
200
|
+
const labels = scopes.map((scope) => scope.isDirectory && scope.testFileCount > 0
|
|
201
|
+
? `${scope.requestedPath} (${scope.testFileCount} test file${scope.testFileCount === 1 ? '' : 's'}, passed explicitly)`
|
|
202
|
+
: scope.requestedPath);
|
|
203
|
+
info(`Running tests: ${labels.join(', ')}`);
|
|
141
204
|
}
|
|
142
205
|
else {
|
|
143
206
|
info('Running all tests...');
|
|
@@ -255,6 +318,13 @@ function appendMarionetteForwardingArgs(extraArgs, options, forwardedPort) {
|
|
|
255
318
|
}
|
|
256
319
|
}
|
|
257
320
|
}
|
|
321
|
+
async function ensureTestMarionettePortAvailable(port, binaryName, killStaleBrowser) {
|
|
322
|
+
if (killStaleBrowser) {
|
|
323
|
+
await ensureMarionettePortAvailable(port, { binaryName, killStaleBrowser: true });
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
await assertMarionettePortAvailable(port, { binaryName });
|
|
327
|
+
}
|
|
258
328
|
/**
|
|
259
329
|
* Runs the test command to execute mach tests.
|
|
260
330
|
* @param projectRoot - Root directory of the project
|
|
@@ -268,11 +338,14 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
268
338
|
if (!(await pathExists(paths.engine))) {
|
|
269
339
|
throw new GeneralError('Firefox source not found. Run "fireforge download" first.');
|
|
270
340
|
}
|
|
341
|
+
assertPathlessTestMode(testPaths, options);
|
|
271
342
|
const buildCheck = await assertTestBuildArtifacts(paths.engine);
|
|
272
343
|
// Load the project config once so both the build and the port
|
|
273
344
|
// probe have access to `binaryName` (the port probe uses it to
|
|
274
345
|
// recognise a fork-branded browser holding the Marionette port).
|
|
275
346
|
const projectConfig = await loadConfig(projectRoot);
|
|
347
|
+
const canaryPath = resolveCanaryPath(options, projectConfig);
|
|
348
|
+
assertTestModeCombinations(testPaths, options, canaryPath);
|
|
276
349
|
// `hasBuildArtifacts` only confirms `obj-*/dist/` exists; a partial
|
|
277
350
|
// build (linker failed, packaging step interrupted, etc.) can satisfy
|
|
278
351
|
// that check without ever writing the launchable binary the marionette
|
|
@@ -283,24 +356,23 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
283
356
|
// during spawn (exit code 1, signal none). stderr tail: (empty)`.
|
|
284
357
|
const launchablePath = await resolveLaunchablePathForTests(paths.engine, projectConfig.binaryName, buildCheck.objDir);
|
|
285
358
|
const harnessRetries = options.harnessRetries ?? DEFAULT_HARNESS_RETRIES;
|
|
359
|
+
// Normalized engine-relative request paths, hoisted above the build/stale
|
|
360
|
+
// gate: the pre-test build records them as the packaging-coverage claim,
|
|
361
|
+
// and the --allow-stale-build path checks the request against the
|
|
362
|
+
// recorded coverage. (Existence is still asserted later, after the gate —
|
|
363
|
+
// stale/coverage refusals keep precedence over missing-path errors.)
|
|
364
|
+
const requestedPaths = canaryPath !== undefined ? [canaryPath] : testPaths;
|
|
365
|
+
const normalizedPaths = requestedPaths.map((p) => stripEnginePrefix(p).trim());
|
|
286
366
|
// Run incremental build if requested
|
|
287
367
|
if (options.build) {
|
|
288
|
-
|
|
368
|
+
// A path-less `test --build` runs (and packages for) the full suite;
|
|
369
|
+
// a scoped invocation only vouches for the requested paths.
|
|
370
|
+
const coverage = normalizedPaths.length === 0 ? 'full' : normalizedPaths;
|
|
371
|
+
await runPreTestBuild(projectRoot, paths, projectConfig, harnessRetries, coverage);
|
|
289
372
|
info('');
|
|
290
373
|
}
|
|
291
374
|
else {
|
|
292
|
-
|
|
293
|
-
// packageable engine edits since the last successful `fireforge build`
|
|
294
|
-
// and warn UP-FRONT. Without this, edits to chrome / packaged resources
|
|
295
|
-
// surface only as a cryptic `NS_ERROR_FILE_NOT_FOUND` inside xpcshell
|
|
296
|
-
// after mach test has already launched (see motivating case in
|
|
297
|
-
// `core/test-stale-check.ts`). The check is warn-only so a fork that
|
|
298
|
-
// rebuilt out-of-band (no FireForge-recorded baseline update) is not
|
|
299
|
-
// blocked from running tests.
|
|
300
|
-
const stale = await checkStaleBuildForTest(projectRoot, paths.engine);
|
|
301
|
-
if (stale.stale) {
|
|
302
|
-
warn(formatStaleBuildWarning(stale));
|
|
303
|
-
}
|
|
375
|
+
await enforceStaleBuildGate(projectRoot, paths.engine, options, normalizedPaths);
|
|
304
376
|
}
|
|
305
377
|
// Resolve the effective Marionette port. Operator precedence:
|
|
306
378
|
// 1. `--marionette-port` (first-class option, parsed at the CLI layer)
|
|
@@ -324,7 +396,7 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
324
396
|
// generic bind failure. 2026-04-21 eval (Finding #20): a stale
|
|
325
397
|
// `-marionette` process from `fresh/` poisoned a later test run in
|
|
326
398
|
// the sibling `mybrowser/` workspace.
|
|
327
|
-
await
|
|
399
|
+
await ensureTestMarionettePortAvailable(effectivePort, projectConfig.binaryName, options.killStaleMarionette === true);
|
|
328
400
|
if (options.doctor) {
|
|
329
401
|
const doctorOutcome = await runDoctorPreflight({
|
|
330
402
|
engineDir: paths.engine,
|
|
@@ -337,7 +409,6 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
337
409
|
if (doctorOutcome === 'stop')
|
|
338
410
|
return;
|
|
339
411
|
}
|
|
340
|
-
const normalizedPaths = testPaths.map((p) => stripEnginePrefix(p).trim());
|
|
341
412
|
await assertTestPathsExist(paths.engine, normalizedPaths);
|
|
342
413
|
const classification = await classifyTestHarnesses(paths.engine, normalizedPaths);
|
|
343
414
|
if (classification.xpcshell.length > 0 && classification.nonXpcshell.length > 0) {
|
|
@@ -351,6 +422,12 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
351
422
|
if (options.headless) {
|
|
352
423
|
extraArgs.push('--headless');
|
|
353
424
|
}
|
|
425
|
+
if (options.auto === true) {
|
|
426
|
+
extraArgs.push('--auto');
|
|
427
|
+
}
|
|
428
|
+
if (canaryPath !== undefined) {
|
|
429
|
+
extraArgs.push(`--timeout=${String(canaryTimeoutSeconds(projectConfig))}`);
|
|
430
|
+
}
|
|
354
431
|
// --mach-arg is a verbatim passthrough for upstream mach/xpcshell/mochitest
|
|
355
432
|
// flags FireForge does not model directly (see the xpcshell appdir hint
|
|
356
433
|
// above for the motivating case). Appended AFTER --headless so mach sees
|
|
@@ -364,13 +441,19 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
364
441
|
// resolver matches paths by string prefix, so a bare directory arg
|
|
365
442
|
// silently swept in prefix-named siblings (152.0b7 → 153.0b8 drill:
|
|
366
443
|
// `…/test/hominis` also ran `…/test/hominis-tiles`, 1224 tests instead
|
|
367
|
-
// of ~200, with no indication the scope widened).
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
444
|
+
// of ~200, with no indication the scope widened). Each directory
|
|
445
|
+
// argument dispatches as its enumerated explicit test-file list — a
|
|
446
|
+
// file list cannot prefix-match a sibling (0.35.0's trailing-`/`
|
|
447
|
+
// normalization turned out to be cosmetic on Firefox 153's mach; field
|
|
448
|
+
// verification showed the sibling still ran while the echo claimed it
|
|
449
|
+
// was excluded). Any prefix-siblings are echoed so the narrowed scope
|
|
450
|
+
// is visible. Classification above intentionally used the raw
|
|
451
|
+
// argument forms; only the mach dispatch needs the exact-match shape.
|
|
372
452
|
const scopes = await analyzeTestPathScopes(paths.engine, normalizedPaths);
|
|
373
|
-
const
|
|
453
|
+
const dispatchGroups = scopes.map((scope) => ({
|
|
454
|
+
label: scope.requestedPath,
|
|
455
|
+
paths: scope.dispatchPaths,
|
|
456
|
+
}));
|
|
374
457
|
for (const scope of scopes) {
|
|
375
458
|
const notice = formatScopeNotice(scope);
|
|
376
459
|
if (notice)
|
|
@@ -380,7 +463,7 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
380
463
|
// `runTestsWithRetries` (src/commands/test-run.ts) so sharded runs probe
|
|
381
464
|
// the manifest for each file individually. See src/core/xpcshell-appdir.ts
|
|
382
465
|
// for the full motivation.
|
|
383
|
-
logTestSelection(
|
|
466
|
+
logTestSelection(scopes);
|
|
384
467
|
const perfSampleEnv = buildPerfSampleEnv(projectRoot, projectConfig.binaryName, options.perfSamples);
|
|
385
468
|
const runCtx = {
|
|
386
469
|
engineDir: paths.engine,
|
|
@@ -394,29 +477,46 @@ export async function testCommand(projectRoot, testPaths, options = {}) {
|
|
|
394
477
|
const postRebuildContext = options.build
|
|
395
478
|
? createPostRebuildFailureContext('fireforge test --build', normalizedPaths)
|
|
396
479
|
: undefined;
|
|
397
|
-
// Multi-
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
480
|
+
// Multi-argument requests shard into sequential harness runs by default
|
|
481
|
+
// (field report C3): one shared mochitest profile across files bleeds
|
|
482
|
+
// pref/media-query state into later files. Sharding is per path
|
|
483
|
+
// ARGUMENT — a directory argument keeps its enumerated files together
|
|
484
|
+
// in one invocation, preserving the one-browser-instance semantics of
|
|
485
|
+
// a directory run. --no-shard restores the combined invocation. The
|
|
486
|
+
// default must not be SILENT (drill finding: a two-file cross-file
|
|
487
|
+
// pollution repro "passed" because the headed comparison run was
|
|
488
|
+
// sharded without saying so, briefly misattributing a suite-context
|
|
489
|
+
// bug to an upstream headless regression), so a one-line notice states
|
|
490
|
+
// what sharding does and does not exercise.
|
|
491
|
+
if (canaryPath === undefined && dispatchGroups.length > 1 && options.shard !== false) {
|
|
492
|
+
info(`Sharding: running ${dispatchGroups.length} test path arguments in isolated browser instances ` +
|
|
493
|
+
'(one mach invocation per argument; a directory argument keeps its files in one instance). ' +
|
|
494
|
+
'Cross-argument state is NOT exercised — pass --no-shard for a combined single-instance run.');
|
|
495
|
+
const generationBefore = await snapshotEngineGeneration(paths.engine);
|
|
496
|
+
try {
|
|
497
|
+
await runShardedTests(runCtx, dispatchGroups, (outcome, label) => diagnoseShardOutcome(outcome, label, projectConfig.binaryName, postRebuildContext));
|
|
498
|
+
}
|
|
499
|
+
finally {
|
|
500
|
+
await assertEngineGenerationUnchanged(paths.engine, generationBefore);
|
|
501
|
+
}
|
|
411
502
|
return;
|
|
412
503
|
}
|
|
504
|
+
const combinedDispatchPaths = dispatchGroups.flatMap((group) => group.paths);
|
|
413
505
|
let outcome;
|
|
506
|
+
const generationBefore = await snapshotEngineGeneration(paths.engine);
|
|
414
507
|
try {
|
|
415
|
-
outcome = await runTestsWithRetries(runCtx,
|
|
508
|
+
outcome = await runTestsWithRetries(runCtx, combinedDispatchPaths);
|
|
416
509
|
}
|
|
417
510
|
catch (error) {
|
|
418
511
|
throw new BuildError('Test process failed to start', 'mach test', error instanceof Error ? error : undefined);
|
|
419
512
|
}
|
|
513
|
+
finally {
|
|
514
|
+
await assertEngineGenerationUnchanged(paths.engine, generationBefore);
|
|
515
|
+
}
|
|
516
|
+
if (canaryPath !== undefined) {
|
|
517
|
+
reportCanaryOutcome(outcome);
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
420
520
|
finalizeSingleRunOutcome(outcome, normalizedPaths, projectConfig.binaryName, postRebuildContext);
|
|
421
521
|
}
|
|
422
522
|
/**
|
|
@@ -432,52 +532,3 @@ function buildPerfSampleEnv(projectRoot, binaryName, perfSamples) {
|
|
|
432
532
|
info(`Perf sample contract: ${envName}=${artifactPath}`);
|
|
433
533
|
return { [envName]: artifactPath };
|
|
434
534
|
}
|
|
435
|
-
/** Registers the test command on the CLI program. */
|
|
436
|
-
export function registerTest(program, { getProjectRoot, withErrorHandling }) {
|
|
437
|
-
program
|
|
438
|
-
.command('test [paths...]')
|
|
439
|
-
.description('Run tests via mach test')
|
|
440
|
-
.option('--headless', 'Run tests in headless mode')
|
|
441
|
-
.option('--build', 'Run incremental UI build before testing')
|
|
442
|
-
.option('--doctor', 'Run a marionette handshake preflight before tests (exit 1 on FAIL). With no paths, runs the preflight only.')
|
|
443
|
-
.option('--mach-arg <arg>', 'Forward this argument verbatim to `mach test` (repeatable). Escape valve for upstream xpcshell/mochitest flags FireForge does not model.', (value, acc) => {
|
|
444
|
-
acc.push(value);
|
|
445
|
-
return acc;
|
|
446
|
-
}, [])
|
|
447
|
-
.option('--harness-retries <n>', `Retry budget for recognized harness crashes (resource-monitor tracebacks, pre-test hangs, post-green shutdown re-entry). 0 disables retries. Default: ${String(DEFAULT_HARNESS_RETRIES)}.`, (raw) => {
|
|
448
|
-
const n = Number.parseInt(raw, 10);
|
|
449
|
-
if (!Number.isFinite(n) || n < 0 || n > 10) {
|
|
450
|
-
throw new GeneralError(`--harness-retries must be an integer in 0..10 (got "${raw}")`);
|
|
451
|
-
}
|
|
452
|
-
return n;
|
|
453
|
-
})
|
|
454
|
-
.option('--generic-mach-test', 'Force dispatch through generic `mach test` instead of the suite-specific `mach xpcshell-test` / `mach mochitest` a single-suite run auto-selects (the suite-specific commands skip the mozlog resource monitor that crashes `mach test` on some hosts).')
|
|
455
|
-
.option('--no-shard', 'Run multiple test paths in one combined mach invocation instead of sequential per-file shards (isolated instances do not exercise cross-file state, so use this to reproduce cross-file pollution bugs)')
|
|
456
|
-
.option('--perf-samples <path>', 'Publish a perf-sample artifact path to the harness via <BINARYNAME>_PERF_SAMPLE_JSON (resolved against the project root)')
|
|
457
|
-
.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) auto-forwarded mach args: --setpref=marionette.port=<n> (browser listener) and --marionette=127.0.0.1:<n> (mochitest client). Omits the client flag when --mach-arg already sets --marionette. Use when 2828 is busy or CI assigns another port.', (raw) => {
|
|
458
|
-
const n = Number.parseInt(raw, 10);
|
|
459
|
-
if (!Number.isFinite(n) || n < 1 || n > 65535) {
|
|
460
|
-
throw new GeneralError(`--marionette-port must be an integer in 1..65535 (got "${raw}")`);
|
|
461
|
-
}
|
|
462
|
-
return n;
|
|
463
|
-
})
|
|
464
|
-
.addHelpText('after', [
|
|
465
|
-
'',
|
|
466
|
-
'[paths...] semantics: a directory argument selects EXACTLY that',
|
|
467
|
-
'directory. FireForge normalizes it with a trailing "/" before',
|
|
468
|
-
'handing it to mach, because mach resolves test paths by string',
|
|
469
|
-
'prefix and a bare directory name would silently sweep in sibling',
|
|
470
|
-
'directories sharing the prefix (e.g. foo also running foo-extras).',
|
|
471
|
-
'When such siblings exist, the excluded directories are listed with',
|
|
472
|
-
'their test-file counts; pass them as separate paths to include them.',
|
|
473
|
-
'',
|
|
474
|
-
'Multiple paths shard into sequential isolated harness runs (one',
|
|
475
|
-
'browser instance per path) by default, which does not exercise',
|
|
476
|
-
'cross-file state; --no-shard restores the combined single-instance',
|
|
477
|
-
'invocation.',
|
|
478
|
-
].join('\n'))
|
|
479
|
-
.action(withErrorHandling(async (paths, options) => {
|
|
480
|
-
await testCommand(getProjectRoot(), paths, pickDefined(options));
|
|
481
|
-
}));
|
|
482
|
-
}
|
|
483
|
-
//# sourceMappingURL=test.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
|
-
import { Option } from 'commander';
|
|
2
|
+
import { Command, Option } from 'commander';
|
|
3
3
|
import { loadConfig } from '../core/config.js';
|
|
4
4
|
import { furnaceConfigExists, loadFurnaceConfig } from '../core/furnace-config.js';
|
|
5
5
|
import { addToken, getTokensCssPath, validateTokenAdd, } from '../core/token-manager.js';
|
|
@@ -183,4 +183,3 @@ export function registerToken(program, { getProjectRoot, withErrorHandling }) {
|
|
|
183
183
|
await tokenCoverageCommand(getProjectRoot());
|
|
184
184
|
}));
|
|
185
185
|
}
|
|
186
|
-
//# sourceMappingURL=token.js.map
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* Exits non-zero on any error-severity diagnostic. Warnings print
|
|
16
16
|
* but do not fail. Designed for CI use.
|
|
17
17
|
*/
|
|
18
|
+
import { Command } from 'commander';
|
|
18
19
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
19
20
|
import { furnaceConfigExists, loadFurnaceConfig } from '../core/furnace-config.js';
|
|
20
21
|
import { findJsconfigPathsDrift, syncFurnaceJsconfigPaths } from '../core/furnace-jsconfig.js';
|
|
@@ -147,4 +148,3 @@ export function registerTypecheck(program, { getProjectRoot, withErrorHandling }
|
|
|
147
148
|
await typecheckCommand(getProjectRoot(), opts);
|
|
148
149
|
}));
|
|
149
150
|
}
|
|
150
|
-
//# sourceMappingURL=typecheck.js.map
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* treat the output as pass/fail.
|
|
16
16
|
*/
|
|
17
17
|
import { join } from 'node:path';
|
|
18
|
+
import { Command } from 'commander';
|
|
18
19
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
19
20
|
import { collectFurnaceManagedPrefixes } from '../core/furnace-config.js';
|
|
20
21
|
import { isGitRepository } from '../core/git.js';
|
|
@@ -23,6 +24,7 @@ import { buildPatchQueueContext, lintPatchQueue } from '../core/patch-lint.js';
|
|
|
23
24
|
import { loadPatchesManifest, validatePatchesManifestConsistency } from '../core/patch-manifest.js';
|
|
24
25
|
import { evaluatePatchPolicy } from '../core/patch-policy.js';
|
|
25
26
|
import { collectPatchRegistrationReferences } from '../core/patch-registration-refs.js';
|
|
27
|
+
import { buildPatchClaims } from '../core/status-classify.js';
|
|
26
28
|
import { classifyFiles } from '../core/status-classify.js';
|
|
27
29
|
import { GeneralError } from '../errors/base.js';
|
|
28
30
|
import { pathExists, readText } from '../utils/fs.js';
|
|
@@ -88,14 +90,7 @@ async function detectDanglingRegistrations(patchesDir, engineDir, patches) {
|
|
|
88
90
|
return issues;
|
|
89
91
|
}
|
|
90
92
|
function detectCrossPatchFileClaims(manifestPatches) {
|
|
91
|
-
const claims =
|
|
92
|
-
for (const patch of manifestPatches) {
|
|
93
|
-
for (const file of patch.filesAffected) {
|
|
94
|
-
const existing = claims.get(file) ?? [];
|
|
95
|
-
existing.push(patch.filename);
|
|
96
|
-
claims.set(file, existing);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
93
|
+
const claims = buildPatchClaims(manifestPatches);
|
|
99
94
|
const results = [];
|
|
100
95
|
for (const [path, filenames] of claims) {
|
|
101
96
|
if (filenames.length > 1) {
|
|
@@ -284,4 +279,3 @@ export function registerVerify(program, { getProjectRoot, withErrorHandling }) {
|
|
|
284
279
|
await verifyCommand(getProjectRoot());
|
|
285
280
|
}));
|
|
286
281
|
}
|
|
287
|
-
//# sourceMappingURL=verify.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { delimiter, dirname } from 'node:path';
|
|
3
|
+
import { Command } from 'commander';
|
|
3
4
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
4
5
|
import { warnIfFurnaceStale } from '../core/furnace-staleness.js';
|
|
5
6
|
import { buildArtifactMismatchMessage, generateMozconfig, hasBuildArtifacts, hasRunnableBundle, watchWithOutput, } from '../core/mach.js';
|
|
@@ -195,6 +196,10 @@ export async function watchCommand(projectRoot) {
|
|
|
195
196
|
catch (error) {
|
|
196
197
|
throw new BuildError('Watch process failed to start', 'mach watch', error instanceof Error ? error : undefined);
|
|
197
198
|
}
|
|
199
|
+
// On Ctrl+C the bin signal handler now waits for the mach child to shut
|
|
200
|
+
// down (waitForActiveChildShutdown) before exiting, so this continuation
|
|
201
|
+
// can run and whitelist the SIGINT exit code — though the bin may still
|
|
202
|
+
// win the race and terminate first; both outcomes are acceptable.
|
|
198
203
|
if (result.exitCode !== 0 && result.exitCode !== 130) {
|
|
199
204
|
const combinedOutput = `${result.stdout}\n${result.stderr}`;
|
|
200
205
|
if (hasConfigureTimeWatchmanFailure(combinedOutput)) {
|
|
@@ -214,4 +219,3 @@ export function registerWatch(program, { getProjectRoot, withErrorHandling }) {
|
|
|
214
219
|
await watchCommand(getProjectRoot());
|
|
215
220
|
}));
|
|
216
221
|
}
|
|
217
|
-
//# sourceMappingURL=watch.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: EUPL-1.2
|
|
2
2
|
import { join, relative } from 'node:path';
|
|
3
|
+
import { Command } from 'commander';
|
|
3
4
|
import { DEFAULT_BROWSER_SUBSCRIPT_DIR, wireSubscript } from '../core/browser-wire.js';
|
|
4
5
|
import { getProjectPaths, loadConfig } from '../core/config.js';
|
|
5
6
|
import { furnaceConfigExists as checkFurnaceConfigExists, loadFurnaceConfig, } from '../core/furnace-config.js';
|
|
@@ -356,4 +357,3 @@ export function registerWire(program, { getProjectRoot, withErrorHandling }) {
|
|
|
356
357
|
await wireCommand(getProjectRoot(), name, pickDefined(options));
|
|
357
358
|
}));
|
|
358
359
|
}
|
|
359
|
-
//# sourceMappingURL=wire.js.map
|
|
@@ -12,4 +12,3 @@ export function validateBrandOverride(configuredBrand, requestedBrand) {
|
|
|
12
12
|
}
|
|
13
13
|
throw new InvalidArgumentError(`Brand override "${requestedBrand}" is not supported yet. FireForge currently operates only on the configured brand "${configuredBrand}".`, 'brand');
|
|
14
14
|
}
|
|
15
|
-
//# sourceMappingURL=brand-validation.js.map
|
|
@@ -475,4 +475,3 @@ export async function auditBuildArtifacts(projectRoot, engineDir, baseline) {
|
|
|
475
475
|
info(`Packaged: ${summary.updated} updated, ${summary.stale} stale, ${summary.missing} missing, ${summary.skipped} skipped`);
|
|
476
476
|
return summary;
|
|
477
477
|
}
|
|
478
|
-
//# sourceMappingURL=build-audit.js.map
|
|
@@ -35,4 +35,28 @@ export interface BuildBaseline {
|
|
|
35
35
|
* was recorded.
|
|
36
36
|
*/
|
|
37
37
|
packageableFingerprints?: Record<string, string>;
|
|
38
|
+
/**
|
|
39
|
+
* What the packaged test runtime produced by the recorded build covers.
|
|
40
|
+
*
|
|
41
|
+
* - `'full'`: the build packaged the full test set — written by
|
|
42
|
+
* `fireforge build` / `build --ui` and by a path-less
|
|
43
|
+
* `fireforge test --build` (full-suite run).
|
|
44
|
+
* - `string[]`: engine-relative POSIX request paths of the file/directory-
|
|
45
|
+
* scoped `fireforge test --build` invocation that produced the runtime.
|
|
46
|
+
* A directory entry covers everything beneath it. Support fixtures for
|
|
47
|
+
* manifests outside this list may be missing from `obj-*`/`_tests/`, so
|
|
48
|
+
* an `--allow-stale-build` run over uncovered paths is refused rather
|
|
49
|
+
* than dispatched into a hang.
|
|
50
|
+
*
|
|
51
|
+
* Missing on baselines written before 0.37.0 — only `fireforge build`
|
|
52
|
+
* wrote baselines then, so "full" is the honest historical value and the
|
|
53
|
+
* stale-check treats an absent field as full coverage.
|
|
54
|
+
*
|
|
55
|
+
* The record is project-scoped, which is also per-obj-dir: multi-objdir
|
|
56
|
+
* checkouts are refused up-front (`AmbiguousBuildArtifactsError`), so at
|
|
57
|
+
* most one obj dir exists per project.
|
|
58
|
+
*/
|
|
59
|
+
testPackagingCoverage?: TestPackagingCoverage;
|
|
38
60
|
}
|
|
61
|
+
/** Coverage claim of the packaged test runtime. See {@link BuildBaseline.testPackagingCoverage}. */
|
|
62
|
+
export type TestPackagingCoverage = 'full' | string[];
|