new-runtimepoc/Pester.RSpec.ps1
function Find-File { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [String[]] $Path, [String[]] $ExcludePath, [Parameter(Mandatory=$true)] [string] $Extension ) $files = foreach ($p in $Path) { if ([String]::IsNullOrWhiteSpace($p)) { continue } if ((Test-Path $p)) { $item = Get-Item $p if ($item.PSIsContainer) { # this is an existing directory search it for tests file Get-ChildItem -Recurse -Path $p -Filter "*$Extension" -File continue } if ("FileSystem" -ne $item.PSProvider.Name) { # item is not a directory and exists but is not a file so we are not interested continue } if (".ps1" -ne $item.Extension) { Write-Error "Script path '$p' is not a ps1 file." -ErrorAction Stop } # this is some file, we don't care if it is just a .ps1 file or .Tests.ps1 file Add-Member -Name UnresolvedPath -Type NoteProperty -Value $p -InputObject $item $item continue } # this is a path that does not exist so let's hope it is # a wildcarded path that will resolve to some files Get-ChildItem -Recurse -Path $p -Filter "*$Extension" -File } Filter-Excluded -Files $files -ExcludePath $ExcludePath | where { $_ } } function Filter-Excluded ($Files, $ExcludePath) { if ($null -eq $ExcludePath -or @($ExcludePath).Length -eq 0) { return @($Files) } foreach ($file in @($Files)) { # normalize backslashes for cross-platform ease of use $p = $file.FullName -replace "/","\" $excluded = $false foreach ($exclusion in (@($ExcludePath) -replace "/","\")) { if ($excluded) { continue } if ($p -like $exclusion) { $excluded = $true } } if (-not $excluded) { $file } } } function Add-RSpecTestObjectProperties { param ($TestObject) # adds properties that are specific to RSpec to the result object # this includes figuring out the result # formatting the failure message and stacktrace $result = if ($TestObject.Skipped) { "Skipped" } elseif ($TestObject.Passed) { "Passed" } elseif ($TestObject.ShouldRun -and (-not $TestObject.Executed -or -not $TestObject.Passed)) { "Failed" } else { "NotRun" } $TestObject.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Result", $result)) # TODO: rename this to Duration, and rename duration to UserCodeDuration or something like that $time = [timespan]::zero + $TestObject.Duration + $TestObject.FrameworkDuration $TestObject.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Time", $time)) foreach ($e in $TestObject.ErrorRecord) { $r = ConvertTo-FailureLines $e $e.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("DisplayErrorMessage", [string]($r.Message -join [Environment]::NewLine))) $e.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("DisplayStackTrace", [string]($r.Trace -join [Environment]::NewLine))) } } function Add-RSpecBlockObjectProperties ($BlockObject) { foreach ($e in $BlockObject.ErrorRecord) { $r = ConvertTo-FailureLines $e $e.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("DisplayErrorMessage", [string]($r.Message -join [Environment]::NewLine))) $e.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("DisplayStackTrace", [string]($r.Trace -join [Environment]::NewLine))) } } function New-RSpecTestRunObject { param( [Parameter(Mandatory)] [DateTime] $ExecutedAt, [Parameter(Mandatory)] [Hashtable] $Parameters, [Hashtable] $BoundParameters, $Plugins, [Hashtable] $PluginConfiguration, [Hashtable] $PluginData, [PesterConfiguration] $Configuration, # [PSTypeName('ExecutedBlockContainer')] [object[]] $BlockContainer) [PSCustomObject]@{ PSTypeName = 'PesterRSpecTestRun' ExecutedAt = $ExecutedAt Containers = [Collections.ArrayList]@($BlockContainer) PSBoundParameters = $BoundParameters Plugins = $Plugins PluginConfiguration = $PluginConfiguration PluginData = $PluginData Configuration = $Configuration Duration = [TimeSpan]::Zero FrameworkDuration = [TimeSpan]::Zero DiscoveryDuration = [TimeSpan]::Zero Passed = [Collections.ArrayList]@() PassedCount = 0 Failed = [Collections.ArrayList]@() FailedCount = 0 Skipped = [Collections.ArrayList]@() SkippedCount = 0 NotRun = [Collections.ArrayList]@() NotRunCount = 0 Tests = [Collections.ArrayList]@() TestsCount = 0 FailedBlocks = [Collections.ArrayList]@() FailedBlocksCount = 0 } } function PostProcess-RspecTestRun ($TestRun) { Fold-Run $Run -OnTest { param($t) ## decorate # we already added the RSpec properties as part of the plugin ### summarize $TestRun.Tests.Add($t) switch ($t.Result) { "NotRun" { $null = $TestRun.NotRun.Add($t) } "Passed" { $null = $TestRun.Passed.Add($t) } "Failed" { $null = $TestRun.Failed.Add($t) } "Skipped" { $null = $TestRun.Skipped.Add($t) } default { throw "Result $($t.Result) is not supported."} } } -OnBlock { param ($b) ## decorate # we already processed errors in the plugin step to make the available for reporting # here we add result $result = if ($b.Skip) { "Skipped" } elseif ($b.Passed) { "Passed" } elseif ($b.ShouldRun -and (-not $b.Executed -or -not $b.Passed)) { "Failed" } else { "NotRun" } $b.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Result", $result)) # add time that we will later rename to Duration in the output object filter $time = [timespan]::zero + $b.Duration + $b.FrameworkDuration $b.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Time", $time)) ## sumamrize # a block that has errors would write into failed blocks so we can report them # later we can filter this to only report errors from AfterAll if (0 -lt $b.ErrorRecord.Count) { $TestRun.FailedBlocks.Add($b) } } -OnContainer { param ($b) ## decorate # here we add result $result = if ($b.Skipped) { "Skipped" } elseif ($b.Passed) { "Passed" } elseif ($b.ShouldRun -and (-not $b.Executed -or -not $b.Passed)) { "Failed" } else { "NotRun" } $b.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Result", $result)) # add time that we will later rename to Duration in the output object filter $time = [timespan]::zero + $b.Duration + $b.FrameworkDuration + $b.DiscoveryDuration $b.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Time", $time)) foreach ($e in $b.ErrorRecord) { $r = ConvertTo-FailureLines $e $e.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("DisplayErrorMessage", [string]($r.Message -join [Environment]::NewLine))) $e.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("DisplayStackTrace", [string]($r.Trace -join [Environment]::NewLine))) } ## summarize $TestRun.Duration += $b.Duration $TestRun.FrameworkDuration += $b.FrameworkDuration $TestRun.DiscoveryDuration += $b.DiscoveryDuration } $TestRun.PassedCount = $TestRun.Passed.Count $TestRun.FailedCount = $TestRun.Failed.Count $TestRun.SkippedCount = $TestRun.Skipped.Count $TestRun.NotRunCount = $TestRun.NotRun.Count $TestRun.TestsCount = $TestRun.Tests.Count $TestRun.FailedBlocksCount = $TestRun.FailedBlocks.Count $result = if (0 -lt ($TestRun.FailedCount + $TestRun.FailedBlocksCount)) { "Failed" } else { "Passed" } $TestRun.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Result", $result)) # add time that we will later rename to Duration in the output object filter $time = [timespan]::zero + $TestRun.Duration + $TestRun.FrameworkDuration + $TestRun.DiscoveryDuration $TestRun.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Time", $time)) } function Get-RSpecObjectDecoratorPlugin () { Pester.Runtime\New-PluginObject -Name "RSpecObjectDecoratorPlugin" ` -EachTestTeardownEnd { param ($Context) # TODO: consider moving this into the core if those results are just what we need, but look first at Gherkin and how many of those results are RSpec specific and how many are Gherkin specific #TODO: also this is a plugin because it needs to run before the error processing kicks in, this mixes concerns here imho, and needs to be revisited, because the error writing logic is now dependent on this plugin Add-RSpecTestObjectProperties $Context.Test } -EachBlockTeardownEnd { param($Context) #TODO: also this is a plugin because it needs to run before the error processing kicks in (to be able to report correctly formatted errors on scrren in case teardown failure), this mixes concerns here imho, and needs to be revisited, because the error writing logic is now dependent on this plugin Add-RSpecBlockObjectProperties $Context.Block } } function New-PesterConfiguration { [CmdletBinding()] param() [PesterConfiguration]@{} } function Remove-RSpecNonPublicProperties ($run){ $runProperties = @( 'Configuration' 'Containers' 'ExecutedAt' 'FailedBlocksCount' 'FailedCount' 'NotRunCount' 'PassedCount' 'PSBoundParameters' 'Result' 'SkippedCount' 'TestsCount' 'Time' ) $containerProperties = @( 'Blocks' 'Content' 'ErrorRecord' 'Executed' 'ExecutedAt' 'FailedCount' 'NotRunCount' 'PassedCount' 'Result' 'ScriptBlock' 'ShouldRun' 'Skip' 'SkippedCount' 'Tests' 'Time' 'Type' # needed because of nunit export path expansion 'TotalCount' ) $blockProperties = @( 'Blocks' 'ErrorRecord' 'Executed' 'ExecutedAt' 'FailedCount' 'Name' 'NotRunCount' 'PassedCount' 'Path' 'Result' 'ScriptBlock' 'ShouldRun' 'Skip' 'SkippedCount' 'StandardOutput' 'Tag' 'Tests' 'Time' 'TotalCount' ) $testProperties = @( 'Data' 'ErrorRecord' 'Executed' 'ExecutedAt' 'ExpandedName' 'Id' # needed because of grouping of data driven tests in nunit export 'Name' 'Path' 'Result' 'ScriptBlock' 'ShouldRun' 'Skip' 'Skipped' 'StandardOutput' 'Tag' 'Time' ) Fold-Run $run -OnRun { param($i) $ps = $i.PsObject.Properties.Name foreach ($p in $ps) { if ($p -notin $runProperties) { $i.PsObject.Properties.Remove($p) } } $i.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Duration", $i.PsObject.Properties.Item("Time").Value)) $i.PsObject.Properties.Remove("Time") } -OnContainer { param($i) $ps = $i.PsObject.Properties.Name foreach ($p in $ps) { if ($p -notin $containerProperties) { $i.PsObject.Properties.Remove($p) } } $i.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Duration", $i.PsObject.Properties.Item("Time").Value)) $i.PsObject.Properties.Remove("Time") } -OnBlock { param($i) $ps = $i.PsObject.Properties.Name foreach ($p in $ps) { if ($p -notin $blockProperties) { $i.PsObject.Properties.Remove($p) } } $i.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Duration", $i.PsObject.Properties.Item("Time").Value)) $i.PsObject.Properties.Remove("Time") } -OnTest { param($i) $ps = $i.PsObject.Properties.Name foreach ($p in $ps) { if ($p -notin $testProperties) { $i.PsObject.Properties.Remove($p) } } $i.PSObject.Properties.Add([Pester.Factory]::CreateNoteProperty("Duration", $i.PsObject.Properties.Item("Time").Value)) $i.PsObject.Properties.Remove("Time") } } # SIG # Begin signature block # MIIcVgYJKoZIhvcNAQcCoIIcRzCCHEMCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUfH8M+GUA33gJaL2sOpsA2yh8 # N8uggheFMIIFDjCCA/agAwIBAgIQCIQ1OU/QbU6rESO7M78utDANBgkqhkiG9w0B # AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD # VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz # c3VyZWQgSUQgQ29kZSBTaWduaW5nIENBMB4XDTIwMDEzMTAwMDAwMFoXDTIxMDEw # NTEyMDAwMFowSzELMAkGA1UEBhMCQ1oxDjAMBgNVBAcTBVByYWhhMRUwEwYDVQQK # DAxKYWt1YiBKYXJlxaExFTATBgNVBAMMDEpha3ViIEphcmXFoTCCASIwDQYJKoZI # hvcNAQEBBQADggEPADCCAQoCggEBALYF0cDtFUyYgraHpHdObGJM9dxjfRr0WaPN # kVZcEHdPXk4bVCPZLSca3Byybx745CpB3oejDHEbohLSTrbunoSA9utpwxVQSutt # /H1onVexiJgwGJ6xoQgR17FGLBGiIHgyPhFJhba9yENh0dqargLWllsg070WE2yb # gz3m659gmfuCuSZOhQ2nCHvOjEocTiI67mZlHvN7axg+pCgdEJrtIyvhHPqXeE2j # cdMrfmYY1lq2FBpELEW1imYlu5BnaJd/5IT7WjHL3LWx5Su9FkY5RwrA6+X78+j+ # vKv00JtDjM0dT+4A/m65jXSywxa4YoGDqQ5n+BwDMQlWCzfu37sCAwEAAaOCAcUw # ggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5LfZldQ5YMB0GA1UdDgQWBBRE # 05R/U5mVzc4vKq4rvKyyPm12EzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYI # KwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0cDovL2NybDMuZGlnaWNlcnQu # Y29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAzoDGGL2h0dHA6Ly9jcmw0LmRp # Z2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEuY3JsMEwGA1UdIARFMEMwNwYJ # YIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNv # bS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0 # dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2Vy # dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5n # Q0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEBADAk7PRuDcdl # lPZQSfZ1Y0jeItmEWPMNcAL0LQaa6M5Slrznjxv1ZiseT9SMWTxOQylfPvpOSo1x # xV3kD7qf7tf2EuicKkV6dBgGiHb0riWZ3+wMA6C8IK3cGesJ4jgpTtYEzbh88pxT # g2MSzpRnwyXHhrgcKSps1z34JmmmHP1lncxNC6DTM6yEUwE7XiDD2xNoeLITgdTQ # jjMMT6nDJe8+xL0Zyh32OPIyrG7qPjG6MmEjzlCaWsE/trVo7I9CSOjwpp8721Hj # q/tIHzPFg1C3dYmDh8Kbmr21dHWBLYQF4P8lq8u8AYDa6H7xvkx7G0i2jglAA4YK # i1V8AlyTwRkwggUwMIIEGKADAgECAhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3 # 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 # ZGUgU2lnbmluZyBDQQIQCIQ1OU/QbU6rESO7M78utDAJBgUrDgMCGgUAoHgwGAYK # KwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIB # BDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQU # fgoqxvfN4oiNEfxA3T0x/LUHFgswDQYJKoZIhvcNAQEBBQAEggEALE1wJ0k2p8B4 # sSXHj15LlSRIl/8BApUdwFz4563xDgwiNcdmCLDwUiQV+Rr32Kfc/kqgvVQn5jro # wmChVn7RkQRDb6kleQGnFQSDvHz+sCSQu2ayyD5hC/YyS+Ey6i0e51jCBwkOIkWt # f2fxZn+DXJuc1fLuv9ik+Fjnfz5tyDHLHebALpOrt96YXs9un3XK5JzjeOFnpIIb # MsriwgTretR7NjMGqTTtRJiDs03SIDVhdzbOtXaywXk5bAAPbjQd27bOhiWEIaEE # ophr4BeJROmskA4EmmimKiDONDbgdWro6HF+CT5skr5CqCkQtA8gO/uZtInBu1zJ # 5etqsTH8T6GCAg8wggILBgkqhkiG9w0BCQYxggH8MIIB+AIBATB2MGIxCzAJBgNV # BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp # Y2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IEFzc3VyZWQgSUQgQ0EtMQIQAwGa # Ajr/WLFr1tXq5hfwZjAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3 # DQEHATAcBgkqhkiG9w0BCQUxDxcNMjAwNDA0MTc1MTQzWjAjBgkqhkiG9w0BCQQx # FgQUOgyMVBlZ6SSMgItnaU3hI+1hYRgwDQYJKoZIhvcNAQEBBQAEggEAT0kIZQ7m # Fxx3LYszAtN8PqvtPYJ9TPbI4j4WWlFsPC13K/NXoLnaNwKovsZAtCDgy8dHq6kM # C9rXoPRcNAHDnQWPJextjCGl1l2T9z2VsRLun4zbRnwsPvknsXAXNIXSUjDhc/3k # 7jTp13zMJj6deyiK0nFrFPSWCw2bezT23eKOy1//5p15eLuLg3N8lFYOVretl+P+ # PHvCc77fhNORvv6GKiAZEovNXfF9AIlr5FtQSPTCMtBTv7+iW9RDtMdzwbKOrWeV # iJObUmb+PDKJqYiA4D/UzNOKCZ7VVLEvVasHYaSV4fsW8FhXHXLIVQvkO54eaNKH # Buwev4Gydb46Iw== # SIG # End signature block |