Functions/PesterState.ps1

function New-PesterState {
    param (
        [String[]]$TagFilter,
        [String[]]$ExcludeTagFilter,
        [String[]]$TestNameFilter,
        [System.Management.Automation.SessionState]$SessionState,
        [Switch]$Strict,
        [Pester.OutputTypes]$Show = 'All',
        [object]$PesterOption,
        [Switch]$RunningViaInvokePester,
        [Hashtable[]] $ScriptBlockFilter
    )

    if ($null -eq $SessionState) {
        $SessionState = Set-SessionStateHint -PassThru  -Hint "Module - Pester (captured in New-PesterState)" -SessionState $ExecutionContext.SessionState
    }

    if ($null -eq $PesterOption) {
        $PesterOption = New-PesterOption
    }
    elseif ($PesterOption -is [System.Collections.IDictionary]) {
        try {
            $PesterOption = New-PesterOption @PesterOption
        }
        catch {
            throw
        }
    }

    & $SafeCommands['New-Module'] -Name PesterState -AsCustomObject -ArgumentList $TagFilter, $ExcludeTagFilter, $TestNameFilter, $SessionState, $Strict, $Show, $PesterOption, $RunningViaInvokePester -ScriptBlock {
        param (
            [String[]]$_tagFilter,
            [String[]]$_excludeTagFilter,
            [String[]]$_testNameFilter,
            [System.Management.Automation.SessionState]$_sessionState,
            [Switch]$Strict,
            [Pester.OutputTypes]$Show,
            [object]$PesterOption,
            [Switch]$RunningViaInvokePester
        )

        #public read-only
        $TagFilter = $_tagFilter
        $ExcludeTagFilter = $_excludeTagFilter
        $TestNameFilter = $_testNameFilter


        $script:SessionState = $_sessionState
        $script:Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
        $script:TestStartTime = $null
        $script:TestStopTime = $null
        $script:CommandCoverage = @()
        $script:Strict = $Strict
        $script:Show = $Show
        $script:InTest = $false

        $script:TestResult = @()

        $script:TotalCount = 0
        $script:Time = [timespan]0
        $script:PassedCount = 0
        $script:FailedCount = 0
        $script:SkippedCount = 0
        $script:PendingCount = 0
        $script:InconclusiveCount = 0

        $script:IncludeVSCodeMarker = $PesterOption.IncludeVSCodeMarker
        $script:TestSuiteName = $PesterOption.TestSuiteName
        $script:ScriptBlockFilter = $PesterOption.ScriptBlockFilter
        $script:RunningViaInvokePester = $RunningViaInvokePester

        $script:SafeCommands = @{}

        $script:SafeCommands['New-Object'] = & (Pester\SafeGetCommand) -Name New-Object          -Module Microsoft.PowerShell.Utility -CommandType Cmdlet
        $script:SafeCommands['Select-Object'] = & (Pester\SafeGetCommand) -Name Select-Object       -Module Microsoft.PowerShell.Utility -CommandType Cmdlet
        $script:SafeCommands['Export-ModuleMember'] = & (Pester\SafeGetCommand) -Name Export-ModuleMember -Module Microsoft.PowerShell.Core    -CommandType Cmdlet
        $script:SafeCommands['Add-Member'] = & (Pester\SafeGetCommand) -Name Add-Member          -Module Microsoft.PowerShell.Utility -CommandType Cmdlet

        function New-TestGroup([string] $Name, [string] $Hint) {
            & $SafeCommands['New-Object'] psobject -Property @{
                Name              = $Name
                Type              = 'TestGroup'
                Hint              = $Hint
                Actions           = [System.Collections.ArrayList]@()
                BeforeEach        = & $SafeCommands['New-Object'] System.Collections.Generic.List[scriptblock]
                AfterEach         = & $SafeCommands['New-Object'] System.Collections.Generic.List[scriptblock]
                BeforeAll         = & $SafeCommands['New-Object'] System.Collections.Generic.List[scriptblock]
                AfterAll          = & $SafeCommands['New-Object'] System.Collections.Generic.List[scriptblock]
                TotalCount        = 0
                StartTime         = $Null
                Time              = [timespan]0
                PassedCount       = 0
                FailedCount       = 0
                SkippedCount      = 0
                PendingCount      = 0
                InconclusiveCount = 0
            }
        }

        $script:TestActions = New-TestGroup -Name Pester -Hint Root
        $script:TestGroupStack = & $SafeCommands['New-Object'] System.Collections.Stack
        $script:TestGroupStack.Push($script:TestActions)

        function EnterTestGroup([string] $Name, [string] $Hint) {
            $newGroup = New-TestGroup @PSBoundParameters
            $newGroup.StartTime = $script:Stopwatch.Elapsed
            $null = $script:TestGroupStack.Peek().Actions.Add($newGroup)

            $script:TestGroupStack.Push($newGroup)
        }

        function LeaveTestGroup([string] $Name, [string] $Hint) {
            $StopTime = $script:Stopwatch.Elapsed
            $currentGroup = $script:TestGroupStack.Pop()

            if ( $Hint -eq 'Script' ) {
                $script:Time += $StopTime - $currentGroup.StartTime
            }

            $currentGroup.Time = $StopTime - $currentGroup.StartTime

            # Removing start time property from output to prevent clutter
            $currentGroup.PSObject.properties.remove('StartTime')

            if ($currentGroup.Name -ne $Name -or $currentGroup.Hint -ne $Hint) {
                throw "TestGroups stack corrupted: Expected name/hint of '$Name','$Hint'. Found '$($currentGroup.Name)', '$($currentGroup.Hint)'."
            }
        }

        function AddTestResult {
            param (
                [string]$Name,
                [ValidateSet("Failed", "Passed", "Skipped", "Pending", "Inconclusive")]
                [string]$Result,
                [Nullable[TimeSpan]]$Time,
                [string]$FailureMessage,
                [string]$StackTrace,
                [string] $ParameterizedSuiteName,
                [System.Collections.IDictionary] $Parameters,
                [System.Management.Automation.ErrorRecord] $ErrorRecord
            )

            # defining this function in here, because otherwise it is not available
            function New-ErrorRecord ([string] $Message, [string] $ErrorId, [string] $File, [string] $Line, [string] $LineText) {
                $exception = & $SafeCommands['New-Object'] Exception $Message
                $errorCategory = [Management.Automation.ErrorCategory]::InvalidResult
                # we use ErrorRecord.TargetObject to pass structured information about the error to a reporting system.
                $targetObject = @{Message = $Message; File = $File; Line = $Line; LineText = $LineText}
                $errorRecord = & $SafeCommands['New-Object'] Management.Automation.ErrorRecord $exception, $ErrorID, $errorCategory, $targetObject
                return $errorRecord
            }

            if ($null -eq $Time) {
                if ( $script:TestStartTime -and $script:TestStopTime ) {
                    $Time = $script:TestStopTime - $script:TestStartTime
                    $script:TestStartTime = $null
                    $script:TestStopTime = [timespan]0
                }
                else {
                    $Time = [timespan]0
                }
            }

            if (-not $script:Strict) {
                $Passed = "Passed", "Skipped", "Pending" -contains $Result
            }
            else {
                $Passed = $Result -eq "Passed"
                if (@("Skipped", "Pending", "Inconclusive") -contains $Result) {
                    $FailureMessage = "The test failed because the test was executed in Strict mode and the result '$result' was translated to Failed."
                    $ErrorRecord = New-ErrorRecord -ErrorId "PesterTest$Result" -Message $FailureMessage
                    $Result = "Failed"
                }

            }

            $script:TotalCount++

            switch ($Result) {
                Passed {
                    $script:PassedCount++; break;
                }
                Failed {
                    $script:FailedCount++; break;
                }
                Skipped {
                    $script:SkippedCount++; break;
                }
                Pending {
                    $script:PendingCount++; break;
                }
                Inconclusive {
                    $script:InconclusiveCount++; break;
                }
            }

            $resultRecord = & $SafeCommands['New-Object'] -TypeName PsObject -Property @{
                Name                   = $Name
                Type                   = 'TestCase'
                Passed                 = $Passed
                Result                 = $Result
                Time                   = $Time
                FailureMessage         = $FailureMessage
                StackTrace             = $StackTrace
                ErrorRecord            = $ErrorRecord
                ParameterizedSuiteName = $ParameterizedSuiteName
                Parameters             = $Parameters
                Show                   = $script:Show
            }

            $null = $script:TestGroupStack.Peek().Actions.Add($resultRecord)

            # Attempting some degree of backward compatibility for the TestResult collection for now; deprecated and will be removed in the future
            $describe = ''
            $contexts = [System.Collections.ArrayList]@()

            # make a copy of the stack and reverse it
            $reversedStack = $script:TestGroupStack.ToArray()
            [array]::Reverse($reversedStack)

            foreach ($group in $reversedStack) {
                if ($group.Hint -eq 'Root' -or $group.Hint -eq 'Script') {
                    continue
                }
                if ($describe -eq '' -or $group.Hint -eq 'Feature') {
                    $describe = $group.Name
                }
                else {
                    $null = $contexts.Add($group.Name)
                }
            }

            $context = $contexts -join '\'

            $script:TestResult += & $SafeCommands['New-Object'] psobject -Property @{
                Describe               = $describe
                Context                = $context
                Name                   = $Name
                Passed                 = $Passed
                Result                 = $Result
                Time                   = $Time
                FailureMessage         = $FailureMessage
                StackTrace             = $StackTrace
                ErrorRecord            = $ErrorRecord
                ParameterizedSuiteName = $ParameterizedSuiteName
                Parameters             = $Parameters
                Show                   = $script:Show
            }
        }

        function AddSetupOrTeardownBlock([scriptblock] $ScriptBlock, [string] $CommandName) {
            $currentGroup = $script:TestGroupStack.Peek()

            $isSetupCommand = IsSetupCommand -CommandName $CommandName
            $isGroupCommand = IsTestGroupCommand -CommandName $CommandName

            if ($isSetupCommand) {
                if ($isGroupCommand) {
                    $currentGroup.BeforeAll.Add($ScriptBlock)
                }
                else {
                    $currentGroup.BeforeEach.Add($ScriptBlock)
                }
            }
            else {
                if ($isGroupCommand) {
                    $currentGroup.AfterAll.Add($ScriptBlock)
                }
                else {
                    $currentGroup.AfterEach.Add($ScriptBlock)
                }
            }
        }

        function IsSetupCommand {
            param ([string] $CommandName)
            return $CommandName -eq 'BeforeEach' -or $CommandName -eq 'BeforeAll'
        }

        function IsTestGroupCommand {
            param ([string] $CommandName)
            return $CommandName -eq 'BeforeAll' -or $CommandName -eq 'AfterAll'
        }

        function GetTestCaseSetupBlocks {
            $blocks = @(
                foreach ($group in $this.TestGroups) {
                    $group.BeforeEach
                }
            )

            return $blocks
        }

        function GetTestCaseTeardownBlocks {
            $groups = @($this.TestGroups)
            [Array]::Reverse($groups)

            $blocks = @(
                foreach ($group in $groups) {
                    $group.AfterEach
                }
            )

            return $blocks
        }

        function GetCurrentTestGroupSetupBlocks {
            return $script:TestGroupStack.Peek().BeforeAll
        }

        function GetCurrentTestGroupTeardownBlocks {
            return $script:TestGroupStack.Peek().AfterAll
        }

        function EnterTest {
            if ($script:InTest) {
                throw 'You are already in a test case.'
            }

            $script:TestStartTime = $script:Stopwatch.Elapsed
            $script:InTest = $true
        }

        function LeaveTest {
            $script:TestStopTime = $script:Stopwatch.Elapsed
            $script:InTest = $false
        }

        $ExportedVariables = "TagFilter",
        "ExcludeTagFilter",
        "TestNameFilter",
        "ScriptBlockFilter",
        "TestResult",
        "SessionState",
        "CommandCoverage",
        "Strict",
        "Show",
        "Time",
        "TotalCount",
        "PassedCount",
        "FailedCount",
        "SkippedCount",
        "PendingCount",
        "InconclusiveCount",
        "IncludeVSCodeMarker",
        "TestActions",
        "TestGroupStack",
        "TestSuiteName",
        "InTest",
        "RunningViaInvokePester"

        $ExportedFunctions = "EnterTestGroup",
        "LeaveTestGroup",
        "AddTestResult",
        "AddSetupOrTeardownBlock",
        "GetTestCaseSetupBlocks",
        "GetTestCaseTeardownBlocks",
        "GetCurrentTestGroupSetupBlocks",
        "GetCurrentTestGroupTeardownBlocks",
        "EnterTest",
        "LeaveTest"

        & $SafeCommands['Export-ModuleMember'] -Variable $ExportedVariables -function $ExportedFunctions
    }  |
        & $SafeCommands['Add-Member'] -PassThru -MemberType ScriptProperty -Name CurrentTestGroup -Value {
        $this.TestGroupStack.Peek()
    } |
        & $SafeCommands['Add-Member'] -PassThru -MemberType ScriptProperty -Name TestGroups -Value {
        $array = $this.TestGroupStack.ToArray()
        [Array]::Reverse($array)
        return $array
    } |
        & $SafeCommands['Add-Member'] -PassThru -MemberType ScriptProperty -Name IndentLevel -Value {
        # We ignore the root node of the stack here, and don't start indenting until after the Script nodes inside the root
        return [Math]::Max(0, $this.TestGroupStack.Count - 2)
    }
}

# SIG # Begin signature block
# MIIZbgYJKoZIhvcNAQcCoIIZXzCCGVsCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUM0YLCmBo7o4/Zz8+JDOhPo7X
# 8d6gghR8MIIE/jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TANBgkqhkiG9w0B
# AQsFADByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFz
# c3VyZWQgSUQgVGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAwMFoXDTMxMDEw
# NjAwMDAwMFowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMu
# MSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFCvQp1dU2UtAxQ
# tSYQ/h3Ib5FrDJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in43Stwhd4CGPN4
# bbx9+cdtCT2+anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6t2HWc+xObTOK
# fF1FLUuxUOZBOjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0TXhG4wODMSlK
# XAwxikqMiMX3MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK02/xWVLwfoYer
# vnpbCiAvSwnJlaeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEAAaOCAbgwggG0
# MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsG
# AQUFBwMIMEEGA1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYBBQUHAgEWG2h0
# dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0tuEgHf4prtLk
# YaWyoiWyyBc1bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNqerwwcQYDVR0f
# BGowaDAyoDCgLoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJl
# ZC10cy5jcmwwMqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFz
# c3VyZWQtdHMuY3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6
# Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NB
# LmNydDANBgkqhkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5nRv1CclF0CiNH
# o6uS0iXEcFm+FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2uHDPYuj1UUp4
# eTZ6J7fz51Kfk6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmDgvoaU+2QzI2h
# F3MN9PNlOXBL85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QRcucbZEnYIpp1
# FUL1LTI4gdr0YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4bTxMd90oNcX6X
# t/Q/hOvB46NJofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aKLzCCBQ0wggP1
# oAMCAQICEAPBBFtdmcD9x4iXgGyKU+cwDQYJKoZIhvcNAQELBQAwcjELMAkGA1UE
# BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj
# ZXJ0LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUg
# U2lnbmluZyBDQTAeFw0yMTAxMjgwMDAwMDBaFw0yNDAxMzEyMzU5NTlaMEsxCzAJ
# BgNVBAYTAkNaMQ4wDAYDVQQHEwVQcmFoYTEVMBMGA1UECgwMSmFrdWIgSmFyZcWh
# MRUwEwYDVQQDDAxKYWt1YiBKYXJlxaEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
# ggEKAoIBAQC154nkzSQ95K1yCflVL3iFQhzw/25ht4o506hK7ATFgvP71ZI+YHce
# gvVoMtaMhJp2/EzXsgxDF7EZBR4Hl9vNbIpLAFSVCK4GBD0DxNCDFrJTPtNohgsA
# STNMcK6t0iunh7MEkaYt1yPgiISA1vcQUMKi51WSUxeWnsUNTkJDZkyM61fETbhy
# CI66xLItaf3OWdyjiOFPq2n8yx+eg1w7GCC/eNYVAjzqtSmiE/xv6Qoj7z9qFyS1
# pAO4cxDRLAD9IcCiYmHOJVgsho3/u4QNNm72ghz7iiRAO5lDoBcZIiLS5RKxJwMG
# nnYbIiAuISZmv4PtrkcSu81Lzmtu81idAgMBAAGjggHEMIIBwDAfBgNVHSMEGDAW
# gBRaxLl7KgqjpepxA8Bg+S32ZXUOWDAdBgNVHQ4EFgQUF2nEZEX1uTrPSD3h5VSJ
# 8g9ef20wDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHcGA1Ud
# HwRwMG4wNaAzoDGGL2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3Vy
# ZWQtY3MtZzEuY3JsMDWgM6Axhi9odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hh
# Mi1hc3N1cmVkLWNzLWcxLmNybDBLBgNVHSAERDBCMDYGCWCGSAGG/WwDATApMCcG
# CCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQB
# MIGEBggrBgEFBQcBAQR4MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj
# ZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB
# /wQCMAAwDQYJKoZIhvcNAQELBQADggEBANuuPZ7LhU/v1GDprUhYch3/fov3sIxp
# wshFvufWbGxUYzxEVQSsZLdFVUzAdsCz3fKInY2ihCwqIoU5vbwKFvV1zvizat/r
# aNn5aa8H34NjEXPHQiyNykOk9CdFgk+zZn+YpeyzBMAEvQR4uB4eDv1USWkwdXPB
# VVZcjM0xEsx9H/ZZRSEGS0x3ue+shvZdPRzoWcuiK8hNcbFZr15hMGi4s0F9IxTZ
# QzoSpNJsBA/vMmkbp2SWeENn49BNx8q760e+ELMfuSBltKs8S2hB9TLrpko3nIvp
# l1323zyR6ZpWK1/FHbGkHRsSJKvOOBdlSL08+KM2kNzXez88eUae+1YwggUwMIIE
# GKADAgECAhAECRgbX9W7ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNV
# BAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdp
# Y2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAe
# Fw0xMzEwMjIxMjAwMDBaFw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUw
# EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
# MTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcg
# Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9ML
# MUkZz9D7RZmxOttE9X/lqJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWs
# DnkoOn7p0WfTxvspJ8fTeyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeK
# iUXULaGj6YgsIJWuHEqHCN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5Tsx
# HM/q8grkV7tKtel05iv+bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sI
# ZD5SlsHyDxL0xY4PwaLoLFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/Rnf
# JZPRAgMBAAGjggHNMIIByTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQE
# AwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYB
# BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0
# cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29t
# L0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMu
# ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAE
# SDBGMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln
# aWNlcnQuY29tL0NQUzAKBghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPA
# YPkt9mV1DlgwHwYDVR0jBBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZI
# hvcNAQELBQADggEBAD7sDVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0P
# xK+L/e8q3yBVN7Dh9tGSdQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK
# 95xGTlz/kLEbBw6RFfu6r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6
# aGivm6dcIFzZcbEMj7uo+MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lF
# luhZHen6dGRrsutmQ9qzsIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmC
# SfdibqFT+hKUGIUukpHqaGxEMrJmoecYpJpkUe8wggUxMIIEGaADAgECAhAKoSXW
# 1jIbfkHkBdo2l8IVMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYD
# VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAi
# BgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xNjAxMDcxMjAw
# MDBaFw0zMTAxMDcxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERp
# Z2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcgQ0EwggEiMA0GCSqG
# SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC90DLuS82Pf92puoKZxTlUKFe2I0rEDgdF
# M1EQfdD5fU1ofue2oPSNs4jkl79jIZCYvxO8V9PD4X4I1moUADj3Lh477sym9jJZ
# /l9lP+Cb6+NGRwYaVX4LJ37AovWg4N4iPw7/fpX786O6Ij4YrBHk8JkDbTuFfAnT
# 7l3ImgtU46gJcWvgzyIQD3XPcXJOCq3fQDpct1HhoXkUxk0kIzBdvOw8YGqsLwfM
# /fDqR9mIUF79Zm5WYScpiYRR5oLnRlD9lCosp+R1PrqYD4R/nzEU1q3V8mTLex4F
# 0IQZchfxFwbvPc3WTe8GQv2iUypPhR3EHTyvz9qsEPXdrKzpVv+TAgMBAAGjggHO
# MIIByjAdBgNVHQ4EFgQU9LbhIB3+Ka7S5GGlsqIlssgXNW4wHwYDVR0jBBgwFoAU
# Reuir/SSy4IxLVGLp6chnfNtyA8wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8B
# Af8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgweQYIKwYBBQUHAQEEbTBrMCQG
# CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKG
# N2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJv
# b3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaGNGh0dHA6Ly9j
# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwUAYD
# VR0gBEkwRzA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
# LmRpZ2ljZXJ0LmNvbS9DUFMwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4IB
# AQBxlRLpUYdWac3v3dp8qmN6s3jPBjdAhO9LhL/KzwMC/cWnww4gQiyvd/MrHwwh
# Wiq3BTQdaq6Z+CeiZr8JqmDfdqQ6kw/4stHYfBli6F6CJR7Euhx7LCHi1lssFDVD
# BGiy23UC4HLHmNY8ZOUfSBAYX4k4YU1iRiSHY4yRUiyvKYnleB/WCxSlgNcSR3Cz
# ddWThZN+tpJn+1Nhiaj1a5bA9FhpDXzIAbG5KHW3mWOFIoxhynmUfln8jA/jb7UB
# JrZspe6HUSHkWGCbugwtK22ixH67xCUrRwIIfEmuE7bhfEJCKMYYVs9BNLZmXbZ0
# e/VWMyIvIjayS6JKldj1po5SMYIEXDCCBFgCAQEwgYYwcjELMAkGA1UEBhMCVVMx
# FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv
# bTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmlu
# ZyBDQQIQA8EEW12ZwP3HiJeAbIpT5zAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIB
# DDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEE
# AYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUra85F1vIZ1Ph
# qK/3ODuLLHtcfGIwDQYJKoZIhvcNAQEBBQAEggEAAOpGrWjKRcDOvq4y35/qoZe+
# mTiTWB95oTC5q5GyJKW72KD+wy0xPDLwzcw9tIGaJUqDLnMjV/S5B0xCRPx9MrCs
# e7ZLUYqNtxO+dHYAuQL5np86q9+DVEPLTqoBqDc6Ian7+YMyz0/fbMkG9jRsBo4i
# Giaw8hGsanuSe6gNO+CKeLJdo13Om6wYqkG+xe7FnqYRpU/xUNNLAucw0UQY0sKb
# f+t8CA0Hq5s3Oe5iqvB4aRGW6zY/zCHIomjGuNpf+Vunl4vG3OWv0+/GqWne8liO
# i39HDsIFfwp9g6Tg7f4lWF36TV5658jYjnS5xtWvAJNT54n/uTVqreIbDgd7waGC
# AjAwggIsBgkqhkiG9w0BCQYxggIdMIICGQIBATCBhjByMQswCQYDVQQGEwJVUzEV
# MBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29t
# MTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5n
# IENBAhANQkrgvjqI/2BAIc4UAPDdMA0GCWCGSAFlAwQCAQUAoGkwGAYJKoZIhvcN
# AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjEwNTI5MTIyODA1WjAv
# BgkqhkiG9w0BCQQxIgQgpwG8MSpone+JXYFGoCCMiZn1Zpn05zTlhCKEaqsGQgIw
# DQYJKoZIhvcNAQEBBQAEggEAFfkh3wM9Wxq3xh68ZzCoob+r68a1QoQXxIhD45tQ
# r7FDqoiSOZT8hB7fEwkn49E3l0fjWYZ99yYroLLz/z3XZEodA8K9brERm0f5+eNx
# y+1nvyYF5g0MtJlcQriFc9tIYmzi4BJURwYqsVZhle0z3DGutFb720ZC52lvxm4g
# boybx/ElyDN9cs+nra0BlLuYbOt1i2+/EccE8WAkPE0niOc6MwjM4Klz9HOOh85J
# wEBS7iLcYWA2wX+orh7knB+6OKuNIhbTcHD2fS1dTa3nb6Q/PHzDaYw3ZrY+4xN1
# jZdEtWLIoI/J45M8SJ25Sefq5+SuSeqn1jzPyTXXE5J1yA==
# SIG # End signature block