Functions/TestResults.ps1
function Get-HumanTime($Seconds) { if ($Seconds -gt 0.99) { $time = [math]::Round($Seconds, 2) $unit = 's' } else { $time = [math]::Floor($Seconds * 1000) $unit = 'ms' } return "$time$unit" } function GetFullPath ([string]$Path) { $Folder = & $SafeCommands['Split-Path'] -Path $Path -Parent $File = & $SafeCommands['Split-Path'] -Path $Path -Leaf if ( -not ([String]::IsNullOrEmpty($Folder))) { $FolderResolved = & $SafeCommands['Resolve-Path'] -Path $Folder } else { $FolderResolved = & $SafeCommands['Resolve-Path'] -Path $ExecutionContext.SessionState.Path.CurrentFileSystemLocation } $Path = & $SafeCommands['Join-Path'] -Path $FolderResolved.ProviderPath -ChildPath $File return $Path } function Export-PesterResults { param ( $PesterState, [string] $Path, [string] $Format ) switch -Wildcard ($Format) { '*Xml' { Export-XmlReport -PesterState $PesterState -Path $Path -Format $Format } default { throw "'$Format' is not a valid Pester export format." } } } function Export-XmlReport { param ( [parameter(Mandatory = $true, ValueFromPipeline = $true)] $PesterState, [parameter(Mandatory = $true)] [String] $Path, [parameter(Mandatory = $true)] [ValidateSet('NUnitXml', 'JUnitXml')] [string] $Format ) #the xmlwriter create method can resolve relatives paths by itself. but its current directory might #be different from what PowerShell sees as the current directory so I have to resolve the path beforehand #working around the limitations of Resolve-Path $Path = GetFullPath -Path $Path $settings = & $SafeCommands['New-Object'] -TypeName Xml.XmlWriterSettings -Property @{ Indent = $true NewLineOnAttributes = $false } $xmlFile = $null $xmlWriter = $null try { $xmlFile = [IO.File]::Create($Path) $xmlWriter = [Xml.XmlWriter]::Create($xmlFile, $settings) switch ($Format) { 'NUnitXml' { Write-NUnitReport -XmlWriter $xmlWriter -PesterState $PesterState } 'JUnitXml' { Write-JUnitReport -XmlWriter $xmlWriter -PesterState $PesterState } } $xmlWriter.Flush() $xmlFile.Flush() } finally { if ($null -ne $xmlWriter) { try { $xmlWriter.Close() } catch { } } if ($null -ne $xmlFile) { try { $xmlFile.Close() } catch { } } } } function Write-NUnitReport($PesterState, [System.Xml.XmlWriter] $XmlWriter) { # Write the XML Declaration $XmlWriter.WriteStartDocument($false) # Write Root Element $xmlWriter.WriteStartElement('test-results') Write-NUnitTestResultAttributes @PSBoundParameters Write-NUnitTestResultChildNodes @PSBoundParameters $XmlWriter.WriteEndElement() } function Write-NUnitTestResultAttributes($PesterState, [System.Xml.XmlWriter] $XmlWriter) { $XmlWriter.WriteAttributeString('xmlns', 'xsi', $null, 'http://www.w3.org/2001/XMLSchema-instance') $XmlWriter.WriteAttributeString('xsi', 'noNamespaceSchemaLocation', [Xml.Schema.XmlSchema]::InstanceNamespace , 'nunit_schema_2.5.xsd') $XmlWriter.WriteAttributeString('name', 'Pester') $XmlWriter.WriteAttributeString('total', ($PesterState.TotalCount - $PesterState.SkippedCount)) $XmlWriter.WriteAttributeString('errors', '0') $XmlWriter.WriteAttributeString('failures', $PesterState.FailedCount) $XmlWriter.WriteAttributeString('not-run', '0') $XmlWriter.WriteAttributeString('inconclusive', $PesterState.PendingCount + $PesterState.InconclusiveCount) $XmlWriter.WriteAttributeString('ignored', $PesterState.SkippedCount) $XmlWriter.WriteAttributeString('skipped', '0') $XmlWriter.WriteAttributeString('invalid', '0') $date = & $SafeCommands['Get-Date'] $XmlWriter.WriteAttributeString('date', (& $SafeCommands['Get-Date'] -Date $date -Format 'yyyy-MM-dd')) $XmlWriter.WriteAttributeString('time', (& $SafeCommands['Get-Date'] -Date $date -Format 'HH:mm:ss')) } function Write-NUnitTestResultChildNodes($PesterState, [System.Xml.XmlWriter] $XmlWriter) { Write-NUnitEnvironmentInformation @PSBoundParameters Write-NUnitCultureInformation @PSBoundParameters $suiteInfo = Get-TestSuiteInfo -TestSuite $PesterState -TestSuiteName $PesterState.TestSuiteName $XmlWriter.WriteStartElement('test-suite') Write-NUnitTestSuiteAttributes -TestSuiteInfo $suiteInfo -XmlWriter $XmlWriter $XmlWriter.WriteStartElement('results') foreach ($action in $PesterState.TestActions.Actions) { Write-NUnitTestSuiteElements -XmlWriter $XmlWriter -Node $action } $XmlWriter.WriteEndElement() $XmlWriter.WriteEndElement() } function Write-NUnitEnvironmentInformation($PesterState, [System.Xml.XmlWriter] $XmlWriter) { $XmlWriter.WriteStartElement('environment') $environment = Get-RunTimeEnvironment foreach ($keyValuePair in $environment.GetEnumerator()) { if ($keyValuePair.Name -eq 'junit-version') { continue } $XmlWriter.WriteAttributeString($keyValuePair.Name, $keyValuePair.Value) } $XmlWriter.WriteEndElement() } function Write-NUnitCultureInformation($PesterState, [System.Xml.XmlWriter] $XmlWriter) { $XmlWriter.WriteStartElement('culture-info') $XmlWriter.WriteAttributeString('current-culture', ([System.Threading.Thread]::CurrentThread.CurrentCulture).Name) $XmlWriter.WriteAttributeString('current-uiculture', ([System.Threading.Thread]::CurrentThread.CurrentUiCulture).Name) $XmlWriter.WriteEndElement() } function Write-NUnitTestSuiteElements($Node, [System.Xml.XmlWriter] $XmlWriter, [string] $Path) { $suiteInfo = Get-TestSuiteInfo $Node $XmlWriter.WriteStartElement('test-suite') Write-NUnitTestSuiteAttributes -TestSuiteInfo $suiteInfo -XmlWriter $XmlWriter $XmlWriter.WriteStartElement('results') $separator = if ($Path) { '.' } else { '' } $newName = if ($Node.Hint -ne 'Script') { $suiteInfo.Name } else { '' } $newPath = "${Path}${separator}${newName}" foreach ($action in $Node.Actions) { if ($action.Type -eq 'TestGroup') { Write-NUnitTestSuiteElements -Node $action -XmlWriter $XmlWriter -Path $newPath } } $suites = @( $Node.Actions | & $SafeCommands['Where-Object'] { $_.Type -eq 'TestCase' } | & $SafeCommands['Group-Object'] -Property ParameterizedSuiteName ) foreach ($suite in $suites) { if ($suite.Name) { $parameterizedSuiteInfo = Get-ParameterizedTestSuiteInfo -TestSuiteGroup $suite $XmlWriter.WriteStartElement('test-suite') Write-NUnitTestSuiteAttributes -TestSuiteInfo $parameterizedSuiteInfo -TestSuiteType 'ParameterizedTest' -XmlWriter $XmlWriter -Path $newPath $XmlWriter.WriteStartElement('results') } foreach ($testCase in $suite.Group) { Write-NUnitTestCaseElement -TestResult $testCase -XmlWriter $XmlWriter -Path $newPath -ParameterizedSuiteName $suite.Name } if ($suite.Name) { $XmlWriter.WriteEndElement() $XmlWriter.WriteEndElement() } } $XmlWriter.WriteEndElement() $XmlWriter.WriteEndElement() } function Write-JUnitReport($PesterState, [System.Xml.XmlWriter] $XmlWriter) { # Write the XML Declaration $XmlWriter.WriteStartDocument($false) # Write Root Element $xmlWriter.WriteStartElement('testsuites') Write-JUnitTestResultAttributes @PSBoundParameters $testSuiteNumber = 0 foreach ($action in $PesterState.TestActions.Actions) { Write-JUnitTestSuiteElements -XmlWriter $XmlWriter -Node $action -Id $testSuiteNumber $testSuiteNumber++ } $XmlWriter.WriteEndElement() } function Write-JUnitTestResultAttributes($PesterState, [System.Xml.XmlWriter] $XmlWriter) { $XmlWriter.WriteAttributeString('xmlns', 'xsi', $null, 'http://www.w3.org/2001/XMLSchema-instance') $XmlWriter.WriteAttributeString('xsi', 'noNamespaceSchemaLocation', [Xml.Schema.XmlSchema]::InstanceNamespace , 'junit_schema_4.xsd') $XmlWriter.WriteAttributeString('name', $PesterState.TestSuiteName) $XmlWriter.WriteAttributeString('tests', $PesterState.PassedCount) $XmlWriter.WriteAttributeString('errors', '0') $XmlWriter.WriteAttributeString('failures', $PesterState.FailedCount) $XmlWriter.WriteAttributeString('disabled', $PesterState.PendingCount + $PesterState.InconclusiveCount + $PesterState.SkippedCount) $XmlWriter.WriteAttributeString('time', ($PesterState.Time.TotalSeconds.ToString('0.000', [System.Globalization.CultureInfo]::InvariantCulture))) } function Write-JUnitTestSuiteElements($Node, [System.Xml.XmlWriter] $XmlWriter, [uint16] $Id) { $XmlWriter.WriteStartElement('testsuite') Write-JUnitTestSuiteAttributes -Action $Node -XmlWriter $XmlWriter -Package $Node.Name -Id $Id $testCases = foreach ($al1 in $node.Actions) { if ($al1.Type -ne 'TestCase') { foreach ($al2 in $al1.Actions) { if ($al2.Type -ne 'TestCase') { foreach ($alt3 in $al2.Actions) { $path = "$($al1.Name).$($al2.Name).$($alt3.Name)" $alt3 | Add-Member -PassThru -MemberType NoteProperty -Name Path -Value $path } } else { $path = "$($al1.Name).$($al2.Name)" $al2 | Add-Member -PassThru -MemberType NoteProperty -Name Path -Value $path } } } else { $path = "$($al1.Name)" $al1 | Add-Member -PassThru -MemberType NoteProperty -Name Path -Value $path } } foreach ($t in $testCases) { Write-JUnitTestCaseElements -Action $t -XmlWriter $XmlWriter -Package $Node.Name } $XmlWriter.WriteEndElement() } function Write-JUnitTestSuiteAttributes($Action, [System.Xml.XmlWriter] $XmlWriter, [string] $Package, [uint16] $Id) { $environment = Get-RunTimeEnvironment $XmlWriter.WriteAttributeString('name', $Action.Name) $XmlWriter.WriteAttributeString('tests', $Action.TotalCount) $XmlWriter.WriteAttributeString('errors', '0') $XmlWriter.WriteAttributeString('failures', $Action.FailedCount) $XmlWriter.WriteAttributeString('hostname', $environment.'machine-name') $XmlWriter.WriteAttributeString('id', $Id) $XmlWriter.WriteAttributeString('skipped', $Action.SkippedCount) $XmlWriter.WriteAttributeString('disabled', $Action.InconclusiveCount + $Action.PendingCount) $XmlWriter.WriteAttributeString('package', $Package) $XmlWriter.WriteAttributeString('time', $Action.Time.TotalSeconds.ToString('0.000', [System.Globalization.CultureInfo]::InvariantCulture)) $XmlWriter.WriteStartElement('properties') foreach ($keyValuePair in $environment.GetEnumerator()) { if ($keyValuePair.Name -eq 'nunit-version') { continue } $XmlWriter.WriteStartElement('property') $XmlWriter.WriteAttributeString('name', $keyValuePair.Name) $XmlWriter.WriteAttributeString('value', $keyValuePair.Value) $XmlWriter.WriteEndElement() } $XmlWriter.WriteEndElement() } function Write-JUnitTestCaseElements($Action, [System.Xml.XmlWriter] $XmlWriter, [string] $Package) { $XmlWriter.WriteStartElement('testcase') Write-JUnitTestCaseAttributes -Action $Action -XmlWriter $XmlWriter -ClassName $Package $XmlWriter.WriteEndElement() } function Write-JUnitTestCaseAttributes($Action, [System.Xml.XmlWriter] $XmlWriter, [string] $ClassName) { $XmlWriter.WriteAttributeString('name', $Action.Path) $statusElementName = switch ($Action.Result) { Passed { $null } Failed { 'failure' } default { 'skipped' } } $XmlWriter.WriteAttributeString('status', $Action.Result) $XmlWriter.WriteAttributeString('classname', $ClassName) $XmlWriter.WriteAttributeString('assertions', '0') $XmlWriter.WriteAttributeString('time', $Action.Time.TotalSeconds.ToString('0.000', [System.Globalization.CultureInfo]::InvariantCulture)) if ($null -ne $statusElementName) { Write-JUnitTestCaseMessageElements -Action $Action -XmlWriter $XmlWriter -StatusElementName $statusElementName } } function Write-JUnitTestCaseMessageElements($Action, [System.Xml.XmlWriter] $XmlWriter, [string] $StatusElementName) { $XmlWriter.WriteStartElement($StatusElementName) $XmlWriter.WriteAttributeString('message', $Action.FailureMessage) #TODO: Add stacktrace $XmlWriter.WriteEndElement() } function Get-ParameterizedTestSuiteInfo ([Microsoft.PowerShell.Commands.GroupInfo] $TestSuiteGroup) { $node = & $SafeCommands['New-Object'] psobject -Property @{ Name = $TestSuiteGroup.Name TotalCount = 0 Time = [timespan]0 PassedCount = 0 FailedCount = 0 SkippedCount = 0 PendingCount = 0 InconclusiveCount = 0 } foreach ($testCase in $TestSuiteGroup.Group) { $node.TotalCount++ switch ($testCase.Result) { Passed { $Node.PassedCount++; break; } Failed { $Node.FailedCount++; break; } Skipped { $Node.SkippedCount++; break; } Pending { $Node.PendingCount++; break; } Inconclusive { $Node.InconclusiveCount++; break; } } $Node.Time += $testCase.Time } return Get-TestSuiteInfo -TestSuite $node } function Get-TestSuiteInfo ($TestSuite, $TestSuiteName) { if (-not $PSBoundParameters.ContainsKey('TestSuiteName')) { $TestSuiteName = $TestSuite.Name } $suite = @{ resultMessage = 'Failure' success = if ($TestSuite.FailedCount -eq 0) { 'True' } else { 'False' } totalTime = Convert-TimeSpan $TestSuite.Time name = $TestSuiteName description = $TestSuiteName } $suite.resultMessage = Get-GroupResult $TestSuite $suite } function Get-TestTime($tests) { [TimeSpan]$totalTime = 0; if ($tests) { foreach ($test in $tests) { $totalTime += $test.time } } Convert-TimeSpan -TimeSpan $totalTime } function Convert-TimeSpan { param ( [Parameter(ValueFromPipeline = $true)] $TimeSpan ) process { if ($TimeSpan) { [string][math]::round(([TimeSpan]$TimeSpan).totalseconds, 4) } else { '0' } } } function Get-TestSuccess($tests) { $result = $true if ($tests) { foreach ($test in $tests) { if (-not $test.Passed) { $result = $false break } } } [String]$result } function Write-NUnitTestSuiteAttributes($TestSuiteInfo, [string] $TestSuiteType = 'TestFixture', [System.Xml.XmlWriter] $XmlWriter, [string] $Path) { $name = $TestSuiteInfo.Name if ($TestSuiteType -eq 'ParameterizedTest' -and $Path) { $name = "$Path.$name" } $XmlWriter.WriteAttributeString('type', $TestSuiteType) $XmlWriter.WriteAttributeString('name', $name) $XmlWriter.WriteAttributeString('executed', 'True') $XmlWriter.WriteAttributeString('result', $TestSuiteInfo.resultMessage) $XmlWriter.WriteAttributeString('success', $TestSuiteInfo.success) $XmlWriter.WriteAttributeString('time', $TestSuiteInfo.totalTime) $XmlWriter.WriteAttributeString('asserts', '0') $XmlWriter.WriteAttributeString('description', $TestSuiteInfo.Description) } function Write-NUnitTestCaseElement($TestResult, [System.Xml.XmlWriter] $XmlWriter, [string] $ParameterizedSuiteName, [string] $Path) { $XmlWriter.WriteStartElement('test-case') Write-NUnitTestCaseAttributes -TestResult $TestResult -XmlWriter $XmlWriter -ParameterizedSuiteName $ParameterizedSuiteName -Path $Path $XmlWriter.WriteEndElement() } function Write-NUnitTestCaseAttributes($TestResult, [System.Xml.XmlWriter] $XmlWriter, [string] $ParameterizedSuiteName, [string] $Path) { $testName = $TestResult.Name if ($testName -eq $ParameterizedSuiteName) { $paramString = '' if ($null -ne $TestResult.Parameters) { $params = @( foreach ($value in $TestResult.Parameters.Values) { if ($null -eq $value) { 'null' } elseif ($value -is [string]) { '"{0}"' -f $value } else { #do not use .ToString() it uses the current culture settings #and we need to use en-US culture, which [string] or .ToString([Globalization.CultureInfo]'en-us') uses [string]$value } } ) $paramString = $params -join ',' } $testName = "$testName($paramString)" } $separator = if ($Path) { '.' } else { '' } $testName = "${Path}${separator}${testName}" $XmlWriter.WriteAttributeString('description', $TestResult.Name) $XmlWriter.WriteAttributeString('name', $testName) $XmlWriter.WriteAttributeString('time', (Convert-TimeSpan $TestResult.Time)) $XmlWriter.WriteAttributeString('asserts', '0') $XmlWriter.WriteAttributeString('success', $TestResult.Passed) switch ($TestResult.Result) { Passed { $XmlWriter.WriteAttributeString('result', 'Success') $XmlWriter.WriteAttributeString('executed', 'True') break } Skipped { $XmlWriter.WriteAttributeString('result', 'Ignored') $XmlWriter.WriteAttributeString('executed', 'False') if ($TestResult.FailureMessage) { $XmlWriter.WriteStartElement('reason') $xmlWriter.WriteElementString('message', $TestResult.FailureMessage) $XmlWriter.WriteEndElement() # Close reason tag } break } Pending { $XmlWriter.WriteAttributeString('result', 'Inconclusive') $XmlWriter.WriteAttributeString('executed', 'True') if ($TestResult.FailureMessage) { $XmlWriter.WriteStartElement('reason') $xmlWriter.WriteElementString('message', $TestResult.FailureMessage) $XmlWriter.WriteEndElement() # Close reason tag } break } Inconclusive { $XmlWriter.WriteAttributeString('result', 'Inconclusive') $XmlWriter.WriteAttributeString('executed', 'True') if ($TestResult.FailureMessage) { $XmlWriter.WriteStartElement('reason') $xmlWriter.WriteElementString('message', $TestResult.FailureMessage) $XmlWriter.WriteEndElement() # Close reason tag } break } Failed { $XmlWriter.WriteAttributeString('result', 'Failure') $XmlWriter.WriteAttributeString('executed', 'True') $XmlWriter.WriteStartElement('failure') $xmlWriter.WriteElementString('message', $TestResult.FailureMessage) $XmlWriter.WriteElementString('stack-trace', $TestResult.StackTrace) $XmlWriter.WriteEndElement() # Close failure tag break } } } function Get-RunTimeEnvironment() { # based on what we found during startup, use the appropriate cmdlet $computerName = $env:ComputerName $userName = $env:Username if ($null -ne $SafeCommands['Get-CimInstance']) { $osSystemInformation = (& $SafeCommands['Get-CimInstance'] Win32_OperatingSystem) } elseif ($null -ne $SafeCommands['Get-WmiObject']) { $osSystemInformation = (& $SafeCommands['Get-WmiObject'] Win32_OperatingSystem) } elseif ($IsMacOS -or $IsLinux) { $osSystemInformation = @{ Name = "Unknown" Version = "0.0.0.0" } try { if ($null -ne $SafeCommands['uname']) { $osSystemInformation.Version = & $SafeCommands['uname'] -r $osSystemInformation.Name = & $SafeCommands['uname'] -s $computerName = & $SafeCommands['uname'] -n } if ($null -ne $SafeCommands['id']) { $userName = & $SafeCommands['id'] -un } } catch { # well, we tried } } else { $osSystemInformation = @{ Name = "Unknown" Version = "0.0.0.0" } } if ( ($PSVersionTable.ContainsKey('PSEdition')) -and ($PSVersionTable.PSEdition -eq 'Core')) { $CLrVersion = "Unknown" } else { $CLrVersion = [string]$PSVersionTable.ClrVersion } @{ 'nunit-version' = '2.5.8.0' 'junit-version' = '4' 'os-version' = $osSystemInformation.Version platform = $osSystemInformation.Name cwd = (& $SafeCommands['Get-Location']).Path #run path 'machine-name' = $computerName user = $username 'user-domain' = $env:userDomain 'clr-version' = $CLrVersion } } function Exit-WithCode ($FailedCount) { $host.SetShouldExit($FailedCount) } function Get-GroupResult ($InputObject) { #I am not sure about the result precedence, and can't find any good source #TODO: Confirm this is the correct order of precedence if ($inputObject.FailedCount -gt 0) { return 'Failure' } if ($InputObject.SkippedCount -gt 0) { return 'Ignored' } if ($InputObject.PendingCount -gt 0) { return 'Inconclusive' } return 'Success' } # SIG # Begin signature block # MIIcVgYJKoZIhvcNAQcCoIIcRzCCHEMCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUIASymVngWq5bkzMM+Z5AlaQB # Vo2ggheFMIIFDjCCA/agAwIBAgIQAkpwj7JyQh8pn8abOhJIUDANBgkqhkiG9w0B # AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz # c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTE4MTEyNzAwMDAwMFoXDTE5MTIw # MjEyMDAwMFowSzELMAkGA1UEBhMCQ1oxDjAMBgNVBAcTBVByYWhhMRUwEwYDVQQK # DAxKYWt1YiBKYXJlxaExFTATBgNVBAMMDEpha3ViIEphcmXFoTCCASIwDQYJKoZI # hvcNAQEBBQADggEPADCCAQoCggEBAKgBHbFhyeivpcxqohppsNGucvZvAyvg3gKT # M1B6EpI18C6NN7FXWTPym9ffe1kS5ZeYDKW98NcOBArm28mdgip6WcUQ0qMt9lI8 # EsTa4Ohlkj/AYYUdgh96zgIl/V+MIO3JVAY3OwkWjkfDKVbzrNG5IcO5yKfwnt8/ # a238OS/VlFNsNELGW7XIQBD/rKrEDY8JZReIrkz6sGdba+3OcXBClp513JFniVgD # vOXo2RDUqtnpFuCdsthe8hWXtpbcjIpFykLzNcNA+2GIbwbBG5XKXsN0ZJsbrWVA # RiwNoDN+Fh3pe5rxGVfMDHdXt1I0KpJbFlGB4P7Sy2Mh5CTwRyUCAwEAAaOCAcUw # ggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBSw # +E81VeF9HSgNxUARWWqXFWZrZjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI # KwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQu # Y29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRp # Z2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMEwGA1UdIARFMEMwNwYJ # YIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv # bS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0 # dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2Vy # dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5n # Q0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBAFltMO2WO9L/ # fLYIbzeQIilLoH97l9PYQAPz5uNh4R8l5w6Y/SgusY3+k+KaNONXSTcSL1KP9Pn3 # 2y11xqFwdrJbRxC8i5x2KFvGDGkvpDWFsX24plq5brRL+qerkyTwRgSRUiU/luvq # BTYQ/eQmgikkIB6l7f6m2An8qtOkNfDM0D/eVJS3+/TRSMIPmBp9Ubktacp8sNIK # JacAkVl1zjucvVhyuWOFsIFtPn25XsiNu4d87pUyMzm8Vehyl1xxLNH/6cqxCkyG # FXCrav1knrz22qD5b8wrwUYnmCt37BeBX6KvpSXpafDdAok5QkPs7TeJVcVVPdb4 # tqaLNvGOpBgwggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3 # DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAX # BgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3Vy # ZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJ # RCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl # +YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQz # UHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNx # PqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqr # hPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItq # cyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/ # AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEF # BQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBD # BggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0 # QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2Ny # bDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDig # NoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v # dENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYc # aHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4E # FgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGL # p6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0c # LToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9x # jmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpx # KAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUI # QjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhiz # gZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggZq # MIIFUqADAgECAhADAZoCOv9YsWvW1ermF/BmMA0GCSqGSIb3DQEBBQUAMGIxCzAJ # BgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k # aWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMTAe # Fw0xNDEwMjIwMDAwMDBaFw0yNDEwMjIwMDAwMDBaMEcxCzAJBgNVBAYTAlVTMREw # DwYDVQQKEwhEaWdpQ2VydDElMCMGA1UEAxMcRGlnaUNlcnQgVGltZXN0YW1wIFJl # c3BvbmRlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNkXfx8s+CC # NeDg9sYq5kl1O8xu4FOpnx9kWeZ8a39rjJ1V+JLjntVaY1sCSVDZg85vZu7dy4Xp # X6X51Id0iEQ7Gcnl9ZGfxhQ5rCTqqEsskYnMXij0ZLZQt/USs3OWCmejvmGfrvP9 # Enh1DqZbFP1FI46GRFV9GIYFjFWHeUhG98oOjafeTl/iqLYtWQJhiGFyGGi5uHzu # 5uc0LzF3gTAfuzYBje8n4/ea8EwxZI3j6/oZh6h+z+yMDDZbesF6uHjHyQYuRhDI # jegEYNu8c3T6Ttj+qkDxss5wRoPp2kChWTrZFQlXmVYwk/PJYczQCMxr7GJCkawC # wO+k8IkRj3cCAwEAAaOCAzUwggMxMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8E # AjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMIIBvwYDVR0gBIIBtjCCAbIwggGh # BglghkgBhv1sBwEwggGSMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2Vy # dC5jb20vQ1BTMIIBZAYIKwYBBQUHAgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAA # bwBmACAAdABoAGkAcwAgAEMAZQByAHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMA # dABpAHQAdQB0AGUAcwAgAGEAYwBjAGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgA # ZQAgAEQAaQBnAGkAQwBlAHIAdAAgAEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgA # ZQAgAFIAZQBsAHkAaQBuAGcAIABQAGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4A # dAAgAHcAaABpAGMAaAAgAGwAaQBtAGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAA # YQBuAGQAIABhAHIAZQAgAGkAbgBjAG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIA # ZQBpAG4AIABiAHkAIAByAGUAZgBlAHIAZQBuAGMAZQAuMAsGCWCGSAGG/WwDFTAf # BgNVHSMEGDAWgBQVABIrE5iymQftHt+ivlcNK2cCzTAdBgNVHQ4EFgQUYVpNJLZJ # Mp1KKnkag0v0HonByn0wfQYDVR0fBHYwdDA4oDagNIYyaHR0cDovL2NybDMuZGln # aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEQ0EtMS5jcmwwOKA2oDSGMmh0dHA6 # Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRENBLTEuY3JsMHcG # CCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQu # Y29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGln # aUNlcnRBc3N1cmVkSURDQS0xLmNydDANBgkqhkiG9w0BAQUFAAOCAQEAnSV+GzNN # siaBXJuGziMgD4CH5Yj//7HUaiwx7ToXGXEXzakbvFoWOQCd42yE5FpA+94GAYw3 # +puxnSR+/iCkV61bt5qwYCbqaVchXTQvH3Gwg5QZBWs1kBCge5fH9j/n4hFBpr1i # 2fAnPTgdKG86Ugnw7HBi02JLsOBzppLA044x2C/jbRcTBu7kA7YUq/OPQ6dxnSHd # FMoVXZJB2vkPgdGZdA0mxA5/G7X1oPHGdwYoFenYk+VVFvC7Cqsc21xIJ2bIo4sK # HOWV2q7ELlmgYd3a822iYemKC23sEhi991VUQAOSK2vCUcIKSK+w1G7g9BQKOhvj # jz3Kr2qNe9zYRDCCBs0wggW1oAMCAQICEAb9+QOWA63qAArrPye7uhswDQYJKoZI # hvcNAQEFBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ # MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNz # dXJlZCBJRCBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTIxMTExMDAwMDAwMFow # YjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQ # d3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgQXNzdXJlZCBJRCBD # QS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6IItmfnKwkKVpYBz # QHDSnlZUXKnE0kEGj8kz/E1FkVyBn+0snPgWWd+etSQVwpi5tHdJ3InECtqvy15r # 7a2wcTHrzzpADEZNk+yLejYIA6sMNP4YSYL+x8cxSIB8HqIPkg5QycaH6zY/2DDD # /6b3+6LNb3Mj/qxWBZDwMiEWicZwiPkFl32jx0PdAug7Pe2xQaPtP77blUjE7h6z # 8rwMK5nQxl0SQoHhg26Ccz8mSxSQrllmCsSNvtLOBq6thG9IhJtPQLnxTPKvmPv2 # zkBdXPao8S+v7Iki8msYZbHBc63X8djPHgp0XEK4aH631XcKJ1Z8D2KkPzIUYJX9 # BwSiCQIDAQABo4IDejCCA3YwDgYDVR0PAQH/BAQDAgGGMDsGA1UdJQQ0MDIGCCsG # AQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCDCC # AdIGA1UdIASCAckwggHFMIIBtAYKYIZIAYb9bAABBDCCAaQwOgYIKwYBBQUHAgEW # Lmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0w # ggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgA # aQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQA # ZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcA # aQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwA # eQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkA # YwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEA # cgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIA # eQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wCwYJYIZIAYb9bAMVMBIGA1UdEwEB/wQI # MAYBAf8CAQAweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz # cC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2lj # ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgw # OqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJ # RFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwHQYDVR0OBBYEFBUAEisTmLKZB+0e36K+ # Vw0rZwLNMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA0GCSqGSIb3 # DQEBBQUAA4IBAQBGUD7Jtygkpzgdtlspr1LPUukxR6tWXHvVDQtBs+/sdR90OPKy # XGGinJXDUOSCuSPRujqGcq04eKx1XRcXNHJHhZRW0eu7NoR3zCSl8wQZVann4+er # Ys37iy2QwsDStZS9Xk+xBdIOPRqpFFumhjFiqKgz5Js5p8T1zh14dpQlc+Qqq8+c # dkvtX8JLFuRLcEwAiR78xXm8TBJX/l/hHrwCXaj++wc4Tw3GXZG5D2dFzdaD7eeS # DY2xaYxP+1ngIw/Sqq4AfO6cQg7PkdcntxbuD8O9fAqg7iwIVYUiuOsYGk38KiGt # STGDR5V3cdyxG0tLHBCcdxTBnU8vWpUIKRAmMYIEOzCCBDcCAQEwgYYwcjELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENv # ZGUgU2lnbmluZyBDQQIQAkpwj7JyQh8pn8abOhJIUDAJBgUrDgMCGgUAoHgwGAYK # KwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIB # BDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQU # Gv2A0j9dMIfaDVi2HlK9GltYe4cwDQYJKoZIhvcNAQEBBQAEggEALsYSYnUCbgtW # bJezVmxsP6+CPAI2jH4KLGWMy/dyjKtX8Bx0AdtlMBjTJQaLcT5RpgOVPVlh24Sq # 0/AHjAbgShUrdHI7qQ1fOKDO0FtsYXQtQxpmH7YttOzDO3pKSWjfOQQ7sACHstT9 # K6DPDVGQtd/rjzW0wLL4ubQPAx+crt8YMI65oNL/qxcNLr31VnwgQ0EF5cu8gzg/ # SpaoYpOMfG96NdnqNOSfjRzp9e3rB61X2lzeVvO8ejmygnbpJqDJxUy1cdqqRm1P # l/C4M34GJ8r9UNyElzf21elKD7Sak8cWr8gU5+CMkXkFWAMVcG53meR6/xwIZfmK # ImnIcmdDtaGCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNV # BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp # Y2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGa # Ajr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3 # DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwOTA4MDg1NTExWjAjBgkqhkiG9w0BCQQx # FgQUqvIsoy/BgG2lvUbhlE+PLQsElS0wDQYJKoZIhvcNAQEBBQAEggEAgo5BtxpW # vhE7gAROvONVXk7YSHeMSWeaFo5v+qUvs126lxQbdq5MWUpNfEHc677Aye28npe6 # 6Ng7LUadGq+NFzepYxjZHfT98vs957gmDINroYVi889tbwIYyEZI8inuwSZOFEox # of89zJTWu/HTEGYCQUNYMLFCXPM1DqzJH+BjJKn3nsF99komZZVG5cSwqG/kn9BR # t4BSRC7rhWwGx8ginnhJmNrb58I40FHZ9kn4hb2Y4Qr0enzsBSYBZdNrzRs1OFnR # 2tkJx063Puc4w2dgal/59Cz0jojUPjza94nh1x/991k0DGu+WhMgFxi/qvgyjcrN # w+MorK1OSZx2jg== # SIG # End signature block |