PowerShell/test/powershell/engine/Module/ModulePath.Tests.ps1
Dan Travison 4683934793 Update PowerShell to handle the case where the Windows PowerShell module path is already in the environment's PSModulePath (#7727)
This change updates ModuleIntrinsics.GetModulePath to handle the case where the Windows PowerShell module path is already in the environment's PSModulePath or when launched from a different version of PowerShell.

Previously, GetModulePath would append $PSHOME\Modules to the PSModulePath after removing the path for the launching version without considering the Windows PowerShell module path. The result, was the Windows PowerShell modules were found first and loaded incompatible modules; such as the built-in modules.

The change detects the Windows PowerShell module path and inserts $PSHOME\Modules path before it. The new test simulates launching from a different version of pwsh that has already added the Windows PowerShell module path.

Fixes #7679
2018-09-10 10:58:38 -07:00

163 lines
6.9 KiB
PowerShell

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Describe "SxS Module Path Basic Tests" -tags "CI" {
BeforeAll {
if ($IsWindows)
{
$powershell = "$PSHOME\pwsh.exe"
$ProductName = "WindowsPowerShell"
if ($IsCoreCLR -and ($PSHOME -notlike "*Windows\System32\WindowsPowerShell\v1.0"))
{
$ProductName = "PowerShell"
}
$expectedUserPath = Join-Path -Path $HOME -ChildPath "Documents\$ProductName\Modules"
$expectedSharedPath = Join-Path -Path $env:ProgramFiles -ChildPath "$ProductName\Modules"
}
else
{
$powershell = "$PSHOME/pwsh"
$expectedUserPath = [System.Management.Automation.Platform]::SelectProductNameForDirectory("USER_MODULES")
$expectedSharedPath = [System.Management.Automation.Platform]::SelectProductNameForDirectory("SHARED_MODULES")
}
$expectedSystemPath = Join-Path -Path $PSHOME -ChildPath 'Modules'
if ($IsWindows)
{
$expectedWindowsPowerShellPSHomePath = Join-Path $env:windir "System32" "WindowsPowerShell" "v1.0" "Modules"
}
## Setup a fake PSHome
$fakePSHome = Join-Path -Path $TestDrive -ChildPath 'FakePSHome'
$fakePSHomeModuleDir = Join-Path -Path $fakePSHome -ChildPath 'Modules'
$fakePowerShell = Join-Path -Path $fakePSHome -ChildPath (Split-Path -Path $powershell -Leaf)
$fakePSDepsFile = Join-Path -Path $fakePSHome -ChildPath "pwsh.deps.json"
New-Item -Path $fakePSHome -ItemType Directory > $null
New-Item -Path $fakePSHomeModuleDir -ItemType Directory > $null
}
BeforeEach {
$originalModulePath = $env:PSModulePath
}
AfterEach {
$env:PSModulePath = $originalModulePath
}
It "validate sxs module path" {
$env:PSModulePath = ""
$defaultModulePath = & $powershell -nopro -c '$env:PSModulePath'
$paths = $defaultModulePath -split [System.IO.Path]::PathSeparator
if ($IsWindows)
{
$paths.Count | Should -Be 4
}
else
{
$paths.Count | Should -Be 3
}
$paths[0].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedUserPath
$paths[1].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedSharedPath
$paths[2].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedSystemPath
if ($IsWindows)
{
$paths[3].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedWindowsPowerShellPSHomePath
}
}
It "ignore pshome module path derived from a different powershell core instance" -Skip:(!$IsCoreCLR) {
## Create 'powershell' and 'pwsh.deps.json' in the fake PSHome folder,
## so that the module path calculation logic would believe it's real.
New-Item -Path $fakePowerShell -ItemType File -Force > $null
New-Item -Path $fakePSDepsFile -ItemType File -Force > $null
try {
## PSHome module path derived from another powershell core instance should be ignored
$env:PSModulePath = $fakePSHomeModuleDir
$newModulePath = & $powershell -nopro -c '$env:PSModulePath'
$paths = $newModulePath -split [System.IO.Path]::PathSeparator
if ($IsWindows)
{
$paths.Count | Should -Be 4
}
else
{
$paths.Count | Should -Be 3
}
$paths[0] | Should -Be $expectedUserPath
$paths[1] | Should -Be $expectedSharedPath
$paths[2] | Should -Be $expectedSystemPath
if ($IsWindows)
{
$paths[3].TrimEnd([System.IO.Path]::DirectorySeparatorChar) | Should -Be $expectedWindowsPowerShellPSHomePath
}
} finally {
## Remove 'powershell' and 'pwsh.deps.json' from the fake PSHome folder
Remove-Item -Path $fakePowerShell -Force -ErrorAction SilentlyContinue
Remove-Item -Path $fakePSDepsFile -Force -ErrorAction SilentlyContinue
}
}
It "keep non-pshome module path derived from powershell core instance parent" -Skip:(!$IsCoreCLR) {
## non-pshome module path derived from another powershell core instance should be preserved
$customeModules = Join-Path -Path $TestDrive -ChildPath 'CustomModules'
$env:PSModulePath = $fakePSHomeModuleDir, $customeModules -join ([System.IO.Path]::PathSeparator)
$newModulePath = & $powershell -nopro -c '$env:PSModulePath'
$paths = $newModulePath -split [System.IO.Path]::PathSeparator
if ($IsWindows)
{
$paths.Count | Should -Be 6
}
else
{
$paths.Count | Should -Be 5
}
$paths -contains $fakePSHomeModuleDir | Should -BeTrue
$paths -contains $customeModules | Should -BeTrue
}
It 'Ensures $PSHOME\Modules is inserted correctly when launched from a different version of PowerShell' -Skip:(!($IsCoreCLR -and $IsWindows)) {
# When launched from a different version of PowerShell, PSModulePath contains the other version's PSHOME\Modules path
# and the Windows PowerShell modoule path. THe other version's module path should be removed and this version's
# PSHOME\Modules path should be inserted before Windows PowerShell module path.
$winpwshModulePath = [System.IO.Path]::Combine([System.Environment]::SystemDirectory, "WindowsPowerShell", "v1.0", "Modules");
$pwshModulePath = Join-Path -Path $PSHOME -ChildPath 'Modules'
# create a fake 'other version' $PSHOME and $PSHOME\Modules
$fakeHome = Join-Path -Path $TestDrive -ChildPath 'fakepwsh'
$fakeModulePath = Join-Path -Path $fakeHome -ChildPath 'Modules'
$null = New-Item -Path $fakeHome -ItemType Directory
$null = New-Item -Path $fakeModulePath -ItemType Directory
# powershell looks for these to files to determine the directory is a pwsh directory.
Set-Content -Path "$fakeHome\pwsh.exe" -Value "fake pwsh.exe"
Set-Content -Path "$fakeHome\pwsh.deps.json" -Value 'fake pwsh.deps.json'
# replace the actual pwsh module path with the fake one.
$fakeModulePath = $env:PSModulePath.Replace($pwshModulePath, $fakeModulePath, [StringComparison]::OrdinalIgnoreCase)
$newModulePath = & $powershell -nopro -c '$env:PSModulePath'
$pwshIndex = $newModulePath.IndexOf($pwshModulePath, [StringComparison]::OrdinalIgnoreCase)
$wpshIndex = $newModulePath.IndexOf($winpwshModulePath, [StringComparison]::OrdinalIgnoreCase)
# ensure both module paths exist and the pwsh module path occurs before the Windows PowerShell module path
$pwshIndex | Should -Not -Be -1
$wpshIndex | Should -Not -Be -1
$pwshIndex | Should -BeLessThan $wpshIndex
}
}