您的位置:首页 > 运维架构 > Shell

Powershell Unit Test Code Coverage Implementation

2014-09-30 11:00 411 查看
Unit Test framework:
https://github.com/pester/Pester
C:\PS>./bin/pester.bat

just run pester.bat and all Unit Tests in *.tests.ps1 will be performed.

Code Coverage Implementation with powershell break point:

1. Line coverage:

function Set-LineBreakPoints

{

param(

[string[]]$scripts,

[HashTable]$profiler

)

foreach($script in $scripts)

{

Set-LineBreakPointFor1Script -scriptFullName $script -profiler $profiler

}

}

function Set-LineBreakPointFor1Script

{

param(

[string]$scriptFullName,

[HashTable]$profiler

)

if ($profiler.ContainsKey($key_lineCoverage) -eq $false)

{

$profiler.$key_lineCoverage = @{}

}

$profiler.$key_lineCoverage.$scriptFullName = @{}

$profiler.$key_lineCoverage.$scriptFullName."CoveredLineCount" = 0

$profiler.$key_lineCoverage.$scriptFullName."TotalLineCount" = 0

$profiler.$key_lineCoverage.$scriptFullName."CoveredLineNumbers" = @()

$scriptCode = Get-Content $scriptFullName

$profiler.$key_lineCoverage.$scriptFullName."TotalLineCount" = $scriptCode.Length

for( $i = 1; $i -le $scriptCode.Length; $i++)

{

# Not Work #
$bp = Set-PSBreakpoint -Script $scriptFullName -Line $i -Action ([ScriptBlock]::Create("`$global:profiler.`"$key_lineCoverage`".`"$scriptFullName`".`"CoveredLineNumbers`" += $i; `$global:profiler.`"$key_lineCoverage`".`"$scriptFullName`".`"CoveredLineCount`"=`$global:profiler.`"$key_lineCoverage`".`"$scriptFullName`".`"CoveredLineNumbers`".Length;
Echo (`"Covered Lines No.: `" + `$global:profiler.`"$key_lineCoverage`".`"$scriptFullName`".`"CoveredLineNumbers`") | Add-Content D:\jobs\log\log0929_2.txt;"))

# works well.
# $bp = Set-PSBreakpoint -Script $scriptFullName -Line $i -Action ([ScriptBlock]::Create("`$global:checkPoint=`"$i+ $scriptFullName`"")) # It

# Not Work #
$bp = Set-PSBreakpoint -Script $scriptFullName -Line $i -Action ([ScriptBlock]::Create("`$global:profiler.`"$key_lineCoverage`".`"$scriptFullName`".`"CoveredLineNumbers`" += $i; `$global:profiler.`"$key_lineCoverage`".`"$scriptFullName`" | Add-Content D:\jobs\log\log0929_2.txt;"))

# Not Work #
$bp = Set-PSBreakpoint -Script $scriptFullName -Line $i -Action ([ScriptBlock]::Create("`$global:profiler.`"$key_lineCoverage`".`"$scriptFullName`".`"CoveredLineNumbers`" += $i"))

Set-PSBreakpoint -Script $scriptFullName -Line $i -Action ([ScriptBlock]::Create("`"$scriptFullName,$i`" | Add-Content -path $coverageCsv")) | Out-Null

}

Write-Output "Finished setting break point for $scriptFullName ."

}

function GenerateHtmlReport1Script

{

param(

[string]$scriptName,

[HashTable]$coverageData,

[string]$reportFile

)

$coveredBG = "LightGreen"

$missedBG = "DarkSalmon"

if (-not (Test-Path $scriptName -PathType Leaf))

{

Write-Error "Could not found script $scriptName!!!"

return

}

$scriptFullName = (Get-Item $scriptName).FullName

$scriptCode = Get-Content $scriptFullName

$scriptLineCount = $scriptCode.Length

Echo "Generating html report for $scriptFullName"

$key_lineCoverage = [CoverageType]::LineCoverage -as [string]

$key_funcCoverage = [CoverageType]::FunctionCoverage -as [string]

if ((-not $coverageData.Contains($key_lineCoverage)) -and (-not $coverageData.Contains($key_funcCoverage)))

{

Write-Error "Line coverage data and function coverage data is not found, pls check!!!"

return

}

else

{

if ($coverageData.Contains($key_lineCoverage))

{

if (-not $coverageData.$key_lineCoverage.Contains($scriptFullName))

{

Write-Warning "No line coverage data is available for script $scriptFullName !!!"

$coveredLines = @()

#return

}

else

{

$coveredLines = @($coverageData.$key_lineCoverage.$scriptFullName."CoveredLineNumbers" | Sort-Object -Unique)

Echo "$scriptFullName coveredLines is : $coveredLines"

}

$coveredLineCount = $coveredLines.Length

}

else

{

Write-Warning "No line coverage data available."

}

if ($coverageData.Contains($key_funcCoverage))

{

if (-not $coverageData.$key_funcCoverage.Contains($scriptFullName))

{

Write-Warning "No function coverage data is available for script $scriptFullName !!!"

$coveredFunctions = @()

}

else

{

$coveredFunctions = @($coverageData.$key_funcCoverage.$scriptFullName | Sort-Object -Unique)

}

$coveredFunctionCount = $coveredFunctions.Length

}

else

{

Write-Warning "No function coverage data available."

}

}

if ($scriptLineCount -eq 0)

{

Write-Warning "Empty script - $scriptFullName !!!"

$lineCoverageRate = "0 %"

}

else

{

$lineCoverageRate = ($coveredLineCount / $scriptLineCount).ToString("P2")

}

$totalFunctionCount = $coverageData.$key_funcCoverage."FunctionCount".$scriptFullName

if ($totalFunctionCount -eq 0 -or $totalFunctionCount -eq $null)

{

Write-Warning "There are no function in script $scriptFullName ..."

$functionCoverageRate = "0 %"

}

else

{

$functionCoverageRate = ($coveredFunctionCount / $totalFunctionCount).ToString("P2")

}

$backgroundColors = 1..($scriptCode.Length)

for($i = 0; $i -lt $scriptLineCount; $i++)

{

if ($coveredLines -contains ($i + 1))

{

$backgroundColors[$i] = $coveredBG

}

else

{

$backgroundColors[$i] = $missedBG

}

}

if ($scriptLineCount -ne $backgroundColors.Length)

{

Write-Error "scriptCode.Length should be same as colorSetting.Length !!!"

return

}

Add-Content "<style>

table,th,td

{

border:1px solid black;

border-collapse:collapse

}

th

{

text-align: left;

background-color: Azure;

}

</style>

<Table Width=80% Align='Center' Border='1,1,1,1'>

<tr>

<th colspan='2'>Code Location: $scriptFullName, <BR>=== Line Coverage Rate: $lineCoverageRate <BR>=== Covered Line Count: $coveredLineCount <BR>=== Total Line Count: $scriptLineCount <BR>=== Function Coverage Rate: $functionCoverageRate
<BR>=== Covered Functions Count: $coveredFunctionCount <BR>=== Total Function Count: $totalFunctionCount</th>

</tr>

" -Path $reportFile

for($i = 0; $i -lt $scriptLineCount; $i++)

{

Echo ("<TR><TD bgcolor='" + $backgroundColors[$i] + "'>" + ($i + 1) + "</TD><TD>" + $scriptCode[$i].Replace(" ", " ").Replace("`t","    ") + "</TD></TR>") | Add-Content $reportFile

}

Add-Content -Value "</Table><BR>" -Path $reportFile

Write-Output "Completed processing $scriptName !"

Write-Output "--> Generated $reportFile !!!"

}

function GenerateMultipleHtmlReport

{

param(

[string[]]$scriptCollection,

[HashTable]$coverageData,

[string]$reportFolder

)

foreach($script in $scriptCollection)

{

$name = (Get-Item $script).BaseName

GenerateHtmlReport1Script -scriptName $script -coverageData $coverageData -reportFile $reportFolder\${name}.html

}

Write-Output "==> Finished generating all html reports!!!"

}

function GenerateOverviewReport

{

param(

[HashTable]$coverageData,

[string]$reportFolder

)

$totalLinesCountAll = 0

$totalCoveredLinesCount = 0

$reportFile = "$reportFolder\CoverageReport.html"

$style = "<style>

table

{

margin:0 auto;

width:80%;

}

table,th,td

{

border:1px solid black;

border-collapse:collapse

}

th

{

text-align: left;

background-color: Azure;

}

a

{

color:black

}

</style>

"

"<table>" | Add-Content $reportFile

foreach($script in $profiler.LineCoverage.Keys)

{

$fileName = Split-Path $script -Leaf

if ($fileName -ne $null -and $fileName.Trim() -ne "")

{

$fileName = $fileName.Replace('.ps1','')

}

else

{

"fileName is empty!!!!!!"

}

$totalLinesCountAll += $profiler.LineCoverage."$script".TotalLineCount;

$totalCoveredLinesCount += $profiler.LineCoverage."$script".CoveredLineCount;

$coverageNumber = $profiler.LineCoverage."$script".CoveredLineCount/$profiler.LineCoverage."$script".TotalLineCount

$coveragePercent = $coverageNumber

if ($coveragePercent -gt 0)

{

$coverText = ("<B>"+ $coveragePercent.ToString("P2") + "</B>")

}

else

{

$coverText = $coveragePercent.ToString("P2")

}

("<tr><td><a href='.\${fileName}.html' target='new'>$script</a></td><td>$coverText</td></tr>") | Add-Content $reportFile

}

"</table>" | Add-Content $reportFile

$reportBody = Get-Content "$reportFile"

$reportHeader = ("<table><th colspan=2><B>OverAll Coverage Percentage: " + ($totalCoveredLinesCount/$totalLinesCountAll).ToString("P2") + "</B></th></table>")

$style + $reportHeader + $reportBody | Set-Content $reportFile

Write-Output "==> Finished generating overview report!!"

}

# Main Process Starts here.

if ("CoverageType" -as [Type])

{

Echo "Type CoverageType already exists, need open a new session to resolve this."

}

else

{

Add-Type -TypeDefinition @"

public enum CoverageType

{

LineCoverage = 0,

FunctionCoverage = 1,

BranchCoverage = 2

}

"@

}

$key_lineCoverage = [CoverageType]::LineCoverage -as [string] # convert to string type.

$key_funcCoverage = [CoverageType]::FunctionCoverage -as [string] # convert to string type.

$profiler = @{}

$files = Get-ChildItem -Recurse -Path "$workSpaceRoot\ToBeTested\" | Where-Object { $_.Extension -eq ".ps1" } | % { $_.FullName }

Set-LineBreakPoints -scripts $files -profiler $profiler

$timestamp = Get-Date -f yyyyMMddhhmmss

$coverageCsv = "$workSpaceRoot\coverage.${timestamp}.csv"

### Perform UnitTest here.

#......

### Perform UnitTest here.

$reportFolder = "$workSpaceRoot\CoverageReports\"

[void](New-Item $reportFolder -ItemType container)

$csvObj = Import-Csv -Path $coverageCsv -Header "script","lineNo"

foreach($scriptFullName in $files)

{

"Checking $scriptFullName ..."

if ($profiler.ContainsKey($key_lineCoverage) -eq $false)

{

$profiler.$key_lineCoverage = @{}

}

$profiler.$key_lineCoverage.$scriptFullName = @{}

$profiler.$key_lineCoverage.$scriptFullName."CoveredLineCount" = 0

$profiler.$key_lineCoverage.$scriptFullName."TotalLineCount" = 0

$profiler.$key_lineCoverage.$scriptFullName."CoveredLineNumbers" = @()

$profiler.$key_lineCoverage.$scriptFullName."TotalLineCount" = (@(Get-Content $scriptFullName)).Length

foreach($entry in ($csvObj | Where-Object { $_.script -eq "$scriptFullName" }))

{

if ($profiler.$key_lineCoverage.$scriptFullName."CoveredLineNumbers" -contains $entry.lineNo)

{

"line $entry.lineNo already added, skipped."

}

else

{

$profiler.$key_lineCoverage.$scriptFullName."CoveredLineNumbers" += $entry.lineNo

$profiler.$key_lineCoverage.$scriptFullName."CoveredLineCount" = $profiler.$key_lineCoverage.$scriptFullName."CoveredLineNumbers".Length

}

}

}

GenerateMultipleHtmlReport -scriptCollection $files -coverageData $profiler -reportFolder $reportFolder

GenerateOverviewReport -coverageData $profiler -reportFolder $reportFolder

2. Function coverage

(

This is not perfect!

a. some script failed with when invoking ([ScriptBlock]::Create($scriptCode), should be defect of Powershell

b. when we invoke New-Module cmdlet, we actually run the script, this is not what we want, we actually need static code analysis and not run it.

)

$scriptCode = Get-Content $scriptFullName

$tempModule = New-Module -ScriptBlock ([ScriptBlock]::Create($scriptCode))

$functionsList = $tempModule.ExportedFunctions.Keys

foreach( $f in $functionsList )

{

[void](Set-PSBreakpoint -Script $scriptFullName -Command $f -Action ([ScriptBlock]::Create("`$global:profiler.`"$key_funcCoverage`".`"$scriptFullName`" += `"$f`"")))

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: