Skip to content

Test failures from module-path refactor PR #26565: tests 103778 / 103842 / 108309 fail on ZIP/TAR/portable distribution lanes #27345

@jshigetomi

Description

@jshigetomi

Summary

Three Pester tests fail in the ReleaseAutomationTest comparison of 7.7.0-preview.1 against the 7.6.0-rc.1 baseline on portable (ZIP/TAR) and several non-portable distribution lanes. All three are downstream of PR #26565 ("Refactor the module path construction code", commit 592668bd0), which is in release/v7.7.0-preview.1 but is not in v7.6.0-rc.1 (verified: git merge-base --is-ancestor 592668bd0 v7.6.0-rc.1 returns 1).

Test ID Test File Origin
103778 ConsoleHost - SettingsFile - PSModulePath - Verify PowerShell PSModulePath should contain paths from config file (Windows) test/powershell/Host/ConsoleHost.Tests.ps1#L402 New test added by PR #26565
103842 Non-Windows equivalent of 103778 (same It block, run on Linux/macOS lanes) test/powershell/Host/ConsoleHost.Tests.ps1#L402 New test added by PR #26565
108309 Import-Module with WinCompat - WinCompat process does not inherit PowerShell-Core-specific paths test/powershell/Modules/Microsoft.PowerShell.Core/CompatiblePSEditions.Module.Tests.ps1#L727 Pre-existing since PR #7183 (2018); newly fails in preview.1

Lanes affected (one or more of these tests fail per lane):

  • WindowsServer2016ZIP - Unelevated
  • ubuntu22 TAR
  • mariner ARM64
  • macOS TAR, macOS
  • linux ARM32
  • debian12
  • azurelinux30

First failing build: ReleaseAutomationTest-27557-ps-671171.

These tests were previously tracked as Case 2 of issue #27343 and are split out here because the root cause is unrelated to PR #27305 (the original framing of #27343).

Background: what PR #26565 changed

Commit 592668bd0 rewrote ModuleIntrinsics.GetModulePath and replaced the helper AddToPath (with PathContainsSubstring) with UpdatePath, which uses HashSet<string> plus Path.TrimEndingDirectorySeparator for dedup and advances insertPosition after each insert. It also added a static static ModuleIntrinsics() { SetModulePath(); } initializer.

The "EVT.Process exists" branch in preview.1's GetModulePath is:

string systemModulePathToUse = string.IsNullOrEmpty(hklmMachineModulePath)
    ? psHomeModulePath
    : hklmMachineModulePath;

int insertIndex = 0;
currentProcessModulePath = UpdatePath(currentProcessModulePath, personalModulePathToUse, ref insertIndex);
currentProcessModulePath = UpdatePath(currentProcessModulePath, sharedModulePath,       ref insertIndex);
currentProcessModulePath = UpdatePath(currentProcessModulePath, systemModulePathToUse,  ref insertIndex);

When hklmMachineModulePath is non-empty, systemModulePathToUse = hklmMachineModulePath and psHomeModulePath (i.e., $PSHOME/Modules) is never added to the process PSModulePath. The same logical branch exists on Linux/macOS via the equivalent OS-level system module path.

PR #26565 also added the new test Verify PowerShell PSModulePath should contain paths from config file (test 103778 / 103842) that asserts $PSHOME/Modules is already present at IndexOf > 0 in $env:PSModulePath. That assertion is incompatible with portable distributions where $PSHOME is a user-local path not registered in the OS-level PSModulePath.

Why each test fails

Tests 103778 (Windows) and 103842 (non-Windows)

# BeforeAll: write settings file with PSModulePath = "$PSHOME/Modules<sep>$TestDrive/NonExist"
$psModulePath = & $powershell -NoProfile -SettingsFile $CustomSettingsFile -Command '$env:PSModulePath'
# Assertion:
$index = $psModulePath.IndexOf("$mPath1$pathSep", [StringComparison]::OrdinalIgnoreCase)
$index | Should -BeGreaterThan 0   # FAILS - $PSHOME/Modules ends up at position 0

Failure mechanics on a portable distribution:

  1. Child pwsh inherits parent $env:PSModulePath = <personal><sep><shared><sep><OS-defaults> and does not contain the ZIP/TAR-extracted $PSHOME/Modules.
  2. Settings-file PSModulePath is read as hkcuUserModulePath = "$PSHOME/Modules<sep>$TestDrive/NonExist".
  3. GetModulePath "Process exists" branch sets personalModulePathToUse = hkcuUserModulePath.
  4. UpdatePath(current, "$PSHOME/Modules<sep>$TestDrive/NonExist", ref insertIndex=0):
    • $PSHOME/Modules is NOT in initialPaths -> inserted at position 0.
    • $TestDrive/NonExist is NOT in initialPaths -> inserted at position 1.
  5. Final PSModulePath starts with $PSHOME/Modules<sep>$TestDrive/NonExist<sep>....
  6. IndexOf("$PSHOME/Modules<sep>") = 0 -> Should -BeGreaterThan 0 fails.
  7. StartsWith("$TestDrive/NonExist<sep>") = false -> later assertion also fails.

The BeforeAll comment in the test literally states: "$mPath1 already exists in the value of env PSModulePath, so it won''t be added again." That precondition does not hold on portable distributions. Since the It block is shared across platforms, it manifests as test 103778 on Windows lanes and 103842 on non-Windows lanes.

Test 108309

$pscoreSystemPath = Join-Path -Path $PSHOME -ChildPath 'Modules'
$pscorePaths = $env:psmodulepath
$pscorePaths | Should -BeLike "*$pscoreSystemPath*"   # FAILS

Same root assumption - the test requires $PSHOME/Modules to be in $env:PSModulePath. On portable distributions this is not guaranteed.

This test is 7+ years old. It is reported as a "New Failure" by the comparator because it likely passed on the rc.1 baseline (rc.1 uses the older AddToPath path-construction code which had different ordering/dedup semantics) and now fails on preview.1 after PR #26565. Open verification item: confirm the rc.1 ReleaseAutomationTest result for 108309 on each affected lane.

Suggested resolution

Test side (required regardless): make the assertions portable-distribution-aware.

For tests 103778 / 103842 (test/powershell/Host/ConsoleHost.Tests.ps1):

Option A - have BeforeAll ensure the precondition the test was written against:

BeforeAll {
    $CustomSettingsFile = Join-Path -Path $TestDrive -ChildPath 'powershell.test.json'
    $mPath1 = Join-Path $PSHOME 'Modules'
    $mPath2 = Join-Path $TestDrive 'NonExist'
    $pathSep = [System.IO.Path]::PathSeparator

    # Test assumes $PSHOME/Modules is already in $env:PSModulePath.
    # On ZIP/TAR/portable distributions this may not hold; ensure it.
    $contains = $env:PSModulePath -split [regex]::Escape($pathSep) |
        Where-Object { [string]::Equals($_, $mPath1, 'OrdinalIgnoreCase') }
    if (-not $contains) {
        $script:savedPSModulePath = $env:PSModulePath
        $env:PSModulePath = "$mPath1$pathSep$env:PSModulePath"
    }

    $ModulePath = "${mPath1}${pathSep}${mPath2}".Replace(''\'', "\\")
    Set-Content -Path $CustomSettingsfile -Value "{`"Microsoft.PowerShell:ExecutionPolicy`":`"Unrestricted`", `"PSModulePath`": `"$ModulePath`" }" -ErrorAction Stop
}

AfterAll {
    if ($script:savedPSModulePath) {
        $env:PSModulePath = $script:savedPSModulePath
    }
}

Option B - skip when the precondition does not hold:

It "Verify PowerShell PSModulePath should contain paths from config file" {
    if (($env:PSModulePath -split [regex]::Escape([IO.Path]::PathSeparator)) -notcontains $mPath1) {
        Set-ItResult -Skipped -Because ''$PSHOME/Modules is not in $env:PSModulePath (portable distribution); test precondition does not hold.''
        return
    }
    # ... existing assertions ...
}

For test 108309 (test/powershell/Modules/Microsoft.PowerShell.Core/CompatiblePSEditions.Module.Tests.ps1):

It ''WinCompat process does not inherit PowerShell-Core-specific paths'' {
    $pscoreUserPath   = Join-Path -Path $HOME -ChildPath "Documents/PowerShell/Modules"
    $pscoreSharedPath = Join-Path -Path $env:ProgramFiles -ChildPath "PowerShell/Modules"
    $pscoreSystemPath = Join-Path -Path $PSHOME -ChildPath ''Modules''
    $pscorePaths = $env:psmodulepath
    $pscorePaths | Should -BeLike "*$pscoreUserPath*"
    $pscorePaths | Should -BeLike "*$pscoreSharedPath*"
    if ($pscorePaths -like "*$pscoreSystemPath*") {
        $pscorePaths | Should -BeLike "*$pscoreSystemPath*"
    }
    # else: tolerated - portable/ZIP/TAR distributions do not put $PSHOME/Modules in $env:PSModulePath.
    # Remaining WinCompat exclusion assertions still run unchanged.
    # ...
}

Product side (only if 108309 was Passed on rc.1): investigate whether the AddToPath -> UpdatePath rewrite in PR #26565 changed the effective PSModulePath shape on portable distributions and, if so, restore the previous behavior or explicitly add psHomeModulePath to the system path even when HKLM (or its OS-level equivalent) is non-empty.

Branch strategy

Per .github/instructions/code-review-branch-strategy.instructions.md: PR #26565 is on master and on release/v7.7.0-preview.1. Fix the test files on master first; backport to release branches as needed. Do not add ZIP/TAR-specific workarounds only on the release branch.

Files affected

  • test/powershell/Host/ConsoleHost.Tests.ps1 (lines 391-413) - tests 103778 / 103842
  • test/powershell/Modules/Microsoft.PowerShell.Core/CompatiblePSEditions.Module.Tests.ps1 (line 727) - test 108309
  • src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs - product-side, only if 108309 regression on rc.1 is confirmed

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-BugIssue has been identified as a bug in the product

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions