AppHandling/Run-TestsInNavContainer.ps1

<#
 .Synopsis
  Run a test suite in a NAV/BC Container
 .Description
 .Parameter containerName
  Name of the container in which you want to run a test suite
 .Parameter tenant
  tenant to use if container is multitenant
 .Parameter companyName
  company to use
 .Parameter profile
  profile to use
 .Parameter credential
  Credentials of the SUPER user if using NavUserPassword authentication
 .Parameter sqlcredential
  SQL Credential if using an external sql server
 .Parameter accesstoken
  If your container is running AAD authentication, you need to specify an accesstoken for the user specified in credential
 .Parameter testSuite
  Name of test suite to run. Default is DEFAULT.
 .Parameter testGroup
  Only supported in 14.x containers or older. Name of test group to run. Wildcards (? and *) are supported. Default is *.
 .Parameter testCodeunit
  Name or ID of test codeunit to run. Wildcards (? and *) are supported. Default is *.
  This parameter will not populate the test suite with the specified codeunit. This is used as a filter on the tests that are already present
  (or otherwise loaded) in the suite.
  This is not to be confused with -testCodeunitRange.
 .Parameter testCodeunitRange
  A BC-compatible filter string to use for loading test codeunits (similar to -extensionId). This is not to be confused with -testCodeunit.
  If you set this parameter to '*', all test codeunits will be loaded.
  This might not work on all versions of BC and only works when using the command-line-testtool.
 .Parameter testFunction
  Name of test function to run. Wildcards (? and *) are supported. Default is *.
 .Parameter ExtensionId
  Specifying an extensionId causes the test tool to run all tests in the app with this app id.
 .Parameter TestRunnerCodeunitId
  Specifying a TestRunnerCodeunitId causes the test tool to switch to this test runner.
 .Parameter XUnitResultFileName
  Filename where the function should place an XUnit compatible result file
 .Parameter AppendToXUnitResultFile
  Specify this switch if you want the function to append to the XUnit compatible result file instead of overwriting it
 .Parameter JUnitResultFileName
  Filename where the function should place an JUnit compatible result file
 .Parameter AppendToJUnitResultFile
  Specify this switch if you want the function to append to the JUnit compatible result file instead of overwriting it
 .Parameter ReRun
  Specify this switch if you want the function to replace an existing test run (of the same test codeunit) in the test result file instead of adding it
 .Parameter AzureDevOps
  Generate Azure DevOps Pipeline compatible output. This setting determines the severity of errors.
 .Parameter GitHubActions
  Generate GitHub Actions compatible output. This setting determines the severity of errors.
 .Parameter detailed
  Include this switch to output success/failure information for all tests.
 .Parameter InteractionTimeout
  Timespan allowed for a single interaction (Running a test codeunit is an interaction). Default is 24 hours.
 .Parameter ReturnTrueIfAllPassed
  Specify this switch if the function should return true/false on whether all tests passes. If not specified, the function returns nothing.
 .Parameter testPage
  ID of the test page to use. Default for 15.x containers is 130455. Default for 14.x containers and earlier is 130409.
 .Parameter culture
  Set the culture when running the tests. Default is en-US. Microsoft tests are written for en-US.
 .Parameter timezone
  Set the timezone when running the tests. Default is current timezone.
 .Parameter debugMode
  Include this switch to output debug information if running the tests fails.
 .Parameter usePublicWebBaseUrl
  Connect to the public Url and not to localhost
 .Parameter disabledTests
  DisabledTests is an array of disabled tests. Example: @( @{ "codeunitName" = "name"; "method" = "*" } )
  If you have the disabledTests in a file, you need to convert the file to Json: -disabledTests (Get-Content $filename | ConvertFrom-Json)
 .Parameter bcAuthContext
  Authorization Context created by New-BcAuthContext. By specifying BcAuthContext and environment, the function will run tests on the online Business Central Environment specified
 .Parameter environment
  Environment to use for the running tests
 .Parameter restartContainerAndRetry
  Include this switch to restart container and retry the operation (everything) on non-recoverable errors.
  This is NOT test failures, but more things like out of memory, communication errors or that kind.
 .Parameter connectFromHost
  Run the Test Runner PS functions on the host connecting to the public Web BaseUrl to allow web debuggers like fiddler to trace connections
 .Example
  Run-TestsInBcContainer -containerName test -credential $credential
 .Example
  Run-TestsInBcContainer -containerName $containername -credential $credential -XUnitResultFileName "c:\ProgramData\BcContainerHelper\$containername.results.xml" -AzureDevOps "warning"
 .Example
  Run-TestsInBcContainer -containerName $containername -credential $credential -JUnitResultFileName "c:\ProgramData\BcContainerHelper\$containername.results.xml" -GitHubActions "warning"
#>

function Run-TestsInBcContainer {
    Param (
        [string] $containerName = $bcContainerHelperConfig.defaultContainerName,
        [string] $compilerFolder = '',
        [Parameter(Mandatory=$false)]
        [string] $tenant = "default",
        [Parameter(Mandatory=$false)]
        [string] $companyName = "",
        [Parameter(Mandatory=$false)]
        [string] $profile = "",
        [Parameter(Mandatory=$false)]
        [PSCredential] $credential = $null,
        [Parameter(Mandatory=$false)]
        [PSCredential] $sqlCredential = $credential,
        [Parameter(Mandatory=$false)]
        [string] $accessToken = "",
        [Parameter(Mandatory=$false)]
        [string] $testSuite = "DEFAULT",
        [Parameter(Mandatory=$false)]
        [string] $testGroup = "*",
        [Parameter(Mandatory=$false)]
        [string] $testCodeunit = "*",
        [Parameter(Mandatory=$false)]
        [string] $testCodeunitRange = "",
        [Parameter(Mandatory=$false)]
        [string] $testFunction = "*",
        [string] $extensionId = "",
        [string] $testRunnerCodeunitId = "",
        [array]  $disabledTests = @(),
        [Parameter(Mandatory=$false)]
        [string] $XUnitResultFileName,
        [switch] $AppendToXUnitResultFile,
        [string] $JUnitResultFileName,
        [switch] $AppendToJUnitResultFile,
        [switch] $ReRun,
        [ValidateSet('no','error','warning')]
        [string] $AzureDevOps = 'no',
        [ValidateSet('no','error','warning')]
        [string] $GitHubActions = 'no',
        [switch] $detailed,
        [timespan] $interactionTimeout = [timespan]::FromHours(24),
        [switch] $returnTrueIfAllPassed,
        [Parameter(Mandatory=$false)]
        [int] $testPage,
        [string] $culture = "en-US",
        [string] $timezone = "",
        [switch] $debugMode = $bcContainerHelperConfig.debugMode,
        [switch] $restartContainerAndRetry,
        [switch] $usePublicWebBaseUrl,
        [string] $useUrl = "",
        [switch] $connectFromHost,
        [Hashtable] $bcAuthContext,
        [string] $environment,
        [switch] $renewClientContextBetweenTests = $bcContainerHelperConfig.renewClientContextBetweenTests
    )

$telemetryScope = InitTelemetryScope -name $MyInvocation.InvocationName -parameterValues $PSBoundParameters -includeParameters @()
try {

    if ($containerName) {
        Write-Host "Using Container"
        $customConfig = Get-BcContainerServerConfiguration -ContainerName $containerName
        $navversion = Get-BcContainerNavversion -containerOrImageName $containerName
        $version = [System.Version]($navversion.split('-')[0])
        $PsTestToolFolder = Join-Path $bcContainerHelperConfig.hostHelperFolder "Extensions\$containerName\PsTestTool"

    }
    elseif ($compilerFolder) {
        Write-Host "Using CompilerFolder"
        $customConfig = $null
        $symbolsFolder = Join-Path $compilerFolder "symbols"
        $baseAppInfo = Get-AppJsonFromAppFile -appFile (Get-ChildItem -Path $symbolsFolder -Filter 'Microsoft_Base Application_*.*.*.*.app').FullName
        $version = [Version]$baseAppInfo.version
        $PsTestToolFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())"
        New-Item $PsTestToolFolder -ItemType Directory | Out-Null
        $testDlls = Join-Path $compilerFolder "dlls/Test Assemblies/*.dll"
        Copy-Item $testDlls -Destination $PsTestToolFolder -Force
        Copy-Item -Path (Join-Path $PSScriptRoot "PsTestFunctions.ps1") -Destination $PsTestToolFolder -Force
        Copy-Item -Path (Join-Path $PSScriptRoot "ClientContext.ps1") -Destination $PsTestToolFolder -Force
    }
    else {
        throw "You must specify either containerName or compilerFolder"
    }

    if ($bcAuthContext -and $environment) {
        if ($environment -like 'https://*') {
            $useUrl = $environment
            if ($bcAuthContext.ContainsKey('Username') -and $bcAuthContext.ContainsKey('Password')) {
                $credential = New-Object System.Management.Automation.PSCredential -ArgumentList $bcAuthContext.Username, $bcAuthContext.Password
                $clientServicesCredentialType = "NavUserPassword"
            }
            if ($bcAuthContext.ContainsKey('ClientServicesCredentialType')) {
                $clientServicesCredentialType = $bcAuthContext.ClientServicesCredentialType
            }
            $testPage = 130455
        }
        else {
            $response = Invoke-RestMethod -Method Get -Uri "$($bcContainerHelperConfig.baseUrl.TrimEnd('/'))/$($bcAuthContext.tenantID)/$environment/deployment/url"
            if($response.status -ne 'Ready') {
                throw "environment not ready, status is $($response.status)"
            }
            $useUrl = $response.data
            if ($testPage) {
                throw "You cannot specify testPage when running tests in an Online tenant"
            }
            $testPage = 130455
        }
        $uri = [Uri]::new($useUrl)
        $useUrl = $useUrl.Split('?')[0]
        $dict = [System.Web.HttpUtility]::ParseQueryString($uri.Query)
        if ($dict['tenant']) { $tenant = $dict['tenant'] }
        if ($dict['testpage']) { $testpage = [int]$dict['testpage'] }
    }
    else {
        $clientServicesCredentialType = $customConfig.ClientServicesCredentialType

        $useTraefik = $false
        $inspect = docker inspect $containerName | ConvertFrom-Json
        if ($inspect.Config.Labels.psobject.Properties.Match('traefik.enable').Count -gt 0) {
            if ($inspect.config.Labels.'traefik.enable' -eq "true") {
                $usePublicWebBaseUrl = ($useUrl -eq "")
                $useTraefik = $true
            }
        }
        if ($usePublicWebBaseUrl -and $useUrl -ne "") {
            throw "You cannot specify usePublicWebBaseUrl and useUrl at the same time"
        }

        if ($customConfig.PublicWebBaseUrl -eq "") {
            throw "Container $containerName needs to include the WebClient in order to run tests (PublicWebBaseUrl is blank)"
        }

        if ($useUrl -eq "") {
            if ([bool]($customConfig.PSobject.Properties.name -eq "EnableTaskScheduler")) {
                if ($customConfig.EnableTaskScheduler -eq "True") {
                    Write-Host -ForegroundColor Red "WARNING: TaskScheduler is running in the container, this can lead to test failures. Specify -EnableTaskScheduler:`$false to disable Task Scheduler."
                }
            }
        }
        if (!$testPage) {
            if ($version.Major -ge 15) {
                $testPage = 130455
            }
            else {
                $testPage = 130409
            }
        }

        if ($clientServicesCredentialType -eq "Windows" -and "$CompanyName" -eq "") {
            $myName = $myUserName.SubString($myUserName.IndexOf('\')+1)
            Get-BcContainerBcUser -containerName $containerName | Where-Object { $_.UserName.EndsWith("\$MyName", [System.StringComparison]::InvariantCultureIgnoreCase) -or $_.UserName -eq $myName } | % {
                $companyName = $_.Company
            }
        }
    
        Invoke-ScriptInBCContainer -containerName $containerName -scriptBlock { Param($timeoutStr)
            $webConfigFile = "C:\inetpub\wwwroot\$WebServerInstance\web.config"
            try {
                $webConfig = [xml](Get-Content $webConfigFile)
                $node = $webConfig.configuration.'system.webServer'.aspNetCore.Attributes.GetNamedItem('requestTimeout')
                if (!($node)) {
                    $node = $webConfig.configuration.'system.webServer'.aspNetCore.Attributes.Append($webConfig.CreateAttribute('requestTimeout'))
                }
                if ($node.Value -ne $timeoutStr) {
                    $node.Value = $timeoutStr
                    $webConfig.Save($webConfigFile)
                }
            }
            catch {
                Write-Host "WARNING: could not set requestTimeout in web.config"
            }
        } -argumentList $interactionTimeout.ToString()
    }

    if ($bcAuthContext -and ($environment -notlike 'https://*')) {
        $bcAuthContext = Renew-BcAuthContext $bcAuthContext
        $accessToken = $bcAuthContext.accessToken
        $credential = New-Object pscredential -ArgumentList $bcAuthContext.upn, (ConvertTo-SecureString -String $accessToken -AsPlainText -Force)
    }

    $PsTestFunctionsPath = Join-Path $PsTestToolFolder "PsTestFunctions.ps1"
    $ClientContextPath = Join-Path $PsTestToolFolder "ClientContext.ps1"
    $fobfile = Join-Path $PsTestToolFolder "PSTestToolPage.fob"

    if ($testPage -eq 130455) {
        if ($testgroup -ne "*" -and $testgroup -ne "") {
            Write-Host -ForegroundColor Red "WARNING: TestGroups are not supported in Business Central 15.x and later"
        }
    }

    If (!(Test-Path -Path $PsTestToolFolder -PathType Container)) {
        try {
            New-Item -Path $PsTestToolFolder -ItemType Directory | Out-Null
    
            Copy-Item -Path (Join-Path $PSScriptRoot "PsTestFunctions.ps1") -Destination $PsTestFunctionsPath -Force
            Copy-Item -Path (Join-Path $PSScriptRoot "ClientContext.ps1") -Destination $ClientContextPath -Force

            if ($version.Major -ge 15) {
                if ($testPage -eq 130409) {
                    Publish-BcContainerApp -containerName $containerName -appFile (Join-Path $PSScriptRoot "Microsoft_PSTestToolPage_15.0.0.0.app") -skipVerification -sync -install
                }
            }
            else {
                if ($version.Major -lt 11) {
                    Copy-Item -Path (Join-Path $PSScriptRoot "PSTestToolPage$($version.Major).fob") -Destination $fobfile -Force
                }
                else {
                    Copy-Item -Path (Join-Path $PSScriptRoot "PSTestToolPage.fob") -Destination $fobfile -Force
                }

                if ($clientServicesCredentialType -eq "Windows") {
                    Import-ObjectsToNavContainer -containerName $containerName -objectsFile $fobfile
                } else {
                    Import-ObjectsToNavContainer -containerName $containerName -objectsFile $fobfile -sqlCredential $sqlCredential
                }
            }
        } catch {
            Remove-Item -Path $PsTestToolFolder -Recurse -Force
            throw
        }
    }

    while ($true) {
        try
        {
            if ($connectFromHost) {
                if ($PSVersionTable.PSVersion.Major -lt 7) {
                    throw "Using ConnectFromHost requires PowerShell 7"
                }
                $newtonSoftDllPath = Join-Path $PsTestToolFolder "Newtonsoft.Json.dll"
                $clientDllPath = Join-Path $PsTestToolFolder "Microsoft.Dynamics.Framework.UI.Client.dll"
                if ($containerName) {
                    if (!((Test-Path $newtonSoftDllPath) -and (Test-Path $clientDllPath))) {
                        Invoke-ScriptInBcContainer -containerName $containerName { Param([string] $myNewtonSoftDllPath, [string] $myClientDllPath)
                    
                            if (!(Test-Path $myNewtonSoftDllPath)) {
                                $newtonSoftDllPath = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Management\Newtonsoft.Json.dll"
                                if (!(Test-Path $newtonSoftDllPath)) {
                                    $newtonSoftDllPath = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Newtonsoft.Json.dll"
                                }
                                $newtonSoftDllPath = (Get-Item $newtonSoftDllPath).FullName
                                Copy-Item -Path $newtonSoftDllPath -Destination $myNewtonSoftDllPath
                            }
                            $clientDllPath = "C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll"
                            if (!(Test-Path $myClientDllPath)) {
                                Copy-Item -Path $clientDllPath -Destination $myClientDllPath
                            }
                        } -argumentList $newtonSoftDllPath, $clientDllPath
                    }
                }
    
                if ($useUrl) {
                    $publicWebBaseUrl = $useUrl.TrimEnd('/')
                }
                else {
                    $publicWebBaseUrl = $customConfig.PublicWebBaseUrl.TrimEnd('/')
                }
                $serviceUrl = "$publicWebBaseUrl/cs?tenant=$tenant"
    
                if ($accessToken) {
                    $clientServicesCredentialType = "AAD"
                    $credential = New-Object pscredential $credential.UserName, (ConvertTo-SecureString -String $accessToken -AsPlainText -Force)
                }
        
                if ($companyName) {
                    $serviceUrl += "&company=$([Uri]::EscapeDataString($companyName))"
                }

                if ($profile) {
                    $serviceUrl += "&profile=$([Uri]::EscapeDataString($profile))"
                }
    
                . $PsTestFunctionsPath -newtonSoftDllPath $newtonSoftDllPath -clientDllPath $clientDllPath -clientContextScriptPath $ClientContextPath
        
                Write-Host "Connecting to $serviceUrl"
                $clientContext = $null
                try {
                    $clientContext = New-ClientContext -serviceUrl $serviceUrl -auth $clientServicesCredentialType -credential $credential -interactionTimeout $interactionTimeout -culture $culture -timezone $timezone -debugMode:$debugMode

                    $Param = @{}
                    if ($renewClientContextBetweenTests) {
                        $Param = @{ "renewClientContext" = { 
                                if ($renewClientContextBetweenTests) {
                                    Write-Host "Renewing Client Context"
                                    Remove-ClientContext -clientContext $clientContext
                                    $clientContext = $null
                                    $clientContext = New-ClientContext -serviceUrl $serviceUrl -auth $clientServicesCredentialType -credential $credential -interactionTimeout $interactionTimeout -culture $culture -timezone $timezone -debugMode:$debugMode
                                    Write-Host "Client Context renewed"
                                }
                                $clientContext
                            }
                        }
                    }

                    $result = Run-Tests @Param -clientContext $clientContext `
                              -TestSuite $testSuite `
                              -TestGroup $testGroup `
                              -TestCodeunit $testCodeunit `
                              -TestCodeunitRange $testCodeunitRange `
                              -TestFunction $testFunction `
                              -ExtensionId $extensionId `
                              -TestRunnerCodeunitId $testRunnerCodeunitId `
                              -DisabledTests $disabledtests `
                              -XUnitResultFileName $XUnitResultFileName `
                              -AppendToXUnitResultFile:$AppendToXUnitResultFile `
                              -JUnitResultFileName $JUnitResultFileName `
                              -AppendToJUnitResultFile:$AppendToJUnitResultFile `
                              -ReRun:$ReRun `
                              -AzureDevOps $AzureDevOps `
                              -GitHubActions $GitHubActions `
                              -detailed:$detailed `
                              -debugMode:$debugMode `
                              -testPage $testPage `
                              -connectFromHost:$connectFromHost
                }
                catch {
                    Write-Host $_.ScriptStackTrace
                    throw
                }
                finally {
                    if ($clientContext) {
                        Remove-ClientContext -clientContext $clientContext
                    }
                }
            }
            else {

                $containerXUnitResultFileName = ""
                if ($XUnitResultFileName) {
                    $containerXUnitResultFileName = Get-BcContainerPath -containerName $containerName -path $XUnitResultFileName
                    if ("$containerXUnitResultFileName" -eq "") {
                        throw "The path for XUnitResultFileName ($XUnitResultFileName) is not shared with the container."
                    }
                }

                $containerJUnitResultFileName = ""
                if ($JUnitResultFileName) {
                    $containerJUnitResultFileName = Get-BcContainerPath -containerName $containerName -path $JUnitResultFileName
                    if ("$containerJUnitResultFileName" -eq "") {
                        throw "The path for JUnitResultFileName ($JUnitResultFileName) is not shared with the container."
                    }
                }

                $result = Invoke-ScriptInBcContainer -containerName $containerName -usePwsh $false -scriptBlock { Param([string] $tenant, [string] $companyName, [string] $profile, [System.Management.Automation.PSCredential] $credential, [string] $accessToken, [string] $testSuite, [string] $testGroup, [string] $testCodeunit, [string] $testCodeunitRange, [string] $testFunction, [string] $PsTestFunctionsPath, [string] $ClientContextPath, [string] $XUnitResultFileName, [bool] $AppendToXUnitResultFile, [string] $JUnitResultFileName, [bool] $AppendToJUnitResultFile, [bool] $ReRun, [string] $AzureDevOps, [string] $GitHubActions, [bool] $detailed, [timespan] $interactionTimeout, $testPage, $version, $culture, $timezone, $debugMode, $usePublicWebBaseUrl, $useUrl, $extensionId, $testRunnerCodeunitId, $disabledtests, $renewClientContextBetweenTests)
    
                    $newtonSoftDllPath = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Management\Newtonsoft.Json.dll"
                    if (!(Test-Path $newtonSoftDllPath)) {
                        $newtonSoftDllPath = "C:\Program Files\Microsoft Dynamics NAV\*\Service\Newtonsoft.Json.dll"
                    }
                    $newtonSoftDllPath = (Get-Item $newtonSoftDllPath).FullName
                    $clientDllPath = "C:\Test Assemblies\Microsoft.Dynamics.Framework.UI.Client.dll"
                    $customConfigFile = Join-Path (Get-Item "C:\Program Files\Microsoft Dynamics NAV\*\Service").FullName "CustomSettings.config"
                    [xml]$customConfig = [System.IO.File]::ReadAllText($customConfigFile)
                    $publicWebBaseUrl = $customConfig.SelectSingleNode("//appSettings/add[@key='PublicWebBaseUrl']").Value.TrimEnd('/')
                    $clientServicesCredentialType = $customConfig.SelectSingleNode("//appSettings/add[@key='ClientServicesCredentialType']").Value
                
                    if ($useUrl) {
                        $serviceUrl = "$($useUrl.TrimEnd('/'))/cs?tenant=$tenant"
                    }
                    elseif ($usePublicWebBaseUrl) {
                        $serviceUrl = "$publicWebBaseUrl/cs?tenant=$tenant"
                    } 
                    else {
                        $uri = [Uri]::new($publicWebBaseUrl)
                        $serviceUrl = "$($Uri.Scheme)://localhost:$($Uri.Port)$($Uri.PathAndQuery)/cs?tenant=$tenant"
                    }
            
                    if ($accessToken) {
                        $clientServicesCredentialType = "AAD"
                        $credential = New-Object pscredential $credential.UserName, (ConvertTo-SecureString -String $accessToken -AsPlainText -Force)
                    }
                    elseif ($clientServicesCredentialType -eq "Windows") {
                        $windowsUserName = whoami
                        $allUsers = @(Get-NAVServerUser -ServerInstance $ServerInstance -tenant $tenant -ErrorAction Ignore)
                        if ($allUsers.count -gt 0) {
                            $NavServerUser = $allUsers | Where-Object { $_.UserName -eq $windowsusername }
                            if (!($NavServerUser)) {
                                Write-Host "Creating $windowsusername as user"
                                New-NavServerUser -ServerInstance $ServerInstance -tenant $tenant -WindowsAccount $windowsusername
                                New-NavServerUserPermissionSet -ServerInstance $ServerInstance -tenant $tenant -WindowsAccount $windowsusername -PermissionSetId SUPER
                            }
                        }
                    }
            
                    if ($companyName) {
                        $serviceUrl += "&company=$([Uri]::EscapeDataString($companyName))"
                    }

                    if ($profile) {
                        $serviceUrl += "&profile=$([Uri]::EscapeDataString($profile))"
                    }
            
                    . $PsTestFunctionsPath -newtonSoftDllPath $newtonSoftDllPath -clientDllPath $clientDllPath -clientContextScriptPath $ClientContextPath

                    Write-Host "Connecting to $serviceUrl"
                    $clientContext = $null
                    try {

                        Disable-SslVerification

                        $clientContext = New-ClientContext -serviceUrl $serviceUrl -auth $clientServicesCredentialType -credential $credential -interactionTimeout $interactionTimeout -culture $culture -timezone $timezone -debugMode:$debugMode

                        $Param = @{}
                        if ($renewClientContextBetweenTests) {
                            $Param = @{ "renewClientContext" = { 
                                    if ($renewClientContextBetweenTests) {
                                        Write-Host "Renewing Client Context"
                                        Remove-ClientContext -clientContext $clientContext
                                        $clientContext = $null
                                        $clientContext = New-ClientContext -serviceUrl $serviceUrl -auth $clientServicesCredentialType -credential $credential -interactionTimeout $interactionTimeout -culture $culture -timezone $timezone -debugMode:$debugMode
                                        Write-Host "Client Context renewed"
                                    }
                                    $clientContext
                                }
                            }
                        }

                        Run-Tests @Param -clientContext $clientContext `
                                  -TestSuite $testSuite `
                                  -TestGroup $testGroup `
                                  -TestCodeunit $testCodeunit `
                                  -TestCodeunitRange $testCodeunitRange `
                                  -TestFunction $testFunction `
                                  -ExtensionId $extensionId `
                                  -TestRunnerCodeunitId $testRunnerCodeunitId `
                                  -DisabledTests $disabledtests `
                                  -XUnitResultFileName $XUnitResultFileName `
                                  -AppendToXUnitResultFile:$AppendToXUnitResultFile `
                                  -JUnitResultFileName $JUnitResultFileName `
                                  -AppendToJUnitResultFile:$AppendToJUnitResultFile `
                                  -ReRun:$ReRun `
                                  -AzureDevOps $AzureDevOps `
                                  -GitHubActions $GitHubActions `
                                  -detailed:$detailed `
                                  -debugMode:$debugMode `
                                  -testPage $testPage `
                                  -connectFromHost:$connectFromHost
                    }
                    catch {
                        Write-Host $_.ScriptStackTrace
                        throw
                    }
                    finally {
                        Enable-SslVerification
                        if ($clientContext) {
                            Remove-ClientContext -clientContext $clientContext
                            $clientContext = $null
                        }
                    }
            
                } -argumentList $tenant, $companyName, $profile, $credential, $accessToken, $testSuite, $testGroup, $testCodeunit, $testCodeunitRange, $testFunction, (Get-BcContainerPath -containerName $containerName -Path $PsTestFunctionsPath), (Get-BCContainerPath -containerName $containerName -path $ClientContextPath), $containerXUnitResultFileName, $AppendToXUnitResultFile, $containerJUnitResultFileName, $AppendToJUnitResultFile, $ReRun, $AzureDevOps, $GitHubActions, $detailed, $interactionTimeout, $testPage, $version, $culture, $timezone, $debugMode, $usePublicWebBaseUrl, $useUrl, $extensionId, $testRunnerCodeunitId, $disabledtests, $renewClientContextBetweenTests.IsPresent
            }
            if ($result -is [array]) {
                0..($result.Count-2) | % { Write-Host $result[$_] }
                $allPassed = $result[$result.Count-1]
            }
            else {
                $allPassed = $result
            }

            if ($returnTrueIfAllPassed) {
                $allPassed
            }
            if (!$allPassed) {
                Remove-BcContainerSession -containerName $containerName
            }
            break
        }
        catch {
            Remove-BcContainerSession $containerName
            if ($restartContainerAndRetry) {
                Write-Host -ForegroundColor Red $_.Exception.Message
                Restart-BcContainer $containerName
                if ($useTraefik) {
                    Write-Host "Waiting for 30 seconds to allow Traefik to pickup restarted container"
                    Start-Sleep -Seconds 30
                }
                $restartContainerAndRetry = $false
            }
            else {
                if ($debugMode) {
                    Write-host $_.ScriptStackTrace
                }
                throw $_.Exception.Message
            }
        }
    }
}
catch {
    TrackException -telemetryScope $telemetryScope -errorRecord $_
    throw
}
finally {
    TrackTrace -telemetryScope $telemetryScope
}
}
Set-Alias -Name Run-TestsInNavContainer -Value Run-TestsInBcContainer
Export-ModuleMember -Function Run-TestsInBcContainer -Alias Run-TestsInNavContainer

# SIG # Begin signature block
# MIImbAYJKoZIhvcNAQcCoIImXTCCJlkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBCMBZBuG5le723
# tpXLXFv3pQC40HqE3mfyp1xYhwD886CCH4QwggWNMIIEdaADAgECAhAOmxiO+dAt
# 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa
# Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E
# MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy
# unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF
# xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1
# 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB
# MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR
# WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6
# nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB
# YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S
# UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x
# q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB
# NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP
# TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC
# AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp
# Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv
# bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB
# LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc
# Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov
# Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy
# oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW
# juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF
# mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z
# twGpn1eqXijiuZQwggYaMIIEAqADAgECAhBiHW0MUgGeO5B5FSCJIRwKMA0GCSqG
# SIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0
# ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBSb290IFI0
# NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5NTlaMFQxCzAJBgNVBAYTAkdC
# MRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVi
# bGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
# ggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjIztNsfvxYB5UXeWUzCxEeAEZG
# bEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NVDgFigOMYzB2OKhdqfWGVoYW3
# haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/36F09fy1tsB8je/RV0mIk8XL/
# tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05ZwmRmTnAO5/arnY83jeNzhP06S
# hdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm+qxp4VqpB3MV/h53yl41aHU5
# pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUedyz8rNyfQJy/aOs5b4s+ac7I
# H60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz44MPZ1f9+YEQIQty/NQd/2yGg
# W+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBMdlyh2n5HirY4jKnFH/9gRvd+
# QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQYMBaAFDLrkpr/NZZILyhAQnAg
# NpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritUpimqF6TNDDAOBgNVHQ8BAf8E
# BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNVHSUEDDAKBggrBgEFBQcDAzAb
# BgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsGA1UdHwREMEIwQKA+oDyGOmh0
# dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nUm9v
# dFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsGAQUFBzAChjpodHRwOi8vY3J0
# LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RSNDYucDdj
# MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0B
# AQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURhw1aVcdGRP4Wh60BAscjW4HL9
# hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0ZdOaWTsyNyBBsMLHqafvIhrCym
# laS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajjcw5+w/KeFvPYfLF/ldYpmlG+
# vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNcWbWDRF/3sBp6fWXhz7DcML4i
# TAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalOhOfCipnx8CaLZeVme5yELg09
# Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJszkyeiaerlphwoKx1uHRzNyE6
# bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z76mKnzAfZxCl/3dq3dUNw4rg3
# sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5JKdGvspbOrTfOXyXvmPL6E52z
# 1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHHj95Ejza63zdrEcxWLDX6xWls
# /GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2Bev6SivBBOHY+uqiirZtg0y9
# ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/L9Uo2bC5a4CH2RwwggZZMIIE
# waADAgECAhANIM3qwHRbWKHw+Zq6JhzlMA0GCSqGSIb3DQEBDAUAMFQxCzAJBgNV
# BAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3Rp
# Z28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwHhcNMjExMDIyMDAwMDAwWhcN
# MjQxMDIxMjM1OTU5WjBdMQswCQYDVQQGEwJESzEUMBIGA1UECAwLSG92ZWRzdGFk
# ZW4xGzAZBgNVBAoMEkZyZWRkeSBLcmlzdGlhbnNlbjEbMBkGA1UEAwwSRnJlZGR5
# IEtyaXN0aWFuc2VuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgYC5
# tlg+VRktRRkahxxaV8+DAd6vHoDpcO6w7yT24lnSoMuA6nR7kgy90Y/sHIwKE9Ww
# t/px/GAY8eBePWjJrFpG8fBtJbXadRTVd/470Hs/q9t+kh6A/0ELj7wYsKSNOyuF
# Poy4rtClOv9ZmrRpoDVnh8Epwg2DpklX2BNzykzBQxIbkpp+xVo2mhPNWDIesntc
# 4/BnSebLGw1Vkxmu2acKkIjYrne/7lsuyL9ue0vk8TGk9JBPNPbGKJvHu9szP9oG
# oH36fU1sEZ+AacXrp+onsyPf/hkkpAMHAhzQHl+5Ikvcus/cDm06twm7VywmZcas
# 2rFAV5MyE6WMEaYAolwAHiPz9WAs2GDhFtZZg1tzbRjJIIgPpR+doTIcpcDBcHnN
# dSdgWKrTkr2f339oT5bnJfo7oVzc/2HGWvb8Fom6LQAqSC11vWmznHYsCm72g+fo
# TKqW8lLDfLF0+aFvToLosrtW9l6Z+l+RQ8MtJ9EHOm2Ny8cFLzZCDZYw32BydwcL
# V5rKdy4Ica9on5xZvyMOLiFwuL4v2V4pjEgKJaGSS/IVSMEGjrM9DHT6YS4/oq9q
# 20rQUmMZZQmGmEyyKQ8t11si8VHtScN5m0Li8peoWfCU9mRFxSESwTWow8d462+o
# 9/SzmDxCACdFwzvfKx4JqDMm55cL+beunIvc0NsCAwEAAaOCAZwwggGYMB8GA1Ud
# IwQYMBaAFA8qyyCHKLjsb0iuK1SmKaoXpM0MMB0GA1UdDgQWBBTZD6uy9ZWIIqQh
# 3srYu1FlUhdM0TAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUE
# DDAKBggrBgEFBQcDAzARBglghkgBhvhCAQEEBAMCBBAwSgYDVR0gBEMwQTA1Bgwr
# BgEEAbIxAQIBAwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9D
# UFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGln
# by5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUF
# BwEBBG0wazBEBggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0
# aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6
# Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4IBgQASEbZACurQeQN8
# WDTR+YyNpoQ29YAbbdBRhhzHkT/1ao7LE0QIOgGR4GwKRzufCAwu8pCBiMOUTDHT
# ezkh0rQrG6khxBX2nSTBL5i4LwKMR08HgZBsbECciABy15yexYWoB/D0H8WuGe63
# PhGWueR4IFPbIz+jEVxfW0Nyyr7bXTecpKd1iprm+TOmzc2E6ab95dkcXdJVx6Zy
# s++QrrOfQ+a57qEXkS/wnjjbN9hukL0zg+g8L4DHLKTodzfiQOampvV8QzbnB7Y8
# YjNcxR9s/nptnlQH3jorNFhktiBXvD62jc8pAIg6wyH6NxSMjtTsn7QhkIp2kusw
# IQwD8hN/fZ/m6gkXZhRJWFr2WRZOz+edZ62Jf25C/NYWscwfBwn2hzRZf1HgyxkX
# Al88dvvUA3kw1T6uo8aAB9IcL6Owiy7q4T+RLRF7oqx0vcw0193Yhq/gPOaUFlqz
# ExP6TQ5TR9XWVPQk+a1B1ATKMLi1JShO6KWTmNkFkgkgpkW69BEwggauMIIElqAD
# AgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYT
# AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy
# dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAz
# MjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK
# Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBS
# U0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDM
# g/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOx
# s+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09ns
# ad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtA
# rF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149z
# k6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6
# OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qh
# HGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1
# KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX
# 6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0
# sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQID
# AQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2F
# L3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08w
# DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEB
# BGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsG
# AQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz
# dGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdp
# Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgG
# BmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+Y
# qUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjY
# C+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0
# FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6
# WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGj
# VoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzp
# SwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwd
# eDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o
# 08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n
# +2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y
# 3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIO
# K+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGwjCCBKqgAwIBAgIQBUSv85SdCDmm
# v9s/X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO
# RGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNB
# NDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0
# MTAxMzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
# bmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZI
# hvcNAQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwv
# SKOXejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsv
# lmbjaedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4Z
# GIX7ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RCh
# G4PBuOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDa
# Y7Mu6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S7
# 69SgLDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0
# rRUGFOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQ
# fqZcClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7Ujip
# mAmhcbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1P
# t7U20clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM6
# 04qDy0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIw
# ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjAL
# BglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYD
# VR0OBBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0
# dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZT
# SEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsG
# AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0
# dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw
# OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa
# 1t6gqbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1O
# tt5s1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4
# JZ0qBXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvP
# nPp44pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3
# NG6wQSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7M
# mJ4ZiQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOG
# v6wnLEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5
# AdzaROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2ny
# Mpqy0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0e
# aHDAdwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9n
# R2XlG3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMYIGPjCCBjoCAQEwaDBUMQsw
# CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJT
# ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2AhANIM3qwHRbWKHw+Zq6
# JhzlMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw
# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG
# AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIMU7KXdTq1a7rHHRnu+MFXw2hd09EX72
# sSnaaCJfO2WMMA0GCSqGSIb3DQEBAQUABIICAEY+BuDCNyWIhCz5nHWIqWvwx32d
# 4txsLbOfEo7+rlXuP1YydfqWN3jVd+K90jq1OHjKqGE/hDsZ229wPO01N6TPXDp/
# QziAZvP6sh3/rR2PD5M6QPW1DhR+BKU/d+S7CijW7qI2sZdWK4YhTJx3unxWA522
# eW3L0bQzzSckTUO5TJjzunYFpr1VJa07muQz2gM2KRLEmSMrYTaBO7GNPLt1s78/
# bs5NvgSZJr5EK9d/0A72VG6S1dQZeizAZKMc5zgPMOdSjtDXAFnNWvbcS7g9DE85
# 8+KCKK0hTIeOXBxRatc63EBGKLSJLIl3cdmmFRR9F25w+uNM2VGONqKrgq25inr5
# UCYaiUoxPqZQIcj6XTP2T+wDuNH12EvIS61Z8ms4WtB5wQDTZUWKrsc1EYVawMd9
# 3U06IVvg6rbv3APCOeVlYhiWKU589bOO3j5lgpc7CunOJqiBRSVr0I+YBwvFdJGW
# C//p+ik93TSL9sAKzIavfQ9w3tVAulY7gbjL7qPVHiW7+Q2nOigRViUacxocIrZG
# Ec/x+tVAyFfAL/14mtOMirzR+BhMjc0x0MU0ulK9Bumnff9WT9bJXj7Ky1163NRx
# 1Fn/8Jw8iJoD+TNNcIWZVh5M0nroiR6gkKDLBZAZrEW8vpBZ4pYianOBPrYIUU+0
# +KpIgZvGJtLDRTk+oYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzEL
# MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE
# aWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBD
# QQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJ
# AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDkwNzA1NTMwMFowLwYJ
# KoZIhvcNAQkEMSIEIDfLXc6vkiPW1bYLHzgKlzSZqLGsr3yW4WGMtrzIBPBTMA0G
# CSqGSIb3DQEBAQUABIICAHDjWWqwHOSwMtMTJsbr16nBi5KXT1GFSM0CcYLCO+Tp
# YvtQiuY0TJrnzFsTyVc8fqq7J352/XxJq1PoONxgnxUC+HPkc4gCoHREA0FI0uTZ
# PqKufMnP8bFosGPIg6E7WnF5SM1Va5QuLxgHmN7/m0Lj3QZSaDIV241zcA5nCb48
# GccVZ3do3WXkKayyiPIqPuVPRmZMGWIknIS7mHCAvoNQTB9NYoFfO+y3rTTUvxZ2
# Q8EwjqWd73Ob56b3O04CC7+hF++y7lsco4boWi5mqbxjhEwb6YFhJMVlzop2Tp8M
# iFrgCrYw0fw4OcKtlazPP7FmvmUjiaLRdaNVryXb94o14FUr7HLvU8TsXZM0t2IW
# 0XY70IEOJksqJEMi4wuzsNXh9Bia6EU0X/uaHFJXPoZ/BeYXnsTPPlFdhhTcJvHd
# aiMWCcY/+c7XEXrB4KSXnyn9i0YGX+ak/69gISI3mM5RYjDl8YxVTvtyJ/KyN/qY
# LQkQFpJJEnqHZBhakdrYgEbh7JaLkiW0mlniXfTPrnyPwMErSf1Rmp1GzT8GJgNI
# +OYNFSFCU4jaHdBFKApVarMAbSodYbnwEyASh2QdJUlGhXnrfY4MeNd3E3XuBFxQ
# veL6iXaPiCdNK+rB5AY6asArIc2j7gaJ2SwyB6QJLCg49pnXYLbXkMVms8lgn/Zb
# SIG # End signature block