From 5ebfa56c05a3479f37758d3d5ed6203741925fdd Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Mon, 19 Jun 2017 13:45:48 -0700 Subject: [PATCH 1/2] Fixed OpenCover module and CodeCoverage launcher script * Changes to layout of package caused some changes to package path. * Added Test modules from tests\tools\modules * Fixed Get-ChildItem test * Added convertor for converting OpenCover output file to JSON. * Updated how the file is uploaded to CodeCov.io --- .../Get-ChildItem.Tests.ps1 | 6 +- .../Start-CodeCoverageRun.ps1 | 122 +++++++++++++++--- test/tools/OpenCover/OpenCover.psm1 | 22 ++-- 3 files changed, 121 insertions(+), 29 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 index d3964dc8dc1..aca63839a86 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 @@ -60,11 +60,11 @@ Describe "Get-ChildItem" -Tags "CI" { $files.Count | Should be 1 $files[0].Name | Should Be $item_F } - It "Should give .sys file if the fullpath is specified with hidden and force parameter" -Skip:(!$IsWindows){ - $file = Get-ChildItem -path "$env:SystemDrive\\pagefile.sys" -Hidden + It "Should find the hidden file if specified with hidden switch" -Skip:(!$IsWindows){ + $file = Get-ChildItem -Path (Join-Path $TestDrive $item_F) -Hidden $file | Should not be $null $file.Count | Should be 1 - $file.Name | Should be "pagefile.sys" + $file.Name | Should be $item_F } It "Should continue enumerating a directory when a contained item is deleted" { $Error.Clear() diff --git a/test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1 b/test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1 index cb259c267ff..7de4f592578 100644 --- a/test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1 +++ b/test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1 @@ -4,6 +4,97 @@ [Parameter(Position = 2)] $azureLogDrive = "L:\" ) +# Read the XML and create a dictionary for FileUID -> file full path. +function GetFileTable() +{ + $files = $script:covData | Select-Xml './/File' + foreach($file in $files) + { + $script:fileTable[$file.Node.uid] = $file.Node.fullPath + } +} + +# Get sequence points for a particular file +function GetSequencePointsForFile([string] $fileId) +{ + $lineCoverage = @{} + $sequencePoints = $script:covData | Select-Xml ".//SequencePoint[@fileid = `"$fileId`"]" + + if($sequencePoints.Count -gt 0) + { + foreach($sp in $sequencePoints) + { + $visitedCount = $sp.Node.vc + $lineNumber = $sp.Node.sl + + ##If this line has already been hit, add the hit count. + if($lineCoverage.Contains($lineNumber)) + { + $lineCoverage[$lineNumber] += [int]::Parse($visitedCount) + } + else ## This line has been hit for the first time, ceate an entry in $lineCoverage + { + $lineCoverage.Add($lineNumber, [int]::Parse($visitedCount)) + } + } + + return $lineCoverage + } +} + +#### Convert the OpenCover XML output for CodeCov.io JSON format as it is smaller. +function ConvertTo-CodeCovJson +{ + param( + [string] $Path, + [string] $DestinationPath + ) + + $Script:fileTable = [ordered]@{} + $Script:covData = [xml] (Get-Content -ReadCount 0 -Raw -Path $Path) + $totalCoverage = [PSCustomObject]::new() + $totalCoverage | Add-Member -MemberType NoteProperty -Name "coverage" -Value ([PSCustomObject]::new()) + + ## Populate the dictionary with file uid and file names. + GetFileTable + $keys = $Script:fileTable.Keys + $progress=0 + foreach($f in $keys) + { + Write-Progress -Id 1 -Activity "Converting to JSON" -Status 'Converting' -PercentComplete ($progress * 100 / $keys.Count) + $fileCoverage = GetSequencePointsForFile -fileId $f + $fileName = $Script:fileTable[$f] + $previousFileCoverage = $totalCoverage.coverage.${fileName} + + ##Update the values for the lines in the file. + if($previousFileCoverage -ne $null) + { + foreach($lineNumber in $fileCoverage.Keys) + { + if($previousFileCoverage.contains($lineNumber)) + { + ## if this line has been hit before add the new hit count. + $previousFileCoverage[$lineNumber] += [int]::Parse($fileCoverage[$lineNumber]) + } + else + { + $previousFileCoverage[$lineNumber] = [int]::Parse($fileCoverage[$lineNumber]) + } + } + } + else ## the file is new, so add the values as a new NoteProperty. + { + $totalCoverage.coverage | Add-Member -MemberType NoteProperty -Value $fileCoverage -Name $fileName + } + + $progress++ + } + + Write-Progress -Id 1 -Completed -Activity "Converting to JSON" + + $totalCoverage | ConvertTo-Json -Depth 5 -Compress | out-file $DestinationPath -Encoding ascii +} + function Write-LogPassThru { Param( @@ -28,19 +119,9 @@ function Push-CodeCovData $url="https://codecov.io" $query = "package=bash-${VERSION}&token=${token}&branch=${Branch}&commit=${CommitID}&build=&build_url=&tag=&slug=&yaml=&service=&flags=&pr=&job=" + $uri = "$url/upload/v2?${query}" + $response = Invoke-WebRequest -Method Post -InFile $file -Uri $uri - $CodeCovHeader = @{ Accept = "text/plain" } - $uri = "$url/upload/v4?${query}" - $response = Invoke-WebRequest -Method POST -Uri $uri -Headers $CodeCovHeader - if ( $response.StatusCode -ne 200 ) - { - Write-LogPassThru -Message "Could not get upload url for request $uri" - throw "Could not get upload url" - } - $uploaduri = $response.content.split("`n")[-1] - - $UploadHeader = @{ "Content-Type" = "text/plain"; "x-amz-acl" = "public-read"; "x-amz-storage-class" = "REDUCED_REDUNDANCY" } - $response = Invoke-WebRequest -Method Put -Uri $uploaduri -InFile $file -Headers $UploadHeader if ( $response.StatusCode -ne 200 ) { Write-LogPassThru -Message "Upload failed for upload uri: $uploaduri" @@ -65,15 +146,15 @@ $outputBaseFolder = "$env:Temp\CC" $null = New-Item -ItemType Directory -Path $outputBaseFolder -Force $openCoverPath = "$outputBaseFolder\OpenCover" -$testPath = "$outputBaseFolder\tests" +$testRootPath = "$outputBaseFolder\tests" +$testPath = "$testRootPath\powershell" $psBinPath = "$outputBaseFolder\PSCodeCoverage" $openCoverTargetDirectory = "$outputBaseFolder\OpenCoverToolset" $outputLog = "$outputBaseFolder\CodeCoverageOutput.xml" -$psCodeZip = "$outputBaseFolder\PSCode.zip" -$psCodePath = "$outputBaseFolder\PSCode" $elevatedLogs = "$outputBaseFolder\TestResults_Elevated.xml" $unelevatedLogs = "$outputBaseFolder\TestResults_Unelevated.xml" -$testToolsPath = "$testPath\tools" +$testToolsPath = "$testRootPath\tools" +$jsonFile = "$outputBaseFolder\CC.json" try { @@ -86,7 +167,7 @@ try Write-LogPassThru -Message "Downloads complete. Starting expansion" Expand-Archive -path "$outputBaseFolder\PSCodeCoverage.zip" -destinationpath "$psBinPath" -Force - Expand-Archive -path "$outputBaseFolder\tests.zip" -destinationpath $testPath -Force + Expand-Archive -path "$outputBaseFolder\tests.zip" -destinationpath $testRootPath -Force Expand-Archive -path "$outputBaseFolder\OpenCover.zip" -destinationpath $openCoverPath -Force ## Download Coveralls.net uploader @@ -165,7 +246,8 @@ try & $coverallsExe """$coverallsParams""" Write-LogPassThru -Message "Uploading to CodeCov" - Push-CodeCovData -file $outputLog -CommitID $commitId -token $codecovToken -Branch 'master' + ConvertTo-CodeCovJson -Path $outputLog -DestinationPath $jsonFile + Push-CodeCovData -file $jsonFile -CommitID $commitId -token $codecovToken -Branch 'master' Write-LogPassThru -Message "Upload complete." } @@ -192,5 +274,9 @@ finally ## Disable the cleanup till we stabilize. #Remove-Item -recurse -force -path $outputBaseFolder + + ## This is required so we do not keep on merging coverage reports. + Remove-Item $outputLog -Force -ErrorAction SilentlyContinue + $ErrorActionPreference = $oldErrorActionPreference } diff --git a/test/tools/OpenCover/OpenCover.psm1 b/test/tools/OpenCover/OpenCover.psm1 index adf03f7e73f..0ab2e5d4b7d 100644 --- a/test/tools/OpenCover/OpenCover.psm1 +++ b/test/tools/OpenCover/OpenCover.psm1 @@ -455,7 +455,7 @@ function Invoke-OpenCover # create the arguments for OpenCover - $startupArgs = '$env:PSModulePath = "${env:PSModulePath};$TestToolsPath";Set-ExecutionPolicy Bypass -Force -Scope Process;' + $startupArgs = 'Set-ExecutionPolicy Bypass -Force -Scope Process;' $targetArgs = "-c","${startupArgs}", "Invoke-Pester","${TestDirectory}" if ( $CIOnly ) @@ -486,10 +486,16 @@ function Invoke-OpenCover # 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" - if ( ! (test-path $env:PSModulePath) ) + $env:PSModulePath = "${PowerShellExeDirectory}\Modules;$TestToolsModulesPath" + + $modulePathParts = $env:PSModulePath -split ';' + + foreach($part in $modulePathParts) { - throw "${env:PSModulePath} does not exist" + if ( ! (test-path $part) ) + { + throw "${part} does not exist" + } } # invoke OpenCover elevated @@ -500,12 +506,12 @@ function Invoke-OpenCover runas.exe /trustlevel:0x20000 "powershell.exe -file $env:temp\unelevated.ps1" # wait for process to start Start-Sleep -Seconds 5 - # poll for process exit every 30 seconds - # timeout of 3 hours - $timeOut = ([datetime]::Now).AddHours(3) + # poll for process exit every 60 seconds + # timeout of 6 hours + $timeOut = ([datetime]::Now).AddHours(6) while([datetime]::Now -lt $timeOut) { - Start-Sleep -Seconds 30 + Start-Sleep -Seconds 60 $openCoverProcess = Get-Process "OpenCover.Console" -ErrorAction SilentlyContinue if(-not $openCoverProcess) From 0450416c01c2de609b00b43dd42c5243217467ef Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Mon, 19 Jun 2017 16:58:27 -0700 Subject: [PATCH 2/2] Addressed code review comments --- .../Get-ChildItem.Tests.ps1 | 4 +-- .../Start-CodeCoverageRun.ps1 | 30 +++++-------------- test/tools/OpenCover/OpenCover.psm1 | 10 +++++++ 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 index aca63839a86..b9d6ccb1c9f 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-ChildItem.Tests.ps1 @@ -60,9 +60,9 @@ Describe "Get-ChildItem" -Tags "CI" { $files.Count | Should be 1 $files[0].Name | Should Be $item_F } - It "Should find the hidden file if specified with hidden switch" -Skip:(!$IsWindows){ + It "Should find the hidden file if specified with hidden switch" { $file = Get-ChildItem -Path (Join-Path $TestDrive $item_F) -Hidden - $file | Should not be $null + $file | Should Not BeNullOrEmpty $file.Count | Should be 1 $file.Name | Should be $item_F } diff --git a/test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1 b/test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1 index 7de4f592578..9c3f24722f1 100644 --- a/test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1 +++ b/test/tools/CodeCoverageAutomation/Start-CodeCoverageRun.ps1 @@ -17,25 +17,17 @@ function GetFileTable() # Get sequence points for a particular file function GetSequencePointsForFile([string] $fileId) { - $lineCoverage = @{} - $sequencePoints = $script:covData | Select-Xml ".//SequencePoint[@fileid = `"$fileId`"]" + $lineCoverage = [System.Collections.Generic.Dictionary[string,int]]::new() + + $sequencePoints = $script:covData | Select-Xml ".//SequencePoint[@fileid = '$fileId']" if($sequencePoints.Count -gt 0) { foreach($sp in $sequencePoints) { - $visitedCount = $sp.Node.vc - $lineNumber = $sp.Node.sl - - ##If this line has already been hit, add the hit count. - if($lineCoverage.Contains($lineNumber)) - { - $lineCoverage[$lineNumber] += [int]::Parse($visitedCount) - } - else ## This line has been hit for the first time, ceate an entry in $lineCoverage - { - $lineCoverage.Add($lineNumber, [int]::Parse($visitedCount)) - } + $visitedCount = [int]::Parse($sp.Node.vc) + $lineNumber = [int]::Parse($sp.Node.sl) + $lineCoverage[$lineNumber] += [int]::Parse($visitedCount) } return $lineCoverage @@ -71,15 +63,7 @@ function ConvertTo-CodeCovJson { foreach($lineNumber in $fileCoverage.Keys) { - if($previousFileCoverage.contains($lineNumber)) - { - ## if this line has been hit before add the new hit count. - $previousFileCoverage[$lineNumber] += [int]::Parse($fileCoverage[$lineNumber]) - } - else - { - $previousFileCoverage[$lineNumber] = [int]::Parse($fileCoverage[$lineNumber]) - } + $previousFileCoverage[$lineNumber] += [int]::Parse($fileCoverage[$lineNumber]) } } else ## the file is new, so add the values as a new NoteProperty. diff --git a/test/tools/OpenCover/OpenCover.psm1 b/test/tools/OpenCover/OpenCover.psm1 index 0ab2e5d4b7d..4e167126763 100644 --- a/test/tools/OpenCover/OpenCover.psm1 +++ b/test/tools/OpenCover/OpenCover.psm1 @@ -433,6 +433,16 @@ function Invoke-OpenCover [switch]$CIOnly ) + # check for elevation + $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $principal = New-Object System.Security.Principal.WindowsPrincipal($identity) + $isElevated = $principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator) + + if(-not $isElevated) + { + throw 'Please run from an elevated PowerShell.' + } + # check to be sure that OpenCover is present $OpenCoverBin = "$OpenCoverPath\opencover.console.exe"