PowerShell/test/powershell/engine/Module/ModulePath.Tests.ps1

191 lines
8.9 KiB
PowerShell

# Copyright (c) Microsoft Corporation.
# 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"
$userConfigPath = "~/Documents/powershell/powershell.config.json"
}
else
{
$powershell = "$PSHOME/pwsh"
$expectedUserPath = [System.Management.Automation.Platform]::SelectProductNameForDirectory("USER_MODULES")
$expectedSharedPath = [System.Management.Automation.Platform]::SelectProductNameForDirectory("SHARED_MODULES")
$userConfigPath = "~/.config/powershell/powershell.config.json"
}
$userConfigExists = $false
if (Test-Path $userConfigPath) {
$userConfigExists = $true
Copy-Item $userConfigPath "$userConfigPath.backup" -Force -ErrorAction Ignore
}
$expectedSystemPath = Join-Path -Path $PSHOME -ChildPath 'Modules'
# Skip these tests in cases when there is no 'pwsh' executable (e.g. when framework dependent PS package is used)
$skipNoPwsh = -not (Test-Path $powershell)
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
}
AfterAll {
if ($userConfigExists) {
Move-Item "$userConfigPath.backup" $userConfigPath -Force -ErrorAction Ignore
}
else {
Remove-Item "$userConfigPath" -Force -ErrorAction Ignore
}
}
BeforeEach {
$originalModulePath = $env:PSModulePath
}
AfterEach {
$env:PSModulePath = $originalModulePath
}
It "validate sxs module path" -Skip:$skipNoPwsh {
$env:PSModulePath = ""
$defaultModulePath = & $powershell -nopro -c '$env:PSModulePath'
$pathSeparator = [System.IO.Path]::PathSeparator
$paths = $defaultModulePath.Replace("$pathSeparator$pathSeparator", "$pathSeparator") -split $pathSeparator
if ($IsWindows)
{
$expectedPaths = 3 # user, shared, pshome
$userPaths = [System.Environment]::GetEnvironmentVariable("PSModulePath", [System.EnvironmentVariableTarget]::User)
$expectedPaths += $userPaths ? $userPaths.Split($pathSeparator).Count : 0
$machinePaths = [System.Environment]::GetEnvironmentVariable("PSModulePath", [System.EnvironmentVariableTarget]::Machine)
$expectedPaths += $machinePaths ? $machinePaths.Split($pathSeparator).Count : 0
$paths.Count | Should -Be $expectedPaths
}
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
$defaultModulePath | Should -BeLike "*$expectedWindowsPowerShellPSHomePath*"
}
It "Works with pshome module path derived from a different PowerShell instance" -Skip:(!$IsCoreCLR -or $skipNoPwsh) {
## 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 instance should be ignored
$env:PSModulePath = $fakePSHomeModuleDir
$newModulePath = & $powershell -nopro -c '$env:PSModulePath'
$paths = $newModulePath -split [System.IO.Path]::PathSeparator
$paths.Count | Should -Be 4
$paths[0] | Should -Be $expectedUserPath
$paths[1] | Should -Be $expectedSharedPath
$paths[2] | Should -Be $expectedSystemPath
$paths[3] | Should -Be $fakePSHomeModuleDir
} 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 instance parent" -Skip:(!$IsCoreCLR -or $skipNoPwsh) {
## non-pshome module path derived from another PowerShell 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
$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) -or $skipNoPwsh) {
# When launched from a different version of PowerShell, PSModulePath contains the other version's PSHOME\Modules path
# and the Windows PowerShell module 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
}
It 'Windows PowerShell does not inherit PowerShell paths' -Skip:(!$IsWindows) {
$out = powershell.exe -noprofile -command '$env:PSModulePath'
$out | Should -Not -BeLike "*$expectedUserPath*"
$out | Should -Not -BeLike "*$expectedSharedPath*"
$out | Should -Not -BeLike "*$expectedSystemPath*"
}
It 'Windows PowerShell inherits user added paths' -Skip:(!$IsWindows) {
$env:PSModulePath += ";myPath"
$out = powershell.exe -noprofile -command '$env:PSModulePath'
$out | Should -BeLike '*;myPath'
}
It 'Windows PowerShell does not inherit path defined in powershell.config.json' -Skip:(!$IsWindows) {
try {
$userConfig = '{ "PSModulePath": "myUserPath" }'
Set-Content -Path $userConfigPath -Value $userConfig -Force
$out = & $powershell -noprofile -command 'powershell.exe -noprofile -command $env:PSModulePath'
$out | Should -Not -BeLike 'myUserPath;*'
}
finally {
Remove-Item -Path $userConfigPath -Force
}
}
}