$here = Split-Path -Parent $MyInvocation.MyCommand.Path $manifestPath = "$here\Pester.psd1" $changeLogPath = "$here\CHANGELOG.md" # DO NOT CHANGE THIS TAG NAME; IT AFFECTS THE CI BUILD. Describe -Tags 'VersionChecks' "Pester manifest and changelog" { $script:manifest = $null It "has a valid manifest" { { $script:manifest = Test-ModuleManifest -Path $manifestPath -ErrorAction Stop -WarningAction SilentlyContinue } | Should Not Throw } It "has a valid name in the manifest" { $script:manifest.Name | Should Be Pester } It "has a valid guid in the manifest" { $script:manifest.Guid | Should Be 'a699dea5-2c73-4616-a270-1f7abb777e71' } It "has a valid version in the manifest" { $script:manifest.Version -as [Version] | Should Not BeNullOrEmpty } $script:changelogVersion = $null It "has a valid version in the changelog" { foreach ($line in (Get-Content $changeLogPath)) { if ($line -match "^\D*(?(\d+\.){1,3}\d+)") { $script:changelogVersion = $matches.Version break } } $script:changelogVersion | Should Not BeNullOrEmpty $script:changelogVersion -as [Version] | Should Not BeNullOrEmpty } It "changelog and manifest versions are the same" { $script:changelogVersion -as [Version] | Should be ( $script:manifest.Version -as [Version] ) } if (Get-Command git.exe -ErrorAction SilentlyContinue) { $script:tagVersion = $null It "is tagged with a valid version" { $thisCommit = git.exe log --decorate --oneline HEAD~1..HEAD if ($thisCommit -match 'tag:\s*(\d+(?:\.\d+)*)') { $script:tagVersion = $matches[1] } $script:tagVersion | Should Not BeNullOrEmpty $script:tagVersion -as [Version] | Should Not BeNullOrEmpty } It "all versions are the same" { $script:changelogVersion -as [Version] | Should be ( $script:manifest.Version -as [Version] ) $script:manifest.Version -as [Version] | Should be ( $script:tagVersion -as [Version] ) } } } if ($PSVersionTable.PSVersion.Major -ge 3) { $error.Clear() Describe 'Clean treatment of the $error variable' { Context 'A Context' { It 'Performs a successful test' { $true | Should Be $true } } It 'Did not add anything to the $error variable' { $error.Count | Should Be 0 } } InModuleScope Pester { Describe 'SafeCommands table' { $path = $ExecutionContext.SessionState.Module.ModuleBase $filesToCheck = Get-ChildItem -Path $path -Recurse -Include *.ps1,*.psm1 -Exclude *.Tests.ps1 $callsToSafeCommands = @( foreach ($file in $files) { $tokens = $parseErrors = $null $ast = [System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref] $tokens, [ref] $parseErrors) $filter = { $args[0] -is [System.Management.Automation.Language.CommandAst] -and $args[0].InvocationOperator -eq [System.Management.Automation.Language.TokenKind]::Ampersand -and $args[0].CommandElements[0] -is [System.Management.Automation.Language.IndexExpressionAst] -and $args[0].CommandElements[0].Target -is [System.Management.Automation.Language.VariableExpressionAst] -and $args[0].CommandElements[0].Target.VariablePath.UserPath -match '^(?:script:)?SafeCommands$' } $ast.FindAll($filter, $true) } ) $uniqueSafeCommands = $callsToSafeCommands | ForEach-Object { $_.CommandElements[0].Index.Value } | Select-Object -Unique $missingSafeCommands = $uniqueSafeCommands | Where-Object { -not $script:SafeCommands.ContainsKey($_) } It 'The SafeCommands table contains all commands that are called from the module' { $missingSafeCommands | Should Be $null } } } } Describe 'Style rules' { $pesterRoot = (Get-Module Pester).ModuleBase $files = @( Get-ChildItem $pesterRoot -Include *.ps1,*.psm1 Get-ChildItem $pesterRoot\Functions -Include *.ps1,*.psm1 -Recurse ) It 'Pester source files contain no trailing whitespace' { $badLines = @( foreach ($file in $files) { $lines = [System.IO.File]::ReadAllLines($file.FullName) $lineCount = $lines.Count for ($i = 0; $i -lt $lineCount; $i++) { if ($lines[$i] -match '\s+$') { 'File: {0}, Line: {1}' -f $file.FullName, ($i + 1) } } } ) if ($badLines.Count -gt 0) { throw "The following $($badLines.Count) lines contain trailing whitespace: `r`n`r`n$($badLines -join "`r`n")" } } It 'Pester Source Files all end with a newline' { $badFiles = @( foreach ($file in $files) { $string = [System.IO.File]::ReadAllText($file.FullName) if ($string.Length -gt 0 -and $string[-1] -ne "`n") { $file.FullName } } ) if ($badFiles.Count -gt 0) { throw "The following files do not end with a newline: `r`n`r`n$($badFiles -join "`r`n")" } } } InModuleScope Pester { Describe 'ResolveTestScripts' { Setup -File SomeFile.ps1 Setup -File SomeFile.Tests.ps1 Setup -File SomeOtherFile.ps1 Setup -File SomeOtherFile.Tests.ps1 It 'Resolves non-wildcarded file paths regardless of whether the file ends with Tests.ps1' { $result = @(ResolveTestScripts $TestDrive\SomeOtherFile.ps1) $result.Count | Should Be 1 $result[0].Path | Should Be "$TestDrive\SomeOtherFile.ps1" } It 'Finds only *.Tests.ps1 files when the path contains wildcards' { $result = @(ResolveTestScripts $TestDrive\*.ps1) $result.Count | Should Be 2 $paths = $result | Select-Object -ExpandProperty Path ($paths -contains "$TestDrive\SomeFile.Tests.ps1") | Should Be $true ($paths -contains "$TestDrive\SomeOtherFile.Tests.ps1") | Should Be $true } It 'Finds only *.Tests.ps1 files when the path refers to a directory and does not contain wildcards' { $result = @(ResolveTestScripts $TestDrive) $result.Count | Should Be 2 $paths = $result | Select-Object -ExpandProperty Path ($paths -contains "$TestDrive\SomeFile.Tests.ps1") | Should Be $true ($paths -contains "$TestDrive\SomeOtherFile.Tests.ps1") | Should Be $true } It 'Assigns empty array and hashtable to the Arguments and Parameters properties when none are specified by the caller' { $result = @(ResolveTestScripts "$TestDrive\SomeFile.ps1") $result.Count | Should Be 1 $result[0].Path | Should Be "$TestDrive\SomeFile.ps1" ,$result[0].Arguments | Should Not Be $null ,$result[0].Parameters | Should Not Be $null $result[0].Arguments.GetType() | Should Be ([object[]]) $result[0].Arguments.Count | Should Be 0 $result[0].Parameters.GetType() | Should Be ([hashtable]) $result[0].Parameters.PSBase.Count | Should Be 0 } Context 'Passing in Dictionaries instead of Strings' { It 'Allows the use of a "P" key instead of "Path"' { $result = @(ResolveTestScripts @{ P = "$TestDrive\SomeFile.ps1" }) $result.Count | Should Be 1 $result[0].Path | Should Be "$TestDrive\SomeFile.ps1" } $testArgs = @('I am a string') It 'Allows the use of an "Arguments" key in the dictionary' { $result = @(ResolveTestScripts @{ Path = "$TestDrive\SomeFile.ps1"; Arguments = $testArgs }) $result.Count | Should Be 1 $result[0].Path | Should Be "$TestDrive\SomeFile.ps1" $result[0].Arguments.Count | Should Be 1 $result[0].Arguments[0] | Should Be 'I am a string' } It 'Allows the use of an "Args" key in the dictionary' { $result = @(ResolveTestScripts @{ Path = "$TestDrive\SomeFile.ps1"; Args = $testArgs }) $result.Count | Should Be 1 $result[0].Path | Should Be "$TestDrive\SomeFile.ps1" $result[0].Arguments.Count | Should Be 1 $result[0].Arguments[0] | Should Be 'I am a string' } It 'Allows the use of an "A" key in the dictionary' { $result = @(ResolveTestScripts @{ Path = "$TestDrive\SomeFile.ps1"; A = $testArgs }) $result.Count | Should Be 1 $result[0].Path | Should Be "$TestDrive\SomeFile.ps1" $result[0].Arguments.Count | Should Be 1 $result[0].Arguments[0] | Should Be 'I am a string' } $testParams = @{ MyKey = 'MyValue' } It 'Allows the use of a "Parameters" key in the dictionary' { $result = @(ResolveTestScripts @{ Path = "$TestDrive\SomeFile.ps1"; Parameters = $testParams }) $result.Count | Should Be 1 $result[0].Path | Should Be "$TestDrive\SomeFile.ps1" $result[0].Parameters.PSBase.Count | Should Be 1 $result[0].Parameters['MyKey'] | Should Be 'MyValue' } It 'Allows the use of a "Params" key in the dictionary' { $result = @(ResolveTestScripts @{ Path = "$TestDrive\SomeFile.ps1"; Params = $testParams }) $result.Count | Should Be 1 $result[0].Path | Should Be "$TestDrive\SomeFile.ps1" $result[0].Parameters.PSBase.Count | Should Be 1 $result[0].Parameters['MyKey'] | Should Be 'MyValue' } It 'Throws an error if no Path is specified' { { ResolveTestScripts @{} } | Should Throw } It 'Throws an error if a Parameters key is used, but does not contain an IDictionary object' { { ResolveTestScripts @{ P='P'; Params = 'A string' } } | Should Throw } } } } # SIG # Begin signature block # MIInjAYJKoZIhvcNAQcCoIInfTCCJ3kCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBfE/e7u3jI6yz7 # QBumibsiH2jWUPju+4aTP+s3pHx6C6CCC8MwggXaMIIEwqADAgECAhMzAAABP8rF # KBkLiTVYAAAAAAE/MA0GCSqGSIb3DQEBCwUAMIGOMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMTgwNgYDVQQDEy9NaWNyb3NvZnQgV2luZG93cyBU # aGlyZCBQYXJ0eSBDb21wb25lbnQgQ0EgMjAxMjAeFw0yNTExMTMxOTU5NDJaFw0y # NjExMTAxOTU5NDJaMIGEMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMS4wLAYDVQQDEyVNaWNyb3NvZnQgV2luZG93cyAzcmQgcGFydHkgQ29tcG9u # ZW50MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA2Zy0BnZmL5lF9IDS # IFBJ81NqCWCPLo2bRGJObUnrlezQw+FWEDU9+poMEyRjgbgafifNKY4TJ3e9Od4p # q56xMXBcGYVIe546gz4p68MQG4iXqqSBB4kk5jwk5U7igTCfZIga6PFElV6Wm7kv # Bdw14NVgdJZZDdmIc9TdaWbxrxAda9IMjZNQYfJZ/WVinf0mPnYM2hQwj4Gl4DGC # 0/KO6U+ayXHAtcS9qj2UJYB7rCyteNydGWHaMa5B8fzOpSNS3ioJfYcBwSjfcBRD # pemnEb5BcIF10FVuNA4foeMz5emIZaGGl8XxVC9K79Xwkc571Sv899qEdYP8ZFW9 # yVXY8l1ptvk4nD52nq9ld4HjWA+FHmhbhKggbjEVymQee7fOgEWKE3Uc73YnTMGf # TXzDwH9jYip5fwls08LWs0HCINu/iA/OG/vm1jrJdK5wcBgX2B0fZPdwgLTEgs0R # rSIyv9WucI0S6XffXJuUH+lziAX7RmCPhy5kZR1R3LB0GnBlAgMBAAGjggG3MIIB # szATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUt3RSDxhJIhKufhwaOyWL # 3nMDoeUwVAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5k # IE9wZXJhdGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwODA5KzUwNjIzOTAfBgNV # HSMEGDAWgBRhcaeHr/9p1SF2T1KTKAC+eRKrhDB0BgNVHR8EbTBrMGmgZ6BlhmNo # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBX # aW5kb3dzJTIwVGhpcmQlMjBQYXJ0eSUyMENvbXBvbmVudCUyMENBJTIwMjAxMi5j # cmwwgYEGCCsGAQUFBwEBBHUwczBxBggrBgEFBQcwAoZlaHR0cDovL3d3dy5taWNy # b3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBXaW5kb3dzJTIwVGhp # cmQlMjBQYXJ0eSUyMENvbXBvbmVudCUyMENBJTIwMjAxMi5jcnQwDAYDVR0TAQH/ # BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAk+iGdVNjQ2VMiNXhflILGybQmTUMM+qd # BZ3KErdJ9wkVTN/fMukvmp9y1iF8Sz1NUqDNqiKLofcL0XukOu5W3zAofFlAs2tR # vf0ArWKgRP5gjpqXeo3xWRM/1LBYTDhwDmylfh36AnfErB+aHyoIr9an+2KqeIqj # 5VvFPgwJ1n6ZTXZMhjvYnIol/P+vwVroo2XKwbOL1/c79xRj7X8Lqw+7sVoIA9/P # ytqdSDV1ClBjltkRpdgwvbSDPzycfvN8V5pPFfkrqrcIHjaL2pe76nqRsEIPqiVQ # SmqiaJV6iprCSDGJYC4/4EMLIDZ4uf+m0XHW3Qzlr7RLlMdsJKny7jCCBeEwggPJ # oAMCAQICCmELqsEAAAAAAAkwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290 # IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEyMDQxODIzNDgzOFoXDTI3 # MDQxODIzNTgzOFowgY4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xODA2BgNVBAMTL01pY3Jvc29mdCBXaW5kb3dzIFRoaXJkIFBhcnR5IENvbXBv # bmVudCBDQSAyMDEyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo5ww # hAmnYy7PCkfw6iT5ozAgD15XMSaBmjEHslDUzmcJCGUKWqVLrtXtEC7npZm1n2gv # mItYAqwgtCnEcb0oHKX9PJtk5MXr32ElvPDuaL/Rp8t+KgKBTmRcDFOGeVcZN2G3 # mPkMoE4iWZv5Gy1nPCc8VpBm4/1/ZX0Phr01R+iKzPTajulqTqunVeyiiR7VM0VT # y/med73NLPkFuH90AR3o+xjhQ9EN6arcN2+9/rgP7R1NAUZOCqz8gujsVoMTjjoB # 7RRkdOpksmYQtmhtyHAAfVBILj1D7uAklcbNjsf9uOSVz91++5VeoQHNQ7EH16Qw # 7puGGipuwQtZonRviwIDAQABo4IBQzCCAT8wEAYJKwYBBAGCNxUBBAMCAQAwHQYD # VR0OBBYEFGFxp4ev/2nVIXZPUpMoAL55EquEMBkGCSsGAQQBgjcUAgQMHgoAUwB1 # AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaA # FNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j # cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8y # MDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAt # MDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQBaimfazNX9DSZBd78KRni0s94S # aSt3I8JlLwFf0gP0YbpQnS6MOXLzbD5qsR52bey384LczLvFaXAoc2YXP1Tr7gEW # SMRG2RuAroE6jQ95bWiwnuotPznTyjh+vV58CG4Z3MbC9DgzaGHiUkeD4QABVtK6 # y4eCBTEKQYtO539fX+1f0zktReuiE7/9HsKYQXFhFl/ICnAlfFlpMSTkcecKuwQX # 959yHsnSuxq+PQL+CQyyQ7RZGplTk5YhX+DWtyYBQpU2rCf9vvSFd2g9GL30vpiI # IhGGUhbzRewDlxBwh6NwQ3E828mGAxcM9XNbxn3hXGTt18VI1+0y4tGq08+n9ldO # Yfl362fyiLPeANoDj9CKNDc+HdhiuNKx8+Evi3I7gZZ8b/zsZnZyYBsk8qCJbVtt # AC7vKN2GhwXCtLnlvmTCKvJKFVyY4sQnhf9S42J+D7ICC9dmxwqy0z0gBBRQMlmD # Cn2b7Vo4EgFSui9eIHKOSvH953ECjDvhB77Jc/TdR9i077SkszC5iT52yrkAmFZ+ # q+qKuKXQOKtpdxMLFC/pqkEf97q9Ois0iu4Kq2PmY/eIJI4gDSs7nePCSVKsnx8O # OTtd1G5QauZ9UjqqfDMVKQ0mXgFYp06pPXqEb3Q/YJ/kMk82AK9tcdM+pkZlX4F0 # 8f7BcdpMoEFagt3xHzGCGx8wghsbAgEBMIGmMIGOMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMTgwNgYDVQQDEy9NaWNyb3NvZnQgV2luZG93cyBU # aGlyZCBQYXJ0eSBDb21wb25lbnQgQ0EgMjAxMgITMwAAAT/KxSgZC4k1WAAAAAAB # PzANBglghkgBZQMEAgEFAKCCARgwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIPeM # bpEhPsuAQOBcxZLF12ho0coz1KZAxOETCWK7sDSyMFAGCisGAQQBgjcKAxwxQgxA # NURGMzhCRUNDRDZGMzI5NzU5QkExMDQzMDc3NkU0NTVCQkM3M0UyQjU0OTA2MjM2 # NUUwREQyMjJFQ0MxRkMxMjBaBgorBgEEAYI3AgEMMUwwSqAkgCIATQBpAGMAcgBv # AHMAbwBmAHQAIABXAGkAbgBkAG8AdwBzoSKAIGh0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS93aW5kb3dzMA0GCSqGSIb3DQEBAQUABIIBgBm5MV+w8ua6FleHX/k1+XFa # U//3WNqPZ8s86b7jmxnX19JA2q2IxdBc6Ofu8hLRDCNVt5pV0q9jMk8e1nyvZus5 # BwiLLXoEFI9Yc4SKRlqml1g7xPRwki2e5xsxnGsrY/BcpR3/jk3CZtaj/jljPEqh # ApX4GNmBG3SuDMY01HhYYA38jKv41StSuVbIdnQ4n7vrS68lvJ0rAD+i9v23PbTh # hthmOTB3qs4RSXa1U7DgJl/bPjPv3fp34rYw6SRxjTAR+cqpz9H9EIoeNp7yu/HK # q2sF1slNsroNsRdI498YSMIk8vkaviLHpIwjr8puqvqYw8UsSQmTXSmuYILqJ0Uw # br+YBSGgszO9PBG7dlq0pBWzuLIbwA6dUU6YOwpw/aKuvlc6/DIwqa6XZN2zstif # REkzI3fNh2WZ7oOk2Tjj0lRBqNW9ZVF8hJylj90OtiYjpbSuIK2VChI/eHOGfUW3 # ctOp7rrLhBH/kRN+6y20nQg21HDadyQgLQrDr/T1TaGCF60wghepBgorBgEEAYI3 # AwMBMYIXmTCCF5UGCSqGSIb3DQEHAqCCF4YwgheCAgEDMQ8wDQYJYIZIAWUDBAIB # BQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAx # MA0GCWCGSAFlAwQCAQUABCD2K3ZS8pX7+NGfbxXKkFR+N4dP0muiepcrFj4KVQ72 # sAIGajYG9cICGBMyMDI2MDcxMTA0MDMyNS4yNTVaMASAAgH0oIHZpIHWMIHTMQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNy # b3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGll # bGQgVFNTIEVTTjo2RjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRp # bWUtU3RhbXAgU2VydmljZaCCEfswggcoMIIFEKADAgECAhMzAAACHAlVFdfDWQfR # AAEAAAIcMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAy # MDEwMB4XDTI1MDgxNDE4NDgzMVoXDTI2MTExMzE4NDgzMVowgdMxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJ # cmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1Mg # RVNOOjZGMUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAow0xEAUa # FIyyLIXeFzeI8IKyBON2u0Dr02ISE5p9G5CUXfnFu2S0E1gWCMvDWpopX6lRxjmg # nqaL3BtnWlBVTo8xUNRZu23ie4YBMAJB7Ut6mnqnHVwvDJxGO4TD3SnrCd+yg35B # 9QFejq3o4+OByvXjynaypZyukcQaLsKQvoxE8ElHH7zcOXEJWmU3rnXzaW/S4SH3 # OPhoUbTTcy6nUgKx5pRWiQ24UEPLYzcxGJjqjkz+GiCWGPFHDMdW86laWvmCslou # QPsN2eBk8dxJcEZmW4l6p4TthoXcfexEA9YdYaMz10aMhZNpdsNaDtDQUMDEC3k1 # D1My69MXSPlUmD9xFyDlkXiVa7BCEp3XcVtqTgzHGwr28JD6oE7zEPYeuZOiuCBX # TZSo/wk3tbDlsESbIPV6inYqrzxiMYqlxfCdzC3Cimh9/NT/Lk9/aU+Iyyc9b3Oa # T0dZ8wgLaVDCGELRMrqyImdFHv0MudctzW/kPsV3Ja9ufpKWujEiN3CW//X8hFa9 # j5ImNeQzcMit3MoSaoGwnbiZJX1IyibIphlqccXFk4oTTSOQBsAUw8U0gwOnM5UJ # D8mBUBd65Np6NBkx2cviJ4I34GyXFCWyy5Ft1QsBYyVfAG3KOhCfPHQf8lQzJvLr # 57YW0bD/xVs4Ag4gTS6KZNyFEfX9jFdRlr0CAwEAAaOCAUkwggFFMB0GA1UdDgQW # BBRa3mOCzB8u7zpvDh8MGKVYLCk7ZDAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJl # pxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAx # MCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3Rh # bXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM # MAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEA # klb6w/deaid3BujQCtWFBe0n9pkyRy+yyWEg70iDwoJ5u0e0O+4GerNzdZb1zTPs # HJ8EGMyo1K7ytL21+pmdFMTl19PC8OJ5Y2p+XKUQy2dD+hggRMmJgDQsgbOCxHYe # O+jg4t+vg61wUrovzzLkH3z0PJXXvoNuBj9Lda9CiNMd60451Kube99ArSf6ZMj3 # t0p4rFbgSazDs+8TJ+8KA5GVaYjPHj9rlMuI3WjohEc9apnQ6hMjMck3jlHZIwlu # VYeUQE0qjmApfMtTAEzbMUdY8sLTunL1GkbDSeKn9O7llBGnNtyM1uM9Mdv1VyWh # 0z/IriQKIjntqqGyoF0HvDHOFZCyUDBPLflyiu7Y1zQ/sPounsb96aBfQdq3h3LO # n6t+m9EnNz/G6MzzWvpJk6YgTHTIqeQN/F/XpiPvbfek3nq/PYbL3au+kBfRUHiC # FXSvt6lor0HC626vUmz9ZNPOxwEWLuccomxsy3JwWH79vsM/7ARqoG5h6d6Nahfa # OuRP4XI9xtdH3Pa/NCLyQjxKXyLxzwQzjddkX2EpTJnlypuhPmEdea59Uz2E303L # xyXSnKBvGsAnyWYAfnejr3YAiL9YrN2l2dn198RpA4DCm9QtZYiwC0q2fuUvui34 # PfPIUZByf7wHuuWu50hY9WLx1kOMI8xyo7AI6TaNrnIwggdxMIIFWaADAgECAhMz # AAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9v # dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0z # MDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjAN # BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP9 # 7pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMM # tY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gm # U3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130 # /o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP # 3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7 # vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+A # utuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz # 1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6 # EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/Zc # UlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZy # acaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJ # KwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVd # AF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8G # CCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3Mv # UmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQC # BAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYD # VR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZF # aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9v # Q2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcw # AoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJB # dXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cB # MSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7 # bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/ # SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2 # EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2Fz # Lixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0 # /fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9 # swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJ # Xk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+ # pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW # 4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N # 7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDVjCCAj4CAQEwggEBoYHZpIHWMIHTMQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNy # b3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGll # bGQgVFNTIEVTTjo2RjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRp # bWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAWmTiA01u5mxq/nVxiRJL # MOskVGeggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkq # hkiG9w0BAQsFAAIFAO38M4EwIhgPMjAyNjA3MTEwMzE0MDlaGA8yMDI2MDcxMjAz # MTQwOVowdDA6BgorBgEEAYRZCgQBMSwwKjAKAgUA7fwzgQIBADAHAgEAAgIYYzAH # AgEAAgISZDAKAgUA7f2FAQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZ # CgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQAT # yYU6x9Rwlj67CVE6Pk7czmY8coAXo6NtMMTGr7K3fhmvRIkufW0mjfKVSlLu0dvw # 9PQ6oZcJmnNFGnW9NYwwGhpO8vqw0o4JmrColqrK86C/GtagqzB3DhPDArc7Tq5l # uGb/5+qWD+hjXwKmrTt5vnpiJVJev0Jjp5UGh+BnfxZptZq3TNg8ZPZfhB2dR7sC # 17h5Xjv+nQIR9D44QDyLnnI8XrNkmgSZ7AVJVhFsLJxtSSriDiXaEnR1IHaQ1/Dz # qutQQrcBcKkqN90DOZgajpD+MqmrWhwqkV2+D7NdEW1Qxri3SW+2dqtNRDRacvPS # q2YwMy89wpgp9S4LqCzxMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgUENBIDIwMTACEzMAAAIcCVUV18NZB9EAAQAAAhwwDQYJYIZIAWUDBAIBBQCg # ggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQg # kSJY9bAIe7HbncHi9EVXEM9PMswBjw2EUWDrZR8l66owgfoGCyqGSIb3DQEJEAIv # MYHqMIHnMIHkMIG9BCCgIGkmNhdo7+KE7dWhI+E2Ctx2RLWoYvvJodCIciHHaDCB # mDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAk # BgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAACHAlVFdfD # WQfRAAEAAAIcMCIEIE1BB2Y7iBnFwAQvL1Pf2Vf46xVPtalc+NgN35HXqPYfMA0G # CSqGSIb3DQEBCwUABIICAHiokZnwZ+zvQ2WJtzTKVR1byq2DG8eCcMsaXgE4/cU5 # RcQ3Q4pn+JFyBMcgM641Z9BBmzvM4QfZoeSP241Zc8+1ril2lwHy9pAfPNknd1DM # 1HPBnVLWwNW5P/tXuZRb6Tr2nWCEyrM7QInyOiI8OuhgBcMv9GQqnimKc5BSFcwR # 5PThXnlKecnZUuByUHayEvuWy0YNnNvB+Jm49HSeqUHtLp39MBUCXMC0ZTeUNCZz # G4FtEX6pknDsn4zKE3jaMiA8b/fpq+rR8v744Rh/nVA80msKJibEcy+2kAI75H8t # BqnI4C7Y+BKtiusY10P6O0Tl1wmGoYGBkOiBiFqpsFE05psRbra4iwOjxDjiwq3+ # 2vxjX6lOLWRftI7kX0O9D+GRnhFFA+Qb2VpneU5rmBw0mkEsrvhaNxMWC2+dado5 # hRa1axiNWRsWx6KgnADlnqzrcG/U44+CQZNJ7gaCy3JrgIcMZrE0g3FwyPxcwtXy # MYFb6PNySn6W8P3OE4CRbUvqztY3hbBhHOgq0BHyZWhLIkiz+pKcQnf/ZG041WTR # dzdCpzuVrOjtavABD//0lleQkZq5QFPRJzbdsCI12X+RMoATfjmTO0Gs947qRkol # UfA4p6SeqVOuBIfE8sdFLLwqjhihvhty2izyBhXJw1Y0/VPwEQVNvd7I/MK2AzIF # SIG # End signature block