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 ($Format) { 'NUnitXml' { Export-NUnitReport -PesterState $PesterState -Path $Path } default { throw "'$Format' is not a valid Pester export format." } } } function Export-NUnitReport { param ( [parameter(Mandatory=$true,ValueFromPipeline=$true)] $PesterState, [parameter(Mandatory=$true)] [String]$Path ) #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) Write-NUnitReport -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()) { $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 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') break } Pending { $XmlWriter.WriteAttributeString('result', 'Inconclusive') $XmlWriter.WriteAttributeString('executed', 'True') 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 ( $SafeCommands['Get-CimInstance'] -ne $null ) { $osSystemInformation = (& $SafeCommands['Get-CimInstance'] Win32_OperatingSystem) } elseif ( $SafeCommands['Get-WmiObject'] -ne $null ) { $osSystemInformation = (& $SafeCommands['Get-WmiObject'] Win32_OperatingSystem) } elseif ( $IsMacOS -or $IsLinux ) { $osSystemInformation = @{ Name = "Unknown" Version = "0.0.0.0" } try { if ( $SafeCommands['uname'] -ne $null ) { $osSystemInformation.Version = & $SafeCommands['uname'] -r $osSystemInformation.Name = & $SafeCommands['uname'] -s $computerName = & $SafeCommands['uname'] -n } if ( $SafeCommands['id'] -ne $null ) { $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' '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 # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUpFVofVXTZSs87K0DEEtr2IY/ # CsWggheFMIIFDjCCA/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 # qCE40khtgjLNzPRr3Hgldqa+Ev0wDQYJKoZIhvcNAQEBBQAEggEAdfpd3yLyBjKP # mgCT75l16ZYhwP2QDaElDtwBdSS2NkT5JqywMXzLe+Y0rZtzTbfs4SZ/kW8KyToT # 8pCzQK/eJrp5ucZthogAseYFxkc6mIQMG+tUslhbmvrAuomFhB0FxnQQyowD4+BM # Y6y28xDn/NBZWtoegEXY8cTvokBSMueOc21/n6WKKf+abX0timZyIE+z2z1E38Um # 5si/PGLNuje4xzdUT0uN0/AAMhPbObN0L5I9zxvQ3I5yExLQ7vRRV2KO1buZyxk5 # BsyCmkOkDRt4cVCZEfhkBe+J5+WRSByZrXvoBj9TJLHp755fXRjMMTknldKuz3FO # 4aQ1skW9t6GCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNV # BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp # Y2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGa # Ajr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3 # DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwMTAzMTg1ODI0WjAjBgkqhkiG9w0BCQQx # FgQUyKNGTGIyo9d/KJzZECL0G+gmgzswDQYJKoZIhvcNAQEBBQAEggEAHKhZ/389 # jYIBKGt6Y1nWqO+s7euGs8OPR4UxaKB+PK3D3oTwDyU0d+SKDRrZIpckkAxfEiFK # XhwQqaq6rfox1rd0hCKfwkq1FHM9pAM3yYcJ9cKC/DrwOD0jpH8eD/30Yk0BaWNj # e7dK+gNe5bSMifrL3w/eI+dcQmjAm0ZiZtv0PcWOsKSXSlh/ATzlH3ulNSoDeCw7 # jrv5F2k3ns8Pv4X9aRMENgA9xwG6Ii0PY6JfUlQH/OiOU/x1CvFLGHXRrZy7CXWs # jAlxIe5QWSfqJzXSyEV7udIIGI3kYIE9iVua4tys2Dsc+hUe5F/GFgS9CfBJO/MH # rTluNSLlkiY7/A== # SIG # End signature block |