8000 Fixed passing common test modules path to unelevated Powershell by adityapatwardhan · Pull Request #4313 · PowerShell/PowerShell · GitHub
[go: up one dir, main page]

Skip to content

Fixed passing common test modules path to unelevated Powershell #4313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
param(
[Parameter(Mandatory = $true, Position = 0)] $coverallsToken,
[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.
Expand Down Expand Up @@ -168,22 +169,27 @@ try
Install-OpenCover -TargetDirectory $openCoverTargetDirectory -force
Write-LogPassThru -Message "OpenCover installed."

Write-LogPassThru -Message "TestDirectory : $testPath"
Write-LogPassThru -Message "TestPath : $testPath"
Write-LogPassThru -Message "openCoverPath : $openCoverTargetDirectory\OpenCover"
Write-LogPassThru -Message "psbinpath : $psBinPath"
Write-LogPassThru -Message "elevatedLog : $elevatedLogs"
Write-LogPassThru -Message "unelevatedLog : $unelevatedLogs"
Write-LogPassThru -Message "TestToolsPath : $testToolsPath"

$openCoverParams = @{outputlog = $outputLog;
TestDirectory = $testPath;
TestPath = $testPath;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since these hashtable entries are on separate lines, the ; is not really needed. I don't think it's a big deal, but something to watch in future

OpenCoverPath = "$openCoverTargetDirectory\OpenCover";
PowerShellExeDirectory = "$psBinPath";
PesterLogElevated = $elevatedLogs;
PesterLogUnelevated = $unelevatedLogs;
TestToolsModulesPath = "$testToolsPath\Modules";
}

if($SuppressQuiet)
{
$openCoverParams.Add('SuppressQuiet', $true)
}

$openCoverParams | Out-String | Write-LogPassThru
Write-LogPassThru -Message "Starting test run."

Expand Down
106 changes: 66 additions & 40 deletions test/tools/OpenCover/OpenCover.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ function Get-CodeCoverageChange($r1, $r2, [string[]]$ClassName)

if ( $ClassName ) {
foreach ( $Class in $ClassName ) {
$c1 = $r1.Assembly.ClassCoverage | ?{$_.ClassName -eq $Class }
$c2 = $r2.Assembly.ClassCoverage | ?{$_.ClassName -eq $Class }
$c1 = $r1.Assembly.ClassCoverage | Where-Object {$_.ClassName -eq $Class }
$c2 = $r2.Assembly.ClassCoverage | Where-Object {$_.ClassName -eq $Class }
$ClassCoverageChange = [pscustomobject]@{
ClassName = $Class
Branch = $c2.Branch
Expand Down Expand Up @@ -136,7 +136,7 @@ function Get-CoverageData($xmlPath)
Assembly = $assemblies
}
$CoverageData.PSTypeNames.Insert(0,"OpenCover.CoverageData")
Add-Member -InputObject $CoverageData -MemberType ScriptMethod -Name GetClassCoverage -Value { param ( $name ) $this.assembly.classcoverage | ?{$_.classname -match $name } }
Add-Member -InputObject $CoverageData -MemberType ScriptMethod -Name GetClassCoverage -Value { param ( $name ) $this.assembly.classcoverage | Where-Object {$_.classname -match $name } }
$null = $CoverageXml

## Adding explicit garbage collection as the $CoverageXml object tends to be very large, in order of 1 GB.
Expand Down Expand Up @@ -413,21 +413,22 @@ function Install-OpenCover
.Description
Invoke-OpenCover runs tests under OpenCover by executing tests on PowerShell.exe located at $PowerShellExeDirectory.
.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
{
[CmdletBinding(SupportsShouldProcess=$true)]
param (
[parameter()]$OutputLog = "$home/Documents/OpenCover.xml",
[parameter()]$TestDirectory = "${script:psRepoPath}/test/powershell",
[parameter()]$TestPath = "${script:psRepoPath}/test/powershell",
[parameter()]$OpenCoverPath = "$home/OpenCover",
[parameter()]$PowerShellExeDirectory = "${script:psRepoPath}/src/powershell-win-core/bin/CodeCoverage/netcoreapp2.0/win10-x64/publish",
[parameter()]$PesterLogElevated = "$pwd/TestResultsElevated.xml",
[parameter()]$PesterLogUnelevated = "$pwd/TestResultsUnelevated.xml",
[parameter()]$PesterLogFormat = "NUnitXml",
[parameter()]$TestToolsModulesPath = "${script:psRepoPath}/test/tools/Modules",
[switch]$CIOnly
[switch]$CIOnly,
[switch]$SuppressQuiet
)

# check for elevation
Expand Down Expand Up @@ -462,8 +463,10 @@ function Invoke-OpenCover

# create the arguments for OpenCover

$startupArgs = 'Set-ExecutionPolicy Bypass -Force -Scope Process;'
$targetArgs = "-c","${startupArgs}", "Invoke-Pester","${TestDirectory}"
$updatedEnvPath = "${PowerShellExeDirectory}\Modules;$TestToolsModulesPath"

$startupArgs = "Set-ExecutionPolicy Bypass -Force -Scope Process; `$env:PSModulePath = '${updatedEnvPath}';"
$targetArgs = "${startupArgs}", "Invoke-Pester","${TestPath}","-OutputFormat $PesterLogFormat"

if ( $CIOnly )
{
Expand All @@ -476,46 +479,40 @@ function Invoke-OpenCover
$targetArgsUnelevated = $targetArgs + @("-excludeTag @('RequireAdminOnWindows')")
}

$targetArgsElevated += @("-OutputFile $PesterLogElevated", "-OutputFormat $PesterLogFormat", "-Quiet")
$targetArgsUnelevated += @("-OutputFile $PesterLogUnelevated", "-OutputFormat $PesterLogFormat", "-Quiet")
$targetArgsElevated += @("-OutputFile $PesterLogElevated")
$targetArgsUnelevated += @("-OutputFile $PesterLogUnelevated")

$targetArgStringElevated = $targetArgsElevated -join " "
$targetArgStringUnelevated = $targetArgsUnelevated -join " "
# the order seems to be important. Always keep -targetargs as the last parameter.
$openCoverArgsElevated = "-target:$target","-register:user","-output:${outputLog}","-nodefaultfilters","-oldstyle","-hideskipped:all","-mergeoutput", "-filter:`"+[*]* -[Microsoft.PowerShell.PSReadLine]*`"", "-targetargs:`"$targetArgStringElevated`""
$openCoverArgsUnelevated = "-target:$target","-register:user","-output:${outputLog}","-nodefaultfilters","-oldstyle","-hideskipped:all", "-mergeoutput", "-filter:`"+[*]* -[Microsoft.PowerShell.PSReadLine]*`"", "-targetargs:`"$targetArgStringUnelevated`""
$openCoverArgsUnelevatedString = $openCoverArgsUnelevated -join " "
if(-not $SuppressQuiet)
{
$targetArgsElevated += @("-Quiet")
$targetArgsUnelevated += @("-Quiet")
}

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
{
# 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
& $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
"$openCoverBin $openCoverArgsUnelevatedString" | Out-File -FilePath "$env:temp\unelevated.ps1" -Force
runas.exe /trustlevel:0x20000 " 8000 ;powershell.exe -file $env:temp\unelevated.ps1"
# wait for process to start
Start-Sleep -Seconds 5
$unelevatedFile = "$env:temp\unelevated.ps1"
"$openCoverBin $cmdlineUnelevated" | Out-File -FilePath $unelevatedFile -Force
runas.exe /trustlevel:0x20000 "powershell.exe -file $unelevatedFile"
# poll for process exit every 60 seconds
# 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)

$openCoverExited = $false

while([datetime]::Now -lt $timeOut)
{
Start-Sleep -Seconds 60
Expand All @@ -524,15 +521,44 @@ function Invoke-OpenCover
if(-not $openCoverProcess)
{
#run must have completed.
$openCoverExited = $true
break
}
}

if(-not $openCoverExited)
{
throw "Opencover has not exited in 6 hours"
}
}
finally
{
# set it back
$env:PSModulePath = $saveModPath
Remove-Item "$env:temp\unelevated.ps1" -force -ErrorAction SilentlyContinue
Remove-Item $elevatedFile -force -ErrorAction SilentlyContinue
Remove-Item $unelevatedFile -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

}
0