Fixed passing common test modules path to unelevated Powershell (#4313)
- Fixed the way common test modules are passed to elevated and unelevated powershell. Earlier, only elevated powershell got those through inheritance as a child process. Now we add them to the startup of the process. - Fixed error reported by PSScriptAnalyzer about ? / Where-Object - Converted all the parameters passed to powershell.exe to be a base64 encoded string to avoid complications with quotes. - Removed code which was updated $env:PSModulePath as we do it in startup args for powershell process instead. - Added a way to disable -Quiet for Pester. - Opencover.console.exe gets confused when the base64 encoded parameter is given with '&' invoke. Writing to a ps1 file and invoking the script works around the issue. This also makes it similar to how unelevated tests are invoked.
This commit is contained in:
parent
fc77c7981a
commit
5cd0e85d12
2 changed files with 72 additions and 40 deletions
|
@ -1,7 +1,8 @@
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory = $true, Position = 0)] $coverallsToken,
|
[Parameter(Mandatory = $true, Position = 0)] $coverallsToken,
|
||||||
[Parameter(Mandatory = $true, Position = 1)] $codecovToken,
|
[Parameter(Mandatory = $true, Position = 1)] $codecovToken,
|
||||||
[Parameter(Position = 2)] $azureLogDrive = "L:\"
|
[Parameter(Position = 2)] $azureLogDrive = "L:\",
|
||||||
|
[switch] $SuppressQuiet
|
||||||
)
|
)
|
||||||
|
|
||||||
# Read the XML and create a dictionary for FileUID -> file full path.
|
# Read the XML and create a dictionary for FileUID -> file full path.
|
||||||
|
@ -168,7 +169,7 @@ try
|
||||||
Install-OpenCover -TargetDirectory $openCoverTargetDirectory -force
|
Install-OpenCover -TargetDirectory $openCoverTargetDirectory -force
|
||||||
Write-LogPassThru -Message "OpenCover installed."
|
Write-LogPassThru -Message "OpenCover installed."
|
||||||
|
|
||||||
Write-LogPassThru -Message "TestDirectory : $testPath"
|
Write-LogPassThru -Message "TestPath : $testPath"
|
||||||
Write-LogPassThru -Message "openCoverPath : $openCoverTargetDirectory\OpenCover"
|
Write-LogPassThru -Message "openCoverPath : $openCoverTargetDirectory\OpenCover"
|
||||||
Write-LogPassThru -Message "psbinpath : $psBinPath"
|
Write-LogPassThru -Message "psbinpath : $psBinPath"
|
||||||
Write-LogPassThru -Message "elevatedLog : $elevatedLogs"
|
Write-LogPassThru -Message "elevatedLog : $elevatedLogs"
|
||||||
|
@ -176,7 +177,7 @@ try
|
||||||
Write-LogPassThru -Message "TestToolsPath : $testToolsPath"
|
Write-LogPassThru -Message "TestToolsPath : $testToolsPath"
|
||||||
|
|
||||||
$openCoverParams = @{outputlog = $outputLog;
|
$openCoverParams = @{outputlog = $outputLog;
|
||||||
TestDirectory = $testPath;
|
TestPath = $testPath;
|
||||||
OpenCoverPath = "$openCoverTargetDirectory\OpenCover";
|
OpenCoverPath = "$openCoverTargetDirectory\OpenCover";
|
||||||
PowerShellExeDirectory = "$psBinPath";
|
PowerShellExeDirectory = "$psBinPath";
|
||||||
PesterLogElevated = $elevatedLogs;
|
PesterLogElevated = $elevatedLogs;
|
||||||
|
@ -184,6 +185,11 @@ try
|
||||||
TestToolsModulesPath = "$testToolsPath\Modules";
|
TestToolsModulesPath = "$testToolsPath\Modules";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($SuppressQuiet)
|
||||||
|
{
|
||||||
|
$openCoverParams.Add('SuppressQuiet', $true)
|
||||||
|
}
|
||||||
|
|
||||||
$openCoverParams | Out-String | Write-LogPassThru
|
$openCoverParams | Out-String | Write-LogPassThru
|
||||||
Write-LogPassThru -Message "Starting test run."
|
Write-LogPassThru -Message "Starting test run."
|
||||||
|
|
||||||
|
|
|
@ -413,21 +413,22 @@ function Install-OpenCover
|
||||||
.Description
|
.Description
|
||||||
Invoke-OpenCover runs tests under OpenCover by executing tests on PowerShell.exe located at $PowerShellExeDirectory.
|
Invoke-OpenCover runs tests under OpenCover by executing tests on PowerShell.exe located at $PowerShellExeDirectory.
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
Invoke-OpenCover -TestDirectory $pwd/test/powershell -PowerShellExeDirectory $pwd/src/powershell-win-core/bin/CodeCoverage/netcoreapp1.0/win10-x64
|
Invoke-OpenCover -TestPath $pwd/test/powershell -PowerShellExeDirectory $pwd/src/powershell-win-core/bin/CodeCoverage/netcoreapp1.0/win10-x64
|
||||||
#>
|
#>
|
||||||
function Invoke-OpenCover
|
function Invoke-OpenCover
|
||||||
{
|
{
|
||||||
[CmdletBinding(SupportsShouldProcess=$true)]
|
[CmdletBinding(SupportsShouldProcess=$true)]
|
||||||
param (
|
param (
|
||||||
[parameter()]$OutputLog = "$home/Documents/OpenCover.xml",
|
[parameter()]$OutputLog = "$home/Documents/OpenCover.xml",
|
||||||
[parameter()]$TestDirectory = "${script:psRepoPath}/test/powershell",
|
[parameter()]$TestPath = "${script:psRepoPath}/test/powershell",
|
||||||
[parameter()]$OpenCoverPath = "$home/OpenCover",
|
[parameter()]$OpenCoverPath = "$home/OpenCover",
|
||||||
[parameter()]$PowerShellExeDirectory = "${script:psRepoPath}/src/powershell-win-core/bin/CodeCoverage/netcoreapp2.0/win10-x64/publish",
|
[parameter()]$PowerShellExeDirectory = "${script:psRepoPath}/src/powershell-win-core/bin/CodeCoverage/netcoreapp2.0/win10-x64/publish",
|
||||||
[parameter()]$PesterLogElevated = "$pwd/TestResultsElevated.xml",
|
[parameter()]$PesterLogElevated = "$pwd/TestResultsElevated.xml",
|
||||||
[parameter()]$PesterLogUnelevated = "$pwd/TestResultsUnelevated.xml",
|
[parameter()]$PesterLogUnelevated = "$pwd/TestResultsUnelevated.xml",
|
||||||
[parameter()]$PesterLogFormat = "NUnitXml",
|
[parameter()]$PesterLogFormat = "NUnitXml",
|
||||||
[parameter()]$TestToolsModulesPath = "${script:psRepoPath}/test/tools/Modules",
|
[parameter()]$TestToolsModulesPath = "${script:psRepoPath}/test/tools/Modules",
|
||||||
[switch]$CIOnly
|
[switch]$CIOnly,
|
||||||
|
[switch]$SuppressQuiet
|
||||||
)
|
)
|
||||||
|
|
||||||
# check for elevation
|
# check for elevation
|
||||||
|
@ -462,8 +463,10 @@ function Invoke-OpenCover
|
||||||
|
|
||||||
# create the arguments for OpenCover
|
# create the arguments for OpenCover
|
||||||
|
|
||||||
$startupArgs = 'Set-ExecutionPolicy Bypass -Force -Scope Process;'
|
$updatedEnvPath = "${PowerShellExeDirectory}\Modules;$TestToolsModulesPath"
|
||||||
$targetArgs = "-c","${startupArgs}", "Invoke-Pester","${TestDirectory}"
|
|
||||||
|
$startupArgs = "Set-ExecutionPolicy Bypass -Force -Scope Process; `$env:PSModulePath = '${updatedEnvPath}';"
|
||||||
|
$targetArgs = "${startupArgs}", "Invoke-Pester","${TestPath}","-OutputFormat $PesterLogFormat"
|
||||||
|
|
||||||
if ( $CIOnly )
|
if ( $CIOnly )
|
||||||
{
|
{
|
||||||
|
@ -476,46 +479,40 @@ function Invoke-OpenCover
|
||||||
$targetArgsUnelevated = $targetArgs + @("-excludeTag @('RequireAdminOnWindows')")
|
$targetArgsUnelevated = $targetArgs + @("-excludeTag @('RequireAdminOnWindows')")
|
||||||
}
|
}
|
||||||
|
|
||||||
$targetArgsElevated += @("-OutputFile $PesterLogElevated", "-OutputFormat $PesterLogFormat", "-Quiet")
|
$targetArgsElevated += @("-OutputFile $PesterLogElevated")
|
||||||
$targetArgsUnelevated += @("-OutputFile $PesterLogUnelevated", "-OutputFormat $PesterLogFormat", "-Quiet")
|
$targetArgsUnelevated += @("-OutputFile $PesterLogUnelevated")
|
||||||
|
|
||||||
$targetArgStringElevated = $targetArgsElevated -join " "
|
if(-not $SuppressQuiet)
|
||||||
$targetArgStringUnelevated = $targetArgsUnelevated -join " "
|
{
|
||||||
# the order seems to be important. Always keep -targetargs as the last parameter.
|
$targetArgsElevated += @("-Quiet")
|
||||||
$openCoverArgsElevated = "-target:$target","-register:user","-output:${outputLog}","-nodefaultfilters","-oldstyle","-hideskipped:all","-mergeoutput", "-filter:`"+[*]* -[Microsoft.PowerShell.PSReadLine]*`"", "-targetargs:`"$targetArgStringElevated`""
|
$targetArgsUnelevated += @("-Quiet")
|
||||||
$openCoverArgsUnelevated = "-target:$target","-register:user","-output:${outputLog}","-nodefaultfilters","-oldstyle","-hideskipped:all", "-mergeoutput", "-filter:`"+[*]* -[Microsoft.PowerShell.PSReadLine]*`"", "-targetargs:`"$targetArgStringUnelevated`""
|
}
|
||||||
$openCoverArgsUnelevatedString = $openCoverArgsUnelevated -join " "
|
|
||||||
|
|
||||||
if ( $PSCmdlet.ShouldProcess("$OpenCoverBin $openCoverArgsUnelevated") )
|
$cmdlineElevated = CreateOpenCoverCmdline -target $target -outputLog $OutputLog -targetArgs $targetArgsElevated
|
||||||
|
$cmdlineUnelevated = CreateOpenCoverCmdline -target $target -outputLog $OutputLog -targetArgs $targetArgsUnelevated
|
||||||
|
|
||||||
|
if ( $PSCmdlet.ShouldProcess("$OpenCoverBin $cmdlineUnelevated") )
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
# check to be sure that the module path is present
|
|
||||||
# this isn't done earlier because there's no need to change env:PSModulePath unless we're going to really run tests
|
|
||||||
$saveModPath = $env:PSModulePath
|
|
||||||
$env:PSModulePath = "${PowerShellExeDirectory}\Modules;$TestToolsModulesPath"
|
|
||||||
|
|
||||||
$modulePathParts = $env:PSModulePath -split ';'
|
|
||||||
|
|
||||||
foreach($part in $modulePathParts)
|
|
||||||
{
|
|
||||||
if ( ! (test-path $part) )
|
|
||||||
{
|
|
||||||
throw "${part} does not exist"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# invoke OpenCover elevated
|
# invoke OpenCover elevated
|
||||||
& $OpenCoverBin $openCoverArgsElevated
|
# Write the command line to a file and then invoke file.
|
||||||
|
# '&' invoke caused issues with cmdline parameters for opencover.console.exe
|
||||||
|
$elevatedFile = "$env:temp\elevated.ps1"
|
||||||
|
"$OpenCoverBin $cmdlineElevated" | Out-File -FilePath $elevatedFile -force
|
||||||
|
powershell.exe -file $elevatedFile
|
||||||
|
|
||||||
# invoke OpenCover unelevated and poll for completion
|
# invoke OpenCover unelevated and poll for completion
|
||||||
"$openCoverBin $openCoverArgsUnelevatedString" | Out-File -FilePath "$env:temp\unelevated.ps1" -Force
|
$unelevatedFile = "$env:temp\unelevated.ps1"
|
||||||
runas.exe /trustlevel:0x20000 "powershell.exe -file $env:temp\unelevated.ps1"
|
"$openCoverBin $cmdlineUnelevated" | Out-File -FilePath $unelevatedFile -Force
|
||||||
# wait for process to start
|
runas.exe /trustlevel:0x20000 "powershell.exe -file $unelevatedFile"
|
||||||
Start-Sleep -Seconds 5
|
|
||||||
# poll for process exit every 60 seconds
|
# poll for process exit every 60 seconds
|
||||||
# timeout of 6 hours
|
# timeout of 6 hours
|
||||||
|
# Runs currently take about 2.5 - 3 hours, we picked 6 hours to be substantially larger.
|
||||||
$timeOut = ([datetime]::Now).AddHours(6)
|
$timeOut = ([datetime]::Now).AddHours(6)
|
||||||
|
|
||||||
|
$openCoverExited = $false
|
||||||
|
|
||||||
while([datetime]::Now -lt $timeOut)
|
while([datetime]::Now -lt $timeOut)
|
||||||
{
|
{
|
||||||
Start-Sleep -Seconds 60
|
Start-Sleep -Seconds 60
|
||||||
|
@ -524,15 +521,44 @@ function Invoke-OpenCover
|
||||||
if(-not $openCoverProcess)
|
if(-not $openCoverProcess)
|
||||||
{
|
{
|
||||||
#run must have completed.
|
#run must have completed.
|
||||||
|
$openCoverExited = $true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(-not $openCoverExited)
|
||||||
|
{
|
||||||
|
throw "Opencover has not exited in 6 hours"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
# set it back
|
Remove-Item $elevatedFile -force -ErrorAction SilentlyContinue
|
||||||
$env:PSModulePath = $saveModPath
|
Remove-Item $unelevatedFile -force -ErrorAction SilentlyContinue
|
||||||
Remove-Item "$env:temp\unelevated.ps1" -force -ErrorAction SilentlyContinue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CreateOpenCoverCmdline($target, $outputLog, $targetArgs)
|
||||||
|
{
|
||||||
|
$targetArgString = $targetArgs -join " "
|
||||||
|
|
||||||
|
$bytes = [System.Text.Encoding]::Unicode.GetBytes($targetArgString)
|
||||||
|
$base64targetArgs = [convert]::ToBase64String($bytes)
|
||||||
|
|
||||||
|
# the order seems to be important. Always keep -targetargs as the last parameter.
|
||||||
|
$cmdline = "-target:$target",
|
||||||
|
"-register:user",
|
||||||
|
"-output:${outputLog}",
|
||||||
|
"-nodefaultfilters",
|
||||||
|
"-oldstyle",
|
||||||
|
"-hideskipped:all",
|
||||||
|
"-mergeoutput",
|
||||||
|
"-filter:`"+[*]* -[Microsoft.PowerShell.PSReadLine]*`"",
|
||||||
|
"-targetargs:`"-EncodedCommand $base64targetArgs`""
|
||||||
|
|
||||||
|
$cmdlineAsString = $cmdline -join " "
|
||||||
|
|
||||||
|
return $cmdlineAsString
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue