2020-03-24 11:08:37 -07:00
# Copyright (c) Microsoft Corporation.
2018-02-13 09:23:53 -08:00
# Licensed under the MIT License.
2016-08-11 17:59:04 -07:00
Describe "SxS Module Path Basic Tests" -tags "CI" {
2017-01-16 13:31:14 -08:00
2016-08-11 17:59:04 -07:00
BeforeAll {
2017-01-16 13:31:14 -08:00
if ( $IsWindows )
2016-08-11 17:59:04 -07:00
{
2017-10-17 17:25:11 -07:00
$powershell = " $PSHOME \pwsh.exe"
2016-08-11 17:59:04 -07:00
$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"
2019-11-15 17:29:04 -08:00
$userConfigPath = "~/Documents/powershell/powershell.config.json"
2016-08-11 17:59:04 -07:00
}
else
{
2017-10-17 17:25:11 -07:00
$powershell = " $PSHOME /pwsh"
2016-08-11 17:59:04 -07:00
$expectedUserPath = [ System.Management.Automation.Platform ]:: SelectProductNameForDirectory ( "USER_MODULES" )
$expectedSharedPath = [ System.Management.Automation.Platform ]:: SelectProductNameForDirectory ( "SHARED_MODULES" )
2019-11-15 17:29:04 -08:00
$userConfigPath = "~/.config/powershell/powershell.config.json"
2016-08-11 17:59:04 -07:00
}
2019-11-15 17:29:04 -08:00
$userConfigExists = $false
if ( Test-Path $userConfigPath ) {
$userConfigExists = $true
Copy-Item $userConfigPath " $userConfigPath .backup" -Force -ErrorAction Ignore
}
2016-08-10 16:34:07 -07:00
$expectedSystemPath = Join-Path -Path $PSHOME -ChildPath 'Modules'
2016-09-14 12:17:54 -07:00
2018-09-17 13:22:00 -07:00
# 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 )
2019-05-03 03:38:06 -07:00
2018-07-16 16:58:40 -07:00
if ( $IsWindows )
{
$expectedWindowsPowerShellPSHomePath = Join-Path $env:windir "System32" "WindowsPowerShell" "v1.0" "Modules"
}
2016-09-14 12:17:54 -07:00
## 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 )
2017-10-17 17:25:11 -07:00
$fakePSDepsFile = Join-Path -Path $fakePSHome -ChildPath "pwsh.deps.json"
2016-09-14 12:17:54 -07:00
New-Item -Path $fakePSHome -ItemType Directory > $null
New-Item -Path $fakePSHomeModuleDir -ItemType Directory > $null
2016-08-10 16:34:07 -07:00
}
2019-11-15 17:29:04 -08:00
AfterAll {
if ( $userConfigExists ) {
Move-Item " $userConfigPath .backup" $userConfigPath -Force -ErrorAction Ignore
}
else {
Remove-Item " $userConfigPath " -Force -ErrorAction Ignore
}
}
2016-08-11 17:59:04 -07:00
BeforeEach {
2017-03-15 12:04:28 -07:00
$originalModulePath = $env:PSModulePath
2016-08-11 17:59:04 -07:00
}
AfterEach {
2017-03-15 12:04:28 -07:00
$env:PSModulePath = $originalModulePath
2016-08-11 17:59:04 -07:00
}
2018-09-17 13:22:00 -07:00
It "validate sxs module path" -Skip: $skipNoPwsh {
2016-08-11 17:59:04 -07:00
2017-03-15 12:04:28 -07:00
$env:PSModulePath = ""
$defaultModulePath = & $powershell -nopro -c '$env:PSModulePath'
2019-12-09 16:25:08 -08:00
$pathSeparator = [ System.IO.Path ]:: PathSeparator
2016-08-11 17:59:04 -07:00
2019-12-09 16:25:08 -08:00
$paths = $defaultModulePath . Replace ( " $pathSeparator$pathSeparator " , " $pathSeparator " ) -split $pathSeparator
2016-08-11 17:59:04 -07:00
2018-07-16 16:58:40 -07:00
if ( $IsWindows )
{
2019-12-09 16:25:08 -08:00
$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
2018-07-16 16:58:40 -07:00
}
else
{
$paths . Count | Should -Be 3
}
2018-03-14 12:13:32 -07:00
$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
2019-12-09 16:25:08 -08:00
$defaultModulePath | Should -BeLike "* $expectedWindowsPowerShellPSHomePath *"
2016-09-14 12:17:54 -07:00
}
2019-11-15 17:29:04 -08:00
It "Works with pshome module path derived from a different PowerShell instance" -Skip: (! $IsCoreCLR -or $skipNoPwsh ) {
2016-09-14 12:17:54 -07:00
2017-10-17 17:25:11 -07:00
## Create 'powershell' and 'pwsh.deps.json' in the fake PSHome folder,
2016-09-14 12:17:54 -07:00
## 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 {
2019-05-10 15:34:27 -07:00
## PSHome module path derived from another PowerShell instance should be ignored
2017-03-15 12:04:28 -07:00
$env:PSModulePath = $fakePSHomeModuleDir
$newModulePath = & $powershell -nopro -c '$env:PSModulePath'
2016-09-14 12:17:54 -07:00
$paths = $newModulePath -split [ System.IO.Path ]:: PathSeparator
2019-11-15 17:29:04 -08:00
$paths . Count | Should -Be 4
2018-03-14 12:13:32 -07:00
$paths [ 0 ] | Should -Be $expectedUserPath
$paths [ 1 ] | Should -Be $expectedSharedPath
$paths [ 2 ] | Should -Be $expectedSystemPath
2019-11-15 17:29:04 -08:00
$paths [ 3 ] | Should -Be $fakePSHomeModuleDir
2016-09-14 12:17:54 -07:00
} finally {
2017-10-17 17:25:11 -07:00
## Remove 'powershell' and 'pwsh.deps.json' from the fake PSHome folder
2016-09-14 12:17:54 -07:00
Remove-Item -Path $fakePowerShell -Force -ErrorAction SilentlyContinue
Remove-Item -Path $fakePSDepsFile -Force -ErrorAction SilentlyContinue
}
}
2019-05-10 15:34:27 -07:00
It "keep non-pshome module path derived from PowerShell instance parent" -Skip: (! $IsCoreCLR -or $skipNoPwsh ) {
2016-09-14 12:17:54 -07:00
2019-05-10 15:34:27 -07:00
## non-pshome module path derived from another PowerShell instance should be preserved
2016-09-14 12:17:54 -07:00
$customeModules = Join-Path -Path $TestDrive -ChildPath 'CustomModules'
2017-03-15 12:04:28 -07:00
$env:PSModulePath = $fakePSHomeModuleDir , $customeModules -join ([ System.IO.Path ]:: PathSeparator )
$newModulePath = & $powershell -nopro -c '$env:PSModulePath'
2016-09-14 12:17:54 -07:00
$paths = $newModulePath -split [ System.IO.Path ]:: PathSeparator
2019-11-15 17:29:04 -08:00
$paths . Count | Should -Be 5
2018-03-14 12:13:32 -07:00
$paths -contains $fakePSHomeModuleDir | Should -BeTrue
$paths -contains $customeModules | Should -BeTrue
2016-08-10 16:34:07 -07:00
}
2017-07-07 09:43:37 -07:00
2018-09-17 13:22:00 -07:00
It 'Ensures $PSHOME\Modules is inserted correctly when launched from a different version of PowerShell' -Skip: (!( $IsCoreCLR -and $IsWindows ) -or $skipNoPwsh ) {
2018-09-10 10:58:38 -07:00
# When launched from a different version of PowerShell, PSModulePath contains the other version's PSHOME\Modules path
2019-05-03 03:38:06 -07:00
# and the Windows PowerShell module path. The other version's module path should be removed and this version's
2018-09-10 10:58:38 -07:00
# 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
}
2019-11-15 17:29:04 -08:00
It 'Windows PowerShell does not inherit PowerShell paths' -Skip: (! $IsWindows ) {
$out = powershell . exe -noprofile -command '$env:PSModulePath'
2020-03-09 23:31:44 -07:00
$out | Should -Not -BeLike "* $expectedUserPath *"
$out | Should -Not -BeLike "* $expectedSharedPath *"
$out | Should -Not -BeLike "* $expectedSystemPath *"
2019-11-15 17:29:04 -08:00
}
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 ) {
2019-12-09 16:25:08 -08:00
try {
$userConfig = '{ "PSModulePath": "myUserPath" }'
Set-Content -Path $userConfigPath -Value $userConfig -Force
2023-08-28 10:55:25 -07:00
$out = & $powershell -noprofile -command 'powershell.exe -noprofile -command `$env:PSModulePath'
2019-12-09 16:25:08 -08:00
$out | Should -Not -BeLike 'myUserPath;*'
}
finally {
Remove-Item -Path $userConfigPath -Force
}
2019-11-15 17:29:04 -08:00
}
2023-05-02 13:04:30 -07:00
It 'User PSModulePath has trailing separator' {
if ( $IsWindows ) {
$validation = "*\ $env:SystemDrive \*"
}
else {
$validation = "*//*"
}
$newUserPath = Join-Path $expectedUserPath ([ System.IO.Path ]:: DirectorySeparatorChar )
$env:PSModulePath = $env:PSModulePath . Replace ( $expectedUserPath , $newUserPath ). Replace ( $expectedSharedPath , "" )
$out = & $powershell -noprofile -command '$env:PSModulePath'
$out . Split ([ System.IO.Path ]:: PathSeparator , [ System.StringSplitOptions ]:: RemoveEmptyEntries ) | Should -Not -BeLike $validation
}
2016-08-10 16:34:07 -07:00
}
2023-05-22 17:02:31 -07:00
Describe "ModuleIntrinsics.GetPSModulePath API tests" -tag @ ( 'CI' , 'RequireAdminOnWindows' , 'RequireSudoOnUnix' ) {
BeforeAll {
# create a local repostory and install a module
$localSourceName = [ Guid ]:: NewGuid (). ToString ( "n" )
$localSourceLocation = Join-Path $PSScriptRoot assets
Register-PSRepository -Name $localSourceName -SourceLocation $localSourceLocation -InstallationPolicy Trusted -ErrorAction SilentlyContinue
Install-Module -Force -Scope AllUsers -Name PowerShell . TestPackage -Repository $localSourceName -ErrorAction SilentlyContinue
Install-Module -Force -Scope CurrentUser -Name PowerShell . TestPackage -Repository $localSourceName -ErrorAction SilentlyContinue
$testCases = @ (
@ { Name = "User" ; Expected = $IsWindows ?
( Resolve-Path ([ Environment ]:: GetFolderPath ( "Personal" ) + "\PowerShell\Modules" )). Path :
( Resolve-Path ([ System.Management.Automation.Platform ]:: SelectProductNameForDirectory ( "USER_MODULES" ))). Path
}
@ { Name = "Machine" ; Expected = $IsWindows ?
[ Environment ]:: GetFolderPath ( "ProgramFiles" ) + "\PowerShell\Modules" :
( Resolve-Path ([ System.Management.Automation.Platform ]:: SelectProductNameForDirectory ( "SHARED_MODULES" ))). Path
}
@ { Name = "Builtin" ; Expected = ( Resolve-Path ( Join-Path $PSHOME Modules )). Path }
)
# resolve the paths to ensure they are in the correct format
$currentModulePathElements = $env:PSModulePath -split [ System.IO.Path ]:: PathSeparator | Foreach-Object { ( Resolve-Path $_ ). Path }
}
AfterAll {
Unregister-PSRepository -Name $localSourceName -ErrorAction SilentlyContinue
}
It "The value '<Name>' should return the proper value" -testcase $testCases {
param ( $Name , $Expected )
$result = [ System.Management.Automation.ModuleIntrinsics ]:: GetPSModulePath ( $name )
$result | Should -not -BeNullOrEmpty
# spot check pshome, the user and shared paths may not be present
if ( $name -eq "PSHOME" ) {
$result | Should -Be $Expected
}
}
It "The current module path should contain the expected paths for '<Name>'" -testcase $testCases {
param ( $Name , $Expected )
$mPath = ( Resolve-Path ([ System.Management.Automation.ModuleIntrinsics ]:: GetPSModulePath ( $name ))). Path
$currentModulePathElements | Should -Contain $mPath
}
}
2023-08-01 04:00:27 +10:00
Describe 'ModuleIntrinsics.GetModulePath environment interaction' -tag @ ( 'CI' ) {
It "GetModulePath does not crash when MyDocuments special folder is empty" {
try {
[ System.Management.Automation.Internal.InternalTestHooks ]:: SetTestHook ( 'SetMyDocumentsSpecialFolderToBlank' , $true )
[ System.Management.Automation.ModuleIntrinsics ]:: GetModulePath ( $null , $null , $null )
}
finally {
[ System.Management.Automation.Internal.InternalTestHooks ]:: SetTestHook ( 'SetMyDocumentsSpecialFolderToBlank' , $false )
}
}
It "GetModulePath does not crash when MyDocuments special folder is not empty" {
[ System.Management.Automation.ModuleIntrinsics ]:: GetModulePath ( $null , $null , $null )
}
}