Repository navigation
Reuse the loaded module in tests instead of force-importing it 165 times - #133
Merged
juemerson-at-purestorage merged 14 commits intoAug 20, 2026
Conversation
Pin the offset contract Task 3 splices on, and correct four stale or inaccurate comments in the single-definition library. - Assert StartOffset/EndOffset/Text in the existing multi-line backtick- continuation test: ReadAllText().Substring(Start, End - Start) must equal the returned Text, and Text must begin with Import-Module and end with -Force. That last pair is what proves the extent spans the whole two-line command rather than truncating at the continuation. - Correct the doc comment: the offsets are CHARACTER offsets into the decoded file, not byte-exact. State that a consumer must splice a decoded string (ReadAllText + Substring) and never index a byte array. - Say six known call-site forms, not five, and enumerate them. - Describe what Get-PfbTestImportAst actually returns (the AST root, not the imports and not a CommandAst list). Not renamed: Tasks 3 and 5 are written against the current name. - Assert that a positive Test-PfbTestModuleUsage case names its reason. Left as accepted by design: NestedJoinPath absorbing any Join-Path spelling containing the manifest leaf name, and the per-spec silent drops for a differently-named variable argument and the ipmo alias. Scoped Pester: 17/17 passed under pwsh 7 and Windows PowerShell 5.1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…de entry - Log line now names the loaded base(s) and distinguishes 'no instance' from a path-spelling mismatch. - Correct the healthy force-count documentation to an enumeration plus a FORCE < CALLS / 10 ratio gate. - Invalidate the two JSON caches only when the incoming module root is not the real module base, so warm caches no longer depend on four other files' unasserted AfterEach restores. - Initialise the global counters defensively for StrictMode callers. - Tests: save/restore both caches, cover both cache-warmth directions, and cover -ManifestPath. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The rewriter hardcoded the assignment target of a -PassThru import and moved the left edge of its splice back to the line's indentation, so `$myOwnName = Import-Module ... -PassThru` came out renamed to `$script:module = ...` and any earlier statement on the same line was deleted silently. It now replaces the enclosing AssignmentStatementAst (target copied verbatim from its left-hand side) or the single-element PipelineAst, and reports anything in another syntactic position as Unrecognised. It also had no exclusion mechanism, so it rewrote the raw -Force import in Tests/PfbTestModule.Tests.ps1 that exists precisely to install an unmarked module instance for the helper's rebuild-detection test. Excluded files are checked before the file is read and reported in their own bucket. Also: enumerate recursively, sort ordinally rather than by culture, take the spliced newline from the preceding line so a mixed-terminator file keeps its mix, and cover all six call-site forms with fixtures. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Resolve-PfbImportSplice guarded the pipeline arity and the assignment operator but never checked the replaced node's OWN parent, so four nested shapes were rewritten instead of reported Unrecognised: an assignment in an if-condition (emitting a file that does not parse), a chained assignment, and a $( ) / @( ) expression body (all three parsing while silently leaving the caller's variable empty). Both arms now require the replaced node to sit in statement position, via one shared predicate. Separately, an import carrying any parameter beyond Force/PassThru/Name was rewritten with that parameter silently dropped (-Global, -ErrorAction Stop). It is now rejected in the rewriter rather than supported; reproducing extra parameters, or widening the shared predicate, is a maintainer design call. Zero sites in the tree match either case: -WhatIf is unchanged at Changed=165, Unchanged=28, Excluded=1, Unrecognised=0. Fixtures: one per nested shape and per extra-parameter case, each asserting Unrecognised plus a byte-identical file, and one pinning a semicolon-joined FOLLOWING statement surviving a rewrite. 26 -> 33. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ate-PfbTestModuleImport.ps1)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Measured 292 skipped on a local Windows PowerShell 5.1 full-suite run
(2983 passed / 0 failed), against the 268 ceiling. Attributed exactly by a
per-file tally of the run's skip markers:
+34 Update-PfbTestModuleImport.Tests.ps1, added by this branch. The
rewriter it tests declares #Requires -Version 7.0, so the PS7 gate is
correct; the file runs in full on 7, where the leg measured 2 skipped
against its ceiling of 8.
+6 Build-PfbDeadKeyReport.Tests.ps1, which is not from this branch. It
landed in 302f9e4 (dmann000#120) on 2026-08-16, two days after 364a0fe set the
ceiling to 268. So 268 was already stale against main.
308 restores the standing +16 headroom over measured.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
juemerson-at-purestorage
added a commit
to juemerson-at-purestorage/fb-powershell
that referenced
this pull request
Aug 20, 2026
Adopt the shared module imports in the new regression suites so this branch remains compatible with PR dmann000#133. Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reuse the loaded module in tests instead of force-importing it 165 times
The problem
Test containers loaded the module in their
BeforeAlllike this:-Forcediscards the loaded module and rebuilds it, re-dot-sourcing the 574.ps1filesunder
Public/andPrivate/. That costs roughly 4.2 s. Pester runs the whole suite in onehost process, so 165 containers each paid it — and 164 of those rebuilds were of an
already-loaded, byte-identical module.
The change
Tests/PfbTestModule.ps1providesImport-PfbTestModule, which reuses the loaded instanceand resets the volatile module-scope state instead of rebuilding. Every converted container now
has two lines in its
BeforeAll:The rewrite was applied mechanically by
tools/Update-PfbTestModuleImport.ps1, an AST rewriterthat splices the replacement over the
Import-Modulecommand node rather than editing lines.It is idempotent and re-runnable: on this tree
-WhatIfreportsChanged=0 Unchanged=195 Excluded=1 Unrecognised=0, which sums to all 196*.Tests.ps1files.-Forceequivalence is the whole correctness question, and it is what the helper's reset blockbuys. All five volatile module-scope variables are cleared unconditionally on every call:
An earlier revision cleared the two JSON caches only when the module root changed, on the
grounds that they are read-only and safe to reuse. That was reversed. The condition keyed off
the module root, which is one of several ways a stale cache arrives — a test that populates
$script:PfbCapabilityMapdirectly leaves it warm and visible to the next container, which isprecisely the order-dependent leakage
-Forceused to make impossible. The reset must notbecome conditional again;
Tests/PfbTestModule.ps1says so at the site.Scope
175 files: 172 under
Tests/, two new files undertools/, and one underscripts/.Nothing under
Public/orPrivate/is touched, and neither the manifest nor the rootmodule is. That is the basis on which the repo owner waived live-FlashBlade verification for
this change — it cannot alter what goes on the wire. The claim was verified mechanically rather
than asserted: the file list contains no
.psm1, no.psd1, and nothing underData/,Reports/or.github/. Both newtools/files are dot-sourced by exactly three consumers,no wildcard
tools/lib/*.ps1dot-source picks them up, and none of their eight function namescollides with an existing definition.
The
scripts/file is a deliberate addition to the original scope — see the CI gate below.What stops this silently regressing
The helper's module-identity check fails closed. If the
ModuleBasepath spelling itcompares against ever stops matching, every container force-imports anyway, the whole suite
stays green, the coverage gate stays green, and the entire saving evaporates with nothing able
to detect it. A performance change whose failure mode is "still passes, just slow again" needs
an instrument, not a test.
So there are two, and both are new:
Tests/TestModuleImportGuard.Tests.ps1— pure AST over tracked.ps1files, so it needsneither the spec cache nor PowerShell 7 and runs ungated on both editions. Three
Its catch araw
-Forcemanifest import returning, a test file that uses the module without loading itthrough the helper, and a new module-scope
$script:variable the helper does not know toreset. A fourth asserts the guard's allowlist and the rewriter's exclusion set are equal as
sets, so the two cannot drift apart in the direction that hides a real offender.
A force-import ratio gate in
scripts/Invoke-PfbCiPester.ps1— emits both countersunconditionally into every CI job log, then fails the run if
force-imports >= calls / 10.The gate is a ratio rather than a small absolute number on purpose. The healthy force count is
an enumeration of legitimate contributors — one cold import per run, about four from
Tests/PfbTestModule.Tests.ps1(which force-imports deliberately, because the force path isits subject), and one recovery per selector-harness container that installs an unmarked
instance — so a tight pin would red on ordinary maintenance. The failure it exists to catch is
not a small drift: it drives force-imports up to roughly equal calls. An order-of-magnitude
gate separates those two regimes and still tolerates a new harness file.
The threshold is not marginal, and it is measured rather than estimated — every pwsh leg reports
183 calls / 7 force-imports and the 5.1 leg 182 / 5, against limits of 18.3 and 18.2.
That is 2.6x and 3.6x headroom. Put the other way: at 7 force-imports the call count would have
to fall below 70 for this to false-red, so a leg would need to stop running roughly two-thirds
of its containers first.
The ubuntu and macOS legs matter here specifically, because the fail-closed hazard is a
ModuleBasepath-spelling mismatch and those runners could plausibly have differed. They do not— all three pwsh legs report the same 183 / 7.
Adding this expanded the change into
scripts/, which the branch otherwise leaves alone. Thejustification is that the original design deferred the assertion on the belief that the
counters could not be read after
Invoke-Pesterreturns. That belief was wrong:Run.Exit = $falseis set deliberately so the coverage gate is not skipped, andInvoke-Pester, the gate call andexitall run in one process with the globals in scope.Without the assertion, the entire value of this PR can evaporate and no test can see it.
Verification
CI on this branch, all four matrix legs green
(run 32340782815):
Every leg passes both the coverage gate and the new force-import ratio gate. The three pwsh legs
report identical counters, and both Windows legs match a local full-suite run exactly (596.38 s
on pwsh7, 226.05 s on 5.1 locally).
On every leg,
expected baseequalledloaded base(s)on every logged line, so the fail-closedcondition is absent rather than merely unobserved.
A note for anyone reading a full run log:
Tests/CiCoverageGate.Tests.ps1prints severalCOVERAGE GATE FAILED (issue #63)blocks as it exercises the gate's own failure paths onpurpose. The real verdicts are the
Coverage gate passedlines at the end.Because the risk this change introduces is order-dependent state leakage between containers,
that was exercised directly rather than inferred from a green suite: a 26-file subset run five
times — control, three seeded permutations, and one hand-built adversarial order placing a
module-root redirector immediately upstream of the cache consumers — on both editions, all
green. The permutations were confirmed to have actually taken effect by checking that the
sequence of force-import reasons differed between them, since five identical green runs would
otherwise be consistent with Pester ignoring the ordering entirely. 21 files were additionally
run standalone, and the grouped totals reconcile exactly to the sum of the isolated totals on
both editions, so grouping neither skipped nor silently added tests.
Disclosures
The recovered-time figure is an estimate, not a controlled measurement. The 596.38 s and
226.05 s above are measured on this tree, but the "before" figure comes from the originating
analysis rather than a run on the same machine. The saving is inferred from the force-import
count — 7 forced imports where there were once 165, which is solid — but this is not a
before/after benchmark on identical hardware.
The 5.1 skip ceiling was raised 273 → 313, and it absorbs +6 of drift that is not from this
branch. The branch was cut before #112 landed, so its own raise (268 → 308) collided with
#112's (268 → 273) on rebase. The two are additive rather than competing, because they gate
different files, and the total is attributed exactly:
Build-PfbCapabilityMap.ContextScopeDrift.Tests.ps1, from #112Update-PfbTestModuleImport.Tests.ps1, added here — PS7-gated because the tool it tests declares#Requires -Version 7.0Build-PfbDeadKeyReport.Tests.ps1, from neither branchThe +6 is the part worth a maintainer's eye. It landed in #120 two days after the ceiling was
last set; #120 still had slack and passed without touching this file, so 268 was already stale
against main before either branch existed. Whether folding those 6 in here is acceptable or
should be split out is your call. 313 keeps the standing +16 headroom over 297.
That collision is a symptom rather than a one-off, and #132 proposes the fix: a per-file
expected-skip map, so movement reds the PR that caused it and names the file, instead of being
absorbed by headroom and billed to a later innocent PR.
Live-FlashBlade verification was waived by the repo owner for this change, on the basis that
it touches only CI test scaffolding and no cmdlet behaviour. The scope section above is the
evidence for the second half of that.
No version bump and no
CHANGELOG.mdentry — those are separate maintainer decisions.