From c6b35ab6629675972a1fcb2c92628690ae305613 Mon Sep 17 00:00:00 2001 From: Jakub Jares Date: Fri, 17 Apr 2026 17:59:58 +0200 Subject: [PATCH 01/56] Migrate Pester tests from v4 to v5 Migrate the PowerShell test suite from Pester 4.99 to Pester 5.7.1. 207 test files updated for Pester 5's discovery/run execution model, plus build.psm1 and tools/ci.psm1 for the version bump and PesterConfiguration-based invocation. No test logic, assertions, or expected behaviors were changed. Key migration patterns applied across 207 test files: - Add BeforeDiscovery blocks for test-case data (260 blocks) - Move setup code from script scope into BeforeAll (236 blocks) - Remove duplicate variable assignments (49 files) - Fix test hangs from Pester 5 stricter execution (4 hangs resolved) - Replace ping with pwsh in Start-Process.Tests (firewall popups) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build.psm1 | 27 +- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 430 ++-- test/powershell/Host/Base-Directory.Tests.ps1 | 8 +- test/powershell/Host/ConsoleHost.Tests.ps1 | 19 +- test/powershell/Host/Logging.Tests.ps1 | 94 +- test/powershell/Host/Read-Host.Tests.ps1 | 13 +- .../TabCompletion/TabCompletion.Tests.ps1 | 267 +- .../Classes/ProtectedAccess.Tests.ps1 | 11 +- .../Scripting.Classes.Attributes.Tests.ps1 | 64 +- .../Scripting.Classes.BasicParsing.Tests.ps1 | 260 +- .../Classes/Scripting.Classes.Break.Tests.ps1 | 12 +- .../Scripting.Classes.Exceptions.Tests.ps1 | 30 +- .../Scripting.Classes.MiscOps.Tests.ps1 | 54 +- .../Scripting.Classes.Modules.Tests.ps1 | 49 +- .../Scripting.Classes.RunPath.Tests.ps1 | 10 +- .../scripting.Classes.NestedModules.tests.ps1 | 8 +- .../scripting.Classes.inheritance.tests.ps1 | 50 +- .../Interop/DotNet/DotNetInterop.Tests.ps1 | 4 +- .../Operators/NullConditional.Tests.ps1 | 2 +- .../Operators/PipelineChainOperator.Tests.ps1 | 178 +- .../Operators/SplitOperator.Tests.ps1 | 2 +- .../Operators/TernaryOperator.Tests.ps1 | 2 +- .../Language/Parser/BNotOperator.Tests.ps1 | 164 +- .../Language/Parser/Conversions.Tests.ps1 | 450 ++-- .../Parser/ExtensibleCompletion.Tests.ps1 | 430 ++-- .../LanguageAndParser.TestFollowup.Tests.ps1 | 79 +- .../Parser/MethodInvocation.Tests.ps1 | 16 +- .../Parser/ParameterBinding.Tests.ps1 | 38 +- .../Language/Parser/Parser.Tests.ps1 | 964 ++++---- .../Language/Parser/Parsing.Tests.ps1 | 18 +- .../Parser/RedirectionOperator.Tests.ps1 | 48 +- .../Language/Parser/TypeAccelerator.Tests.ps1 | 24 +- .../Language/Parser/UsingAssembly.Tests.ps1 | 15 +- .../Scripting/ActionPreference.Tests.ps1 | 72 +- .../Scripting/CmdletDeclaration.Tests.ps1 | 199 +- .../Scripting/CommonParameters.Tests.ps1 | 22 +- .../Debugging/DebuggerScriptTests.Tests.ps1 | 408 +-- .../Scripting/Debugging/Debugging.Tests.ps1 | 165 +- .../Debugging/DebuggingInHost.Tests.ps1 | 44 +- .../Language/Scripting/Delegates.Tests.ps1 | 30 +- .../Scripting/ErrorHistoryId.Tests.ps1 | 21 +- .../Scripting/ErrorPosition.Tests.ps1 | 3 +- ...htableToPSCustomObjectConversion.Tests.ps1 | 92 +- .../Language/Scripting/I18n/I18n.Tests.ps1 | 4 +- .../Language/Scripting/LineEndings.Tests.ps1 | 200 +- .../NativeCommandArguments.Tests.ps1 | 15 +- .../NativeCommandPathUpdate.Tests.ps1 | 143 +- .../NativeCommandProcessor.Tests.ps1 | 2 +- .../NativeLinuxCommands.Tests.ps1 | 2 +- .../NativeExecution/NativeStreams.Tests.ps1 | 16 +- .../NativeUnixGlobbing.Tests.ps1 | 10 +- .../OrderedAttributeForHashTables.Tests.ps1 | 24 +- .../Scripting/OutErrorVariable.Tests.ps1 | 89 +- .../Language/Scripting/Requires.Tests.ps1 | 116 +- .../Language/Scripting/ScriptHelp.Tests.ps1 | 565 +++-- .../CompatiblePSEditions.Module.Tests.ps1 | 84 +- .../Microsoft.PowerShell.Core/Exec.Tests.ps1 | 28 +- .../Get-Module.Tests.ps1 | 83 +- .../History.Tests.ps1 | 4 +- .../Import-Module.Tests.ps1 | 29 +- .../ModuleConstraint.Tests.ps1 | 142 +- .../PSSessionConfiguration.Tests.ps1 | 128 +- ...ster.Commands.Cmdlets.GetCommand.Tests.ps1 | 27 +- .../Remove-Module.Tests.ps1 | 111 +- .../Where-Object.Tests.ps1 | 49 +- .../Export-Counter.Tests.ps1 | 187 +- .../Get-Counter.Tests.ps1 | 198 +- .../Import-Counter.Tests.ps1 | 371 +-- ...Cmdlets.LocalAccounts.LocalGroup.Tests.ps1 | 78 +- ...s.LocalAccounts.LocalGroupMember.Tests.ps1 | 103 +- ....Cmdlets.LocalAccounts.LocalUser.Tests.ps1 | 83 +- .../Add-Content.Tests.ps1 | 6 +- .../Clear-Content.Tests.ps1 | 82 +- .../Convert-Path.Tests.ps1 | 53 +- .../Copy-Item.Tests.ps1 | 233 +- .../FileSystem.Tests.ps1 | 185 +- .../Get-ChildItem.Tests.ps1 | 52 +- .../Get-ComputerInfo.Tests.ps1 | 2179 +++++++++-------- .../Get-Content.Tests.ps1 | 60 +- .../Get-Item.Tests.ps1 | 27 +- .../Get-ItemProperty.Tests.ps1 | 14 +- .../Get-Location.Tests.ps1 | 4 +- .../Get-PSProvider.Tests.ps1 | 2 +- .../Get-Service.Tests.ps1 | 8 +- .../Hierarchical-Path.Tests.ps1 | 2 +- .../ItemProperty.Tests.ps1 | 2 +- .../Join-Path.Tests.ps1 | 20 +- .../Move-Item.Tests.ps1 | 4 +- .../New-Item.Tests.ps1 | 84 +- .../Pop-Location.Tests.ps1 | 8 +- .../Push-Location.Tests.ps1 | 8 +- .../Registry.Tests.ps1 | 20 +- .../Rename-Computer.Tests.ps1 | 38 +- .../Rename-Item.Tests.ps1 | 6 +- .../Resolve-Path.Tests.ps1 | 152 +- .../Restart-Computer.Tests.ps1 | 158 +- .../Set-Item.Tests.ps1 | 28 +- .../Start-Process.Tests.ps1 | 58 +- .../Stop-Computer.Tests.ps1 | 82 +- .../Unimplemented-Cmdlet.Tests.ps1 | 40 +- .../UnixStat.Tests.ps1 | 22 +- .../AclCmdlets.Tests.ps1 | 2 +- .../CmsMessage2.Tests.ps1 | 10 +- .../ConstrainedLanguageDebugger.Tests.ps1 | 40 +- .../ConstrainedLanguageModules.Tests.ps1 | 84 +- .../ConstrainedLanguageRestriction.Tests.ps1 | 40 +- .../ExecutionPolicy.Tests.ps1 | 733 +++--- .../FileCatalog.Tests.ps1 | 36 +- .../Compare-Object.Tests.ps1 | 18 +- .../ConvertFrom-Json.Tests.ps1 | 44 +- .../ConvertFrom-StringData.Tests.ps1 | 6 +- .../ConvertTo-Csv.Tests.ps1 | 4 +- .../ConvertTo-Html.Tests.ps1 | 10 +- .../ConvertTo-Json.Tests.ps1 | 4 + .../Foreach-Object-Parallel.Tests.ps1 | 9 +- .../Format-Custom.Tests.ps1 | 10 +- .../Format-Hex.Tests.ps1 | 1119 ++++----- .../Format-List.Tests.ps1 | 6 +- .../Format-Table.Tests.ps1 | 2 +- .../Format-Wide.Tests.ps1 | 6 +- .../Get-Alias.Tests.ps1 | 8 +- .../Get-Date.Tests.ps1 | 2 +- .../Get-FileHash.Tests.ps1 | 4 +- .../Get-FormatData.Tests.ps1 | 19 +- .../Get-Member.Tests.ps1 | 16 +- .../Get-PSBreakpoint.Tests.ps1 | 7 +- .../Get-Random.Tests.ps1 | 114 +- .../Get-SecureRandom.Tests.ps1 | 114 +- .../Implicit.Remoting.Tests.ps1 | 27 +- .../Import-Alias.Tests.ps1 | 8 +- .../Import-Csv.Tests.ps1 | 40 +- .../Import-LocalizedData.Tests.ps1 | 9 +- .../ImportExportCSV.Delimiter.Tests.ps1 | 24 +- .../Invoke-Item.Tests.ps1 | 12 +- .../Json.Tests.ps1 | 1846 +++++++------- .../JsonObject.Tests.ps1 | 4 +- .../MarkdownCmdlets.Tests.ps1 | 32 +- .../Measure-Object.Tests.ps1 | 26 +- .../New-Object.Tests.ps1 | 26 +- .../New-TimeSpan.Tests.ps1 | 34 +- .../PowerShellData.tests.ps1 | 10 +- .../RunspaceBreakpointManagement.Tests.ps1 | 16 +- .../Select-Object.Tests.ps1 | 8 +- .../Select-String.Tests.ps1 | 6 +- .../Select-Xml.Tests.ps1 | 32 +- .../Send-MailMessage.Tests.ps1 | 110 +- .../Set-Alias.Tests.ps1 | 4 +- .../Set-PSBreakpoint.Tests.ps1 | 26 +- .../Set-Variable.Tests.ps1 | 2 +- .../Sort-Object.Tests.ps1 | 221 +- .../Start-Sleep.Tests.ps1 | 6 +- .../Tee-Object.Tests.ps1 | 6 +- .../Test-Json.Tests.ps1 | 85 +- .../Unimplemented-Cmdlet.Tests.ps1 | 17 +- .../WebCmdlets.Tests.ps1 | 304 +-- .../Write-Host.Tests.ps1 | 25 +- .../Write-Output.Tests.ps1 | 8 +- .../Write-Stream.Tests.ps1 | 28 +- .../alias.tests.ps1 | 95 +- .../clixml.tests.ps1 | 56 +- .../command.tests.ps1 | 4 +- .../object.tests.ps1 | 31 +- .../typedata.tests.ps1 | 4 +- .../ConfigProvider.Tests.ps1 | 99 +- .../PowerShellGet/PowerShellGet.Tests.ps1 | 353 +-- .../Modules/ThreadJob/ThreadJob.Tests.ps1 | 40 +- .../Provider/ProviderIntrinsics.Tests.ps1 | 2 +- test/powershell/SDK/PSDebugging.Tests.ps1 | 47 +- .../GetMethodInfoOverloadDefinition.Tests.ps1 | 62 +- .../Api/SuspiciousContentChecker.Tests.ps1 | 10 +- .../engine/Api/TypeInference.Tests.ps1 | 128 +- .../Assembly.LoadWithPartialName.Tests.ps1 | 32 +- .../engine/Basic/CommandDiscovery.Tests.ps1 | 89 +- .../engine/Basic/DefaultCommands.Tests.ps1 | 131 +- .../engine/Basic/Encoding.Tests.ps1 | 38 +- .../Basic/GroupPolicySettings.Tests.ps1 | 24 +- .../Basic/NativeCommandBytePiping.Tests.ps1 | 6 +- .../NativeCommandErrorHandling.Tests.ps1 | 19 +- .../engine/Basic/ProxyCommand.tests.ps1 | 2 +- .../engine/Basic/SemanticVersion.Tests.ps1 | 18 +- .../Basic/StandardLibraryTypes.Tests.ps1 | 12 +- .../engine/Basic/Telemetry.Tests.ps1 | 18 +- .../engine/Basic/ValidateAttributes.Tests.ps1 | 41 +- test/powershell/engine/Cdxml/Cdxml.Tests.ps1 | 82 +- test/powershell/engine/ETS/Adapter.Tests.ps1 | 7 +- .../engine/Formatting/ErrorView.Tests.ps1 | 9 +- .../engine/Formatting/PSStyle.Tests.ps1 | 3 +- .../Help/HelpSystem.OnlineHelp.Tests.ps1 | 62 +- .../engine/Help/HelpSystem.Tests.ps1 | 218 +- test/powershell/engine/Job/Jobs.Tests.ps1 | 91 +- .../engine/Module/ModulePath.Tests.ps1 | 29 +- .../Module/ModuleSpecification.Tests.ps1 | 68 +- .../engine/Module/NewModuleManifest.Tests.ps1 | 26 +- .../Module/SubmodulePathInManifest.Tests.ps1 | 63 +- .../Module/TestModuleManifest.Tests.ps1 | 116 +- .../BooleanParameterDCR.Tests.ps1 | 26 +- .../NullableBooleanDCR.Tests.ps1 | 26 +- .../ParameterBinding.Tests.ps1 | 29 +- .../StaticParameterBinder.Tests.ps1 | 4 +- .../engine/Remoting/PSSession.Tests.ps1 | 36 +- .../Remoting/RemoteSession.Basic.Tests.ps1 | 99 +- .../engine/Remoting/RunspacePool.Tests.ps1 | 8 +- .../engine/ResourceValidation/TestRunner.ps1 | 77 +- .../engine/Security/FileSignature.Tests.ps1 | 24 +- .../Security/UntrustedDataMode.Tests.ps1 | 36 +- .../engine/WildcardPattern.Tests.ps1 | 6 + .../HelpersLanguage/HelpersLanguage.psm1 | 169 +- tools/ci.psm1 | 1 + 208 files changed, 10512 insertions(+), 9057 deletions(-) diff --git a/build.psm1 b/build.psm1 index d353eb251e5..24cfcdd5803 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1149,7 +1149,7 @@ function Restore-PSPester [ValidateNotNullOrEmpty()] [string] $Destination = ([IO.Path]::Combine((Split-Path (Get-PSOptions -DefaultToNew).Output), "Modules")) ) - Save-Module -Name Pester -Path $Destination -Repository PSGallery -MaximumVersion 4.99 + Save-Module -Name Pester -Path $Destination -Repository PSGallery -RequiredVersion 5.7.1 } function Compress-TestContent { @@ -1737,7 +1737,7 @@ function Start-PSPester { Switch-PSNugetConfig -Source Public } - if (-not (Get-Module -ListAvailable -Name $Pester -ErrorAction SilentlyContinue | Where-Object { $_.Version -ge "4.2" } )) + if (-not (Get-Module -ListAvailable -Name $Pester -ErrorAction SilentlyContinue | Where-Object { $_.Version -ge "5.0" } )) { Restore-PSPester } @@ -1838,25 +1838,32 @@ function Start-PSPester { } } - $command += "Invoke-Pester " + $command += "`$pesterConfig = [PesterConfiguration]@{" + $command += " Run = @{ Path = @('" + ($Path -join "','") + "') }" + $command += "; TestResult = @{ Enabled = `$true; OutputPath = '${OutputFile}'; OutputFormat = '${OutputFormat}' }" - $command += "-OutputFormat ${OutputFormat} -OutputFile ${OutputFile} " if ($ExcludeTag -and ($ExcludeTag -ne "")) { - $command += "-ExcludeTag @('" + (${ExcludeTag} -join "','") + "') " + $command += "; Filter = @{ ExcludeTag = @('" + (${ExcludeTag} -join "','") + "')" + if ($Tag) { + $command += "; Tag = @('" + (${Tag} -join "','") + "')" + } + $command += " }" } - if ($Tag) { - $command += "-Tag @('" + (${Tag} -join "','") + "') " + elseif ($Tag) { + $command += "; Filter = @{ Tag = @('" + (${Tag} -join "','") + "') }" } + # sometimes we need to eliminate Pester output, especially when we're # doing a daily build as the log file is too large if ( $Quiet ) { - $command += "-Quiet " + $command += "; Output = @{ Verbosity = 'None' }" } + if ( $PassThru ) { - $command += "-PassThru " + $command += "; Run = @{ PassThru = `$true; Path = @('" + ($Path -join "','") + "') }" } - $command += "'" + ($Path -join "','") + "'" + $command += " }; Invoke-Pester -Configuration `$pesterConfig" if ($Unelevate) { $command += " *> $outputBufferFilePath; '__UNELEVATED_TESTS_THE_END__' >> $outputBufferFilePath" diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 1aa87ba3d69..e352ae97996 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -6,133 +6,135 @@ Describe "SSHRemoting Basic Tests" -tags CI { # SSH remoting is set up to automatically authenticate current user via SSH keys # All tests connect back to localhost machine - $script:TestConnectingTimeout = 5000 # Milliseconds + BeforeAll { + $script:TestConnectingTimeout = 5000 # Milliseconds - function RestartSSHDService - { - if ($IsWindows) + function RestartSSHDService { - Write-Verbose -Verbose "Restarting Windows SSHD service..." - Restart-Service sshd - Write-Verbose -Verbose "SSHD service status: $(Get-Service sshd | Out-String)" - } - else - { - Write-Verbose -Verbose "Restarting Unix SSHD service..." - sudo service ssh restart - $status = sudo service ssh status - Write-Verbose -Verbose "SSHD service status: $status" + if ($IsWindows) + { + Write-Verbose -Verbose "Restarting Windows SSHD service..." + Restart-Service sshd + Write-Verbose -Verbose "SSHD service status: $(Get-Service sshd | Out-String)" + } + else + { + Write-Verbose -Verbose "Restarting Unix SSHD service..." + sudo service ssh restart + $status = sudo service ssh status + Write-Verbose -Verbose "SSHD service status: $status" + } } - } - function TryNewPSSession - { - param( - [string[]] $HostName, - [string[]] $Name, - [int] $Port, - [string] $UserName, - [string] $KeyFilePath, - [string] $Subsystem - ) - - Write-Verbose -Verbose "Starting TryNewPSSession ..." - - # Try creating a new SSH connection - $timeout = $script:TestConnectingTimeout - $connectionError = $null - $session = $null - $count = 0 - while (($null -eq $session) -and ($count++ -lt 2)) + function TryNewPSSession { - $session = New-PSSession @PSBoundParameters -ConnectingTimeout $timeout -ErrorVariable connectionError -ErrorAction SilentlyContinue - if ($null -eq $session) - { - Write-Verbose -Verbose "SSH New-PSSession remoting connect failed." + param( + [string[]] $HostName, + [string[]] $Name, + [int] $Port, + [string] $UserName, + [string] $KeyFilePath, + [string] $Subsystem + ) - if ($count -eq 1) + Write-Verbose -Verbose "Starting TryNewPSSession ..." + + # Try creating a new SSH connection + $timeout = $script:TestConnectingTimeout + $connectionError = $null + $session = $null + $count = 0 + while (($null -eq $session) -and ($count++ -lt 2)) + { + $session = New-PSSession @PSBoundParameters -ConnectingTimeout $timeout -ErrorVariable connectionError -ErrorAction SilentlyContinue + if ($null -eq $session) { - # Try restarting sshd service - RestartSSHDService + Write-Verbose -Verbose "SSH New-PSSession remoting connect failed." + + if ($count -eq 1) + { + # Try restarting sshd service + RestartSSHDService + } } } + + if ($null -eq $session) + { + $message = "New-PSSession unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Exception.Message)" + throw [System.Management.Automation.PSInvalidOperationException]::new($message) + } + + Write-Verbose -Verbose "SSH New-PSSession remoting connect succeeded." + Write-Output $session } - if ($null -eq $session) + function TryNewPSSessionHash { - $message = "New-PSSession unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Exception.Message)" - throw [System.Management.Automation.PSInvalidOperationException]::new($message) - } + param ( + [hashtable[]] $SSHConnection, + [string[]] $Name + ) - Write-Verbose -Verbose "SSH New-PSSession remoting connect succeeded." - Write-Output $session - } + Write-Verbose -Verbose "Starting TryNewPSSessionHash ..." - function TryNewPSSessionHash - { - param ( - [hashtable[]] $SSHConnection, - [string[]] $Name - ) + foreach ($connect in $SSHConnection) + { + $connect.Add('ConnectingTimeout', $script:TestConnectingTimeout) + } - Write-Verbose -Verbose "Starting TryNewPSSessionHash ..." + # Try creating a new SSH connection + $connectionError = $null + $session = $null + $count = 0 + while (($null -eq $session) -and ($count++ -lt 2)) + { + $session = New-PSSession @PSBoundParameters -ErrorVariable connectionError -ErrorAction SilentlyContinue + if ($null -eq $session) + { + Write-Verbose -Verbose "SSH New-PSSession remoting connect failed." - foreach ($connect in $SSHConnection) - { - $connect.Add('ConnectingTimeout', $script:TestConnectingTimeout) - } + if ($count -eq 1) + { + # Try restarting sshd service + RestartSSHDService + } + } + } - # Try creating a new SSH connection - $connectionError = $null - $session = $null - $count = 0 - while (($null -eq $session) -and ($count++ -lt 2)) - { - $session = New-PSSession @PSBoundParameters -ErrorVariable connectionError -ErrorAction SilentlyContinue if ($null -eq $session) { - Write-Verbose -Verbose "SSH New-PSSession remoting connect failed." - - if ($count -eq 1) - { - # Try restarting sshd service - RestartSSHDService - } + $message = "New-PSSession unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Exception.Message)" + throw [System.Management.Automation.PSInvalidOperationException]::new($message) } - } - if ($null -eq $session) - { - $message = "New-PSSession unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Exception.Message)" - throw [System.Management.Automation.PSInvalidOperationException]::new($message) + Write-Verbose -Verbose "SSH New-PSSession remoting connect succeeded." + Write-Output $session } - Write-Verbose -Verbose "SSH New-PSSession remoting connect succeeded." - Write-Output $session - } + function VerifySession { + param ( + [System.Management.Automation.Runspaces.PSSession] $session + ) - function VerifySession { - param ( - [System.Management.Automation.Runspaces.PSSession] $session - ) + if ($null -eq $session) + { + return + } - if ($null -eq $session) - { - return - } + Write-Verbose -Verbose "VerifySession called for session: $($session.Id)" - Write-Verbose -Verbose "VerifySession called for session: $($session.Id)" - - $session.State | Should -BeExactly 'Opened' - $session.ComputerName | Should -BeExactly 'localhost' - $session.Transport | Should -BeExactly 'SSH' - Write-Verbose -Verbose "Invoking whoami" - Invoke-Command -Session $session -ScriptBlock { whoami } | Should -BeExactly $(whoami) - Write-Verbose -Verbose "Invoking PSSenderInfo" - $psRemoteVersion = Invoke-Command -Session $session -ScriptBlock { $PSSenderInfo.ApplicationArguments.PSVersionTable.PSVersion } - $psRemoteVersion.Major | Should -BeExactly $PSVersionTable.PSVersion.Major - $psRemoteVersion.Minor | Should -BeExactly $PSVersionTable.PSVersion.Minor - Write-Verbose -Verbose "VerifySession complete" + $session.State | Should -BeExactly 'Opened' + $session.ComputerName | Should -BeExactly 'localhost' + $session.Transport | Should -BeExactly 'SSH' + Write-Verbose -Verbose "Invoking whoami" + Invoke-Command -Session $session -ScriptBlock { whoami } | Should -BeExactly $(whoami) + Write-Verbose -Verbose "Invoking PSSenderInfo" + $psRemoteVersion = Invoke-Command -Session $session -ScriptBlock { $PSSenderInfo.ApplicationArguments.PSVersionTable.PSVersion } + $psRemoteVersion.Major | Should -BeExactly $PSVersionTable.PSVersion.Major + $psRemoteVersion.Minor | Should -BeExactly $PSVersionTable.PSVersion.Minor + Write-Verbose -Verbose "VerifySession complete" + } } Context "New-PSSession Tests" { @@ -251,86 +253,88 @@ Describe "SSHRemoting Basic Tests" -tags CI { #> } - function TryCreateRunspace - { - param ( - [string] $UserName, - [string] $ComputerName, - [string] $KeyFilePath, - [int] $Port, - [string] $Subsystem - ) - - Write-Verbose -Verbose "Starting TryCreateRunspace ..." - - $timeout = $script:TestConnectingTimeout - $connectionError = $null - $count = 0 - $rs = $null - $ci = [System.Management.Automation.Runspaces.SSHConnectionInfo]::new($UserName, $ComputerName, $KeyFilePath, $Port, $Subsystem, $timeout) - while (($null -eq $rs) -and ($count++ -lt 2)) + BeforeAll { + function TryCreateRunspace { - try - { - $rs = [runspacefactory]::CreateRunspace($host, $ci) - $null = $rs.Open() - } - catch - { - $connectionError = $_ - $rs = $null - Write-Verbose -Verbose "SSH Runspace Open remoting connect failed." + param ( + [string] $UserName, + [string] $ComputerName, + [string] $KeyFilePath, + [int] $Port, + [string] $Subsystem + ) + + Write-Verbose -Verbose "Starting TryCreateRunspace ..." - if ($count -eq 1) + $timeout = $script:TestConnectingTimeout + $connectionError = $null + $count = 0 + $rs = $null + $ci = [System.Management.Automation.Runspaces.SSHConnectionInfo]::new($UserName, $ComputerName, $KeyFilePath, $Port, $Subsystem, $timeout) + while (($null -eq $rs) -and ($count++ -lt 2)) + { + try { - # Try restarting sshd service - RestartSSHDService + $rs = [runspacefactory]::CreateRunspace($host, $ci) + $null = $rs.Open() + } + catch + { + $connectionError = $_ + $rs = $null + Write-Verbose -Verbose "SSH Runspace Open remoting connect failed." + + if ($count -eq 1) + { + # Try restarting sshd service + RestartSSHDService + } } } - } - if (($null -eq $rs) -or !($rs -is [runspace])) - { - $message = "Runspace open unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Message)" - throw [System.Management.Automation.PSInvalidOperationException]::new($message) - } - - Write-Verbose -Verbose "SSH Runspace Open remoting connect succeeded." - Write-Output $rs - } - - function VerifyRunspace { - param ( - [runspace] $rs - ) + if (($null -eq $rs) -or !($rs -is [runspace])) + { + $message = "Runspace open unable to connect to SSH remoting endpoint after two attempts. Error: $($connectionError.Message)" + throw [System.Management.Automation.PSInvalidOperationException]::new($message) + } - if ($null -eq $rs) - { - return + Write-Verbose -Verbose "SSH Runspace Open remoting connect succeeded." + Write-Output $rs } - Write-Verbose -Verbose "VerifyRunspace called for runspace: $($rs.Id)" + function VerifyRunspace { + param ( + [runspace] $rs + ) - $rs.RunspaceStateInfo.State | Should -BeExactly 'Opened' - $rs.RunspaceAvailability | Should -BeExactly 'Available' - $rs.RunspaceIsRemote | Should -BeTrue - $ps = [powershell]::Create() - try - { - Write-Verbose -Verbose "VerifyRunspace: Invoking PSSenderInfo" - $ps.Runspace = $rs - $psRemoteVersion = $ps.AddScript('$PSSenderInfo.ApplicationArguments.PSVersionTable.PSVersion').Invoke() - $psRemoteVersion.Major | Should -BeExactly $PSVersionTable.PSVersion.Major - $psRemoteVersion.Minor | Should -BeExactly $PSVersionTable.PSVersion.Minor + if ($null -eq $rs) + { + return + } - $ps.Commands.Clear() - Write-Verbose -Verbose "VerifyRunspace: Invoking whoami" - $ps.AddScript('whoami').Invoke() | Should -BeExactly $(whoami) - Write-Verbose -Verbose "VerifyRunspace complete" - } - finally - { - $ps.Dispose() + Write-Verbose -Verbose "VerifyRunspace called for runspace: $($rs.Id)" + + $rs.RunspaceStateInfo.State | Should -BeExactly 'Opened' + $rs.RunspaceAvailability | Should -BeExactly 'Available' + $rs.RunspaceIsRemote | Should -BeTrue + $ps = [powershell]::Create() + try + { + Write-Verbose -Verbose "VerifyRunspace: Invoking PSSenderInfo" + $ps.Runspace = $rs + $psRemoteVersion = $ps.AddScript('$PSSenderInfo.ApplicationArguments.PSVersionTable.PSVersion').Invoke() + $psRemoteVersion.Major | Should -BeExactly $PSVersionTable.PSVersion.Major + $psRemoteVersion.Minor | Should -BeExactly $PSVersionTable.PSVersion.Minor + + $ps.Commands.Clear() + Write-Verbose -Verbose "VerifyRunspace: Invoking whoami" + $ps.AddScript('whoami').Invoke() | Should -BeExactly $(whoami) + Write-Verbose -Verbose "VerifyRunspace complete" + } + finally + { + $ps.Dispose() + } } } @@ -342,48 +346,50 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "AfterEach complete" } - $testCases = @( - @{ - testName = 'Verifies connection with implicit user' - UserName = $null - ComputerName = 'localhost' - KeyFilePath = $null - Port = 0 - Subsystem = $null - }, - @{ - testName = 'Verifies connection with UserName' - UserName = whoami - ComputerName = 'localhost' - KeyFilePath = $null - Port = 0 - Subsystem = $null - }, - @{ - testName = 'Verifies connection with KeyFilePath' - UserName = whoami - ComputerName = 'localhost' - KeyFilePath = "$HOME/.ssh/id_rsa" - Port = 0 - Subsystem = $null - }, - @{ - testName = 'Verifies connection with Port specified' - UserName = whoami - ComputerName = 'localhost' - KeyFilePath = "$HOME/.ssh/id_rsa" - Port = 22 - Subsystem = $null - }, - @{ - testName = 'Verifies connection with Subsystem specified' - UserName = whoami - ComputerName = 'localhost' - KeyFilePath = "$HOME/.ssh/id_rsa" - Port = 22 - Subsystem = 'powershell' - } - ) + BeforeDiscovery { + $testCases = @( + @{ + testName = 'Verifies connection with implicit user' + UserName = $null + ComputerName = 'localhost' + KeyFilePath = $null + Port = 0 + Subsystem = $null + }, + @{ + testName = 'Verifies connection with UserName' + UserName = whoami + ComputerName = 'localhost' + KeyFilePath = $null + Port = 0 + Subsystem = $null + }, + @{ + testName = 'Verifies connection with KeyFilePath' + UserName = whoami + ComputerName = 'localhost' + KeyFilePath = "$HOME/.ssh/id_rsa" + Port = 0 + Subsystem = $null + }, + @{ + testName = 'Verifies connection with Port specified' + UserName = whoami + ComputerName = 'localhost' + KeyFilePath = "$HOME/.ssh/id_rsa" + Port = 22 + Subsystem = $null + }, + @{ + testName = 'Verifies connection with Subsystem specified' + UserName = whoami + ComputerName = 'localhost' + KeyFilePath = "$HOME/.ssh/id_rsa" + Port = 22 + Subsystem = 'powershell' + } + ) + } It "" -TestCases $testCases { param ( diff --git a/test/powershell/Host/Base-Directory.Tests.ps1 b/test/powershell/Host/Base-Directory.Tests.ps1 index 203d214e937..1eeb1d090ae 100644 --- a/test/powershell/Host/Base-Directory.Tests.ps1 +++ b/test/powershell/Host/Base-Directory.Tests.ps1 @@ -9,6 +9,10 @@ Describe "Configuration file locations" -tags "CI","Slow" { Context "Default configuration file locations" { + BeforeDiscovery { + $ItArgs = @{} + } + BeforeAll { if ($IsWindows) { @@ -27,8 +31,6 @@ Describe "Configuration file locations" -tags "CI","Slow" { $expectedProfile = [io.path]::Combine($env:HOME,".config","powershell",$profileName) $expectedReadline = [IO.Path]::Combine($env:HOME, ".local", "share", "powershell", "PSReadLine", "ConsoleHost_history.txt") } - - $ItArgs = @{} } BeforeEach { @@ -64,7 +66,7 @@ Describe "Configuration file locations" -tags "CI","Slow" { } Context "XDG Base Directory Specification is supported on Linux" { - BeforeAll { + BeforeDiscovery { # Using It @ItArgs, we automatically skip on Windows for all these tests if ($IsWindows) { $ItArgs = @{ skip = $true } diff --git a/test/powershell/Host/ConsoleHost.Tests.ps1 b/test/powershell/Host/ConsoleHost.Tests.ps1 index 735c0a39682..a89e244cc14 100644 --- a/test/powershell/Host/ConsoleHost.Tests.ps1 +++ b/test/powershell/Host/ConsoleHost.Tests.ps1 @@ -125,10 +125,18 @@ Describe "ConsoleHost unit tests" -tags "Feature" { & $powershell -noprofile { $args[0] } -args 1,(2,3) | Should -Be 1 (& $powershell -noprofile { $args[1] } -args 1,(2,3))[1] | Should -Be 3 } - foreach ($x in "--help", "-help", "-h", "-?", "--he", "-hel", "--HELP", "-hEl") { - It "Accepts '$x' as a parameter for help" { - & $powershell -noprofile $x | Where-Object { $_ -match "pwsh[.exe] -Help | -? | /?" } | Should -Not -BeNullOrEmpty - } + It "Accepts '' as a parameter for help" -TestCases @( + @{ x = "--help" }, + @{ x = "-help" }, + @{ x = "-h" }, + @{ x = "-?" }, + @{ x = "--he" }, + @{ x = "-hel" }, + @{ x = "--HELP" }, + @{ x = "-hEl" } + ) { + param($x) + & $powershell -noprofile $x | Where-Object { $_ -match "pwsh[.exe] -Help | -? | /?" } | Should -Not -BeNullOrEmpty } It "Should accept a Base64 encoded command" { @@ -1097,7 +1105,8 @@ Describe "Console host api tests" -Tag CI { @{InputObject = "${esc}abc"; Length = 4; Name = "Malformed escape - no csi"}, @{InputObject = "[31mabc"; Length = 7; Name = "Malformed escape - no escape"} - $testCases += if ($Host.UI.SupportsVirtualTerminal) + $supportsVT = try { $Host.UI.SupportsVirtualTerminal } catch { $false } + $testCases += if ($supportsVT) { @{InputObject = "$esc[31mabc"; Length = 3; Name = "Escape at start"} @{InputObject = "$esc[31mabc$esc[0m"; Length = 3; Name = "Escape at start and end"} diff --git a/test/powershell/Host/Logging.Tests.ps1 b/test/powershell/Host/Logging.Tests.ps1 index 53798dc1c3c..d2614d36836 100644 --- a/test/powershell/Host/Logging.Tests.ps1 +++ b/test/powershell/Host/Logging.Tests.ps1 @@ -2,46 +2,47 @@ # Licensed under the MIT License. using namespace System.Text -Set-StrictMode -Version 3.0 -$ErrorActionPreference = 'Stop' - -Import-Module HelpersCommon -Import-Module PSSysLog - -<# - Define enums that mirror the internal enums used - in product code. These are used to configure - syslog logging. -#> -enum LogLevel -{ - LogAlways = 0x0 - Critical = 0x1 - Error = 0x2 - Warning = 0x3 - Informational = 0x4 - Verbose = 0x5 - Debug = 0x14 -} +BeforeAll { + Set-StrictMode -Version 3.0 + $ErrorActionPreference = 'Stop' + + Import-Module HelpersCommon + Import-Module PSSysLog + + <# + Define enums that mirror the internal enums used + in product code. These are used to configure + syslog logging. + #> + enum LogLevel + { + LogAlways = 0x0 + Critical = 0x1 + Error = 0x2 + Warning = 0x3 + Informational = 0x4 + Verbose = 0x5 + Debug = 0x14 + } -enum LogChannel -{ - Operational = 0x10 - Analytic = 0x11 -} + enum LogChannel + { + Operational = 0x10 + Analytic = 0x11 + } -enum LogKeyword -{ - Runspace = 0x1 - Pipeline = 0x2 - Protocol = 0x4 - Transport = 0x8 - Host = 0x10 - Cmdlets = 0x20 - Serializer = 0x40 - Session = 0x80 - ManagedPlugin = 0x100 -} + enum LogKeyword + { + Runspace = 0x1 + Pipeline = 0x2 + Protocol = 0x4 + Transport = 0x8 + Host = 0x10 + Cmdlets = 0x20 + Serializer = 0x40 + Session = 0x80 + ManagedPlugin = 0x100 + } # mac log command can emit json, so just use that # we need to deconstruct the eventmessage to get the event id @@ -155,6 +156,7 @@ function Get-RegEx $regex = $regex -replace '\^', '\^' return $regex } +} Describe 'Basic SysLog tests on Linux' -Tag @('CI','RequireSudoOnUnix') { BeforeAll { @@ -441,13 +443,7 @@ $PID } Describe 'Basic EventLog tests on Windows' -Tag @('CI','RequireAdminOnWindows') { - BeforeAll { - [bool] $IsSupportedEnvironment = $IsWindows - [string] $powershell = Join-Path -Path $PSHOME -ChildPath 'pwsh' - - $currentWarningPreference = $WarningPreference - $WarningPreference = "SilentlyContinue" - + BeforeDiscovery { $scriptBlockLoggingCases = @( @{ name = 'normal script block' @@ -460,6 +456,14 @@ Describe 'Basic EventLog tests on Windows' -Tag @('CI','RequireAdminOnWindows') expectedText="Write-Verbose 'testheader123␀' ;Write-verbose 'after'`r`n" } ) + } + + BeforeAll { + [bool] $IsSupportedEnvironment = $IsWindows + [string] $powershell = Join-Path -Path $PSHOME -ChildPath 'pwsh' + + $currentWarningPreference = $WarningPreference + $WarningPreference = "SilentlyContinue" if ($IsSupportedEnvironment) { diff --git a/test/powershell/Host/Read-Host.Tests.ps1 b/test/powershell/Host/Read-Host.Tests.ps1 index dc225362ed0..24f8f965447 100644 --- a/test/powershell/Host/Read-Host.Tests.ps1 +++ b/test/powershell/Host/Read-Host.Tests.ps1 @@ -2,16 +2,13 @@ # Licensed under the MIT License. Describe "Read-Host" -Tags "Slow","Feature" { Context "[Console]::ReadKey() implementation on non-Windows" { + BeforeDiscovery { + $skip = $IsWindows -or -not (Get-Command expect -ErrorAction Ignore) + } + BeforeAll { $powershell = Join-Path -Path $PSHOME -ChildPath "pwsh" $assetsDir = Join-Path -Path $PSScriptRoot -ChildPath assets - if ($IsWindows) { - $ItArgs = @{ skip = $true } - } elseif (-not (Get-Command expect -ErrorAction Ignore)) { - $ItArgs = @{ pending = $true } - } else { - $ItArgs = @{ } - } $expectFile = Join-Path $assetsDir "Read-Host.Output.expect" @@ -20,7 +17,7 @@ Describe "Read-Host" -Tags "Slow","Feature" { } } - It @ItArgs "Should output correctly" { + It "Should output correctly" -Skip:$skip { & $expectFile $powershell | Out-Null $LASTEXITCODE | Should -Be 0 } diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index f8762a63929..ce427b9f31b 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -1384,7 +1384,7 @@ param([ValidatePattern( } Context 'Get-Verb & Get-Command -Verb parameter completion' { - BeforeAll { + BeforeDiscovery { $allVerbs = 'Add Approve Assert Backup Block Build Checkpoint Clear Close Compare Complete Compress Confirm Connect Convert ConvertFrom ConvertTo Copy Debug Deny Deploy Disable Disconnect Dismount Edit Enable Enter Exit Expand Export Find Format Get Grant Group Hide Import Initialize Install Invoke Join Limit Lock Measure Merge Mount Move New Open Optimize Out Ping Pop Protect Publish Push Read Receive Redo Register Remove Rename Repair Request Reset Resize Resolve Restart Restore Resume Revoke Save Search Select Send Set Show Skip Split Start Step Stop Submit Suspend Switch Sync Test Trace Unblock Undo Uninstall Unlock Unprotect Unpublish Unregister Update Use Wait Watch Write' $allVerbsSingleQuote = "'Add' 'Approve' 'Assert' 'Backup' 'Block' 'Build' 'Checkpoint' 'Clear' 'Close' 'Compare' 'Complete' 'Compress' 'Confirm' 'Connect' 'Convert' 'ConvertFrom' 'ConvertTo' 'Copy' 'Debug' 'Deny' 'Deploy' 'Disable' 'Disconnect' 'Dismount' 'Edit' 'Enable' 'Enter' 'Exit' 'Expand' 'Export' 'Find' 'Format' 'Get' 'Grant' 'Group' 'Hide' 'Import' 'Initialize' 'Install' 'Invoke' 'Join' 'Limit' 'Lock' 'Measure' 'Merge' 'Mount' 'Move' 'New' 'Open' 'Optimize' 'Out' 'Ping' 'Pop' 'Protect' 'Publish' 'Push' 'Read' 'Receive' 'Redo' 'Register' 'Remove' 'Rename' 'Repair' 'Request' 'Reset' 'Resize' 'Resolve' 'Restart' 'Restore' 'Resume' 'Revoke' 'Save' 'Search' 'Select' 'Send' 'Set' 'Show' 'Skip' 'Split' 'Start' 'Step' 'Stop' 'Submit' 'Suspend' 'Switch' 'Sync' 'Test' 'Trace' 'Unblock' 'Undo' 'Uninstall' 'Unlock' 'Unprotect' 'Unpublish' 'Unregister' 'Update' 'Use' 'Wait' 'Watch' 'Write'" $allVerbsDoubleQuote = """Add"" ""Approve"" ""Assert"" ""Backup"" ""Block"" ""Build"" ""Checkpoint"" ""Clear"" ""Close"" ""Compare"" ""Complete"" ""Compress"" ""Confirm"" ""Connect"" ""Convert"" ""ConvertFrom"" ""ConvertTo"" ""Copy"" ""Debug"" ""Deny"" ""Deploy"" ""Disable"" ""Disconnect"" ""Dismount"" ""Edit"" ""Enable"" ""Enter"" ""Exit"" ""Expand"" ""Export"" ""Find"" ""Format"" ""Get"" ""Grant"" ""Group"" ""Hide"" ""Import"" ""Initialize"" ""Install"" ""Invoke"" ""Join"" ""Limit"" ""Lock"" ""Measure"" ""Merge"" ""Mount"" ""Move"" ""New"" ""Open"" ""Optimize"" ""Out"" ""Ping"" ""Pop"" ""Protect"" ""Publish"" ""Push"" ""Read"" ""Receive"" ""Redo"" ""Register"" ""Remove"" ""Rename"" ""Repair"" ""Request"" ""Reset"" ""Resize"" ""Resolve"" ""Restart"" ""Restore"" ""Resume"" ""Revoke"" ""Save"" ""Search"" ""Select"" ""Send"" ""Set"" ""Show"" ""Skip"" ""Split"" ""Start"" ""Step"" ""Stop"" ""Submit"" ""Suspend"" ""Switch"" ""Sync"" ""Test"" ""Trace"" ""Unblock"" ""Undo"" ""Uninstall"" ""Unlock"" ""Unprotect"" ""Unpublish"" ""Unregister"" ""Update"" ""Use"" ""Wait"" ""Watch"" ""Write""" @@ -1408,7 +1408,6 @@ param([ValidatePattern( $utilityModuleObjectVerbsStartingWithS = 'Select Sort' $utilityModuleObjectVerbsStartingWithSSingleQuote = "'Select' 'Sort'" $utilityModuleObjectVerbsStartingWithSDoubleQuote = """Select"" ""Sort""" - $utilityModuleObjectVerbsStartingWithS $coreModuleObjectVerbs = 'ForEach Where' } @@ -1453,7 +1452,7 @@ param([ValidatePattern( } Context 'StrictMode Version parameter completion' { - BeforeAll { + BeforeDiscovery { $allStrictModeVersions = '1.0 2.0 3.0 Latest' $allStrictModeVersionsSingleQuote = "'1.0' '2.0' '3.0' 'Latest'" $allStrictModeVersionsDoubleQuote = """1.0"" ""2.0"" ""3.0"" ""Latest""" @@ -1485,14 +1484,17 @@ param([ValidatePattern( } Context 'Help Module parameter completion' { - BeforeAll { + BeforeDiscovery { $utilityModule = 'Microsoft.PowerShell.Utility' $managementModule = 'Microsoft.PowerShell.Management' $allMicrosoftPowerShellModules = (Get-Module -Name Microsoft.PowerShell* -ListAvailable).Name - Import-Module -Name $allMicrosoftPowerShellModules -ErrorAction SilentlyContinue $allMicrosoftPowerShellModules = ($allMicrosoftPowerShellModules | Sort-Object -Unique) -join ' ' } + BeforeAll { + Import-Module -Name (Get-Module -Name Microsoft.PowerShell* -ListAvailable).Name -ErrorAction SilentlyContinue + } + It "Should complete Module for ''" -TestCases @( @{ TextInput = "Save-Help -Module Microsoft.PowerShell.U"; ExpectedModules = $utilityModule } @{ TextInput = "Update-Help -Module Microsoft.PowerShell.U"; ExpectedModules = $utilityModule } @@ -1511,6 +1513,22 @@ param([ValidatePattern( } Context 'New-ItemProperty -PropertyType parameter completion' { + BeforeDiscovery { + if ($IsWindows) { + $allRegistryValueKinds = 'String ExpandString Binary DWord MultiString QWord Unknown' + $allRegistryValueKindsWithQuotes = "'String' 'ExpandString' 'Binary' 'DWord' 'MultiString' 'QWord' 'Unknown'" + $dwordValueKind = 'DWord' + $qwordValueKind = 'QWord' + $binaryValueKind = 'Binary' + $multiStringValueKind = 'MultiString' + $registryPath = "HKCU:\test1\sub" + $registryLiteralPath = "HKCU:\test2\*\sub" + $fileSystemPath = "TestDrive:\test1.txt" + $fileSystemLiteralPathDir = "TestDrive:\[]" + $fileSystemLiteralPath = "$fileSystemLiteralPathDir\test2.txt" + } + } + BeforeAll { if ($IsWindows) { $allRegistryValueKinds = 'String ExpandString Binary DWord MultiString QWord Unknown' @@ -1599,45 +1617,29 @@ param([ValidatePattern( } Context 'Get-Command -Noun parameter completion' { - BeforeAll { + BeforeDiscovery { function GetModuleCommandNouns( [string]$Module, [string]$Verb, [switch]$SingleQuote, [switch]$DoubleQuote) { - $commandParams = @{} - - if ($PSBoundParameters.ContainsKey('Module')) { - $commandParams['Module'] = $Module - } - - if ($PSBoundParameters.ContainsKey('Verb')) { - $commandParams['Verb'] = $Verb - } - + if ($PSBoundParameters.ContainsKey('Module')) { $commandParams['Module'] = $Module } + if ($PSBoundParameters.ContainsKey('Verb')) { $commandParams['Verb'] = $Verb } $nouns = (Get-Command @commandParams).Noun - - if ($SingleQuote) { - return ($nouns | ForEach-Object { "'$_'" }) - } - elseif ($DoubleQuote) { - return ($nouns | ForEach-Object { """$_""" }) - } - + if ($SingleQuote) { return ($nouns | ForEach-Object { "'$_'" }) } + elseif ($DoubleQuote) { return ($nouns | ForEach-Object { """$_""" }) } return $nouns } $utilityModuleName = 'Microsoft.PowerShell.Utility' - $allUtilityCommandNouns = GetModuleCommandNouns -Module $utilityModuleName $allUtilityCommandNounsSingleQuote = GetModuleCommandNouns -Module $utilityModuleName -SingleQuote $allUtilityCommandNounsDoubleQuote = GetModuleCommandNouns -Module $utilityModuleName -DoubleQuote $utilityCommandNounsStartingWithF = $allUtilityCommandNouns | Where-Object { $_ -like 'F*'} $utilityCommandNounsStartingWithFSingleQuote = $allUtilityCommandNounsSingleQuote | Where-Object { $_ -like "'F*"} $utilityCommandNounsStartingWithFDoubleQuote = $allUtilityCommandNounsDoubleQuote | Where-Object { $_ -like """F*"} - $allUtilityCommandNounsWithConvertToVerb = GetModuleCommandNouns -Module $utilityModuleName -Verb 'ConvertTo' $allUtilityCommandNounsWithConvertToVerbSingleQuote = GetModuleCommandNouns -Module $utilityModuleName -SingleQuote -Verb 'ConvertTo' $allUtilityCommandNounsWithConvertToVerbDoubleQuote = GetModuleCommandNouns -Module $utilityModuleName -DoubleQuote -Verb 'ConvertTo' @@ -1679,17 +1681,11 @@ param([ValidatePattern( } Context "Get-ExperimentalFeature -Name parameter completion" { - BeforeAll { + BeforeDiscovery { function GetExperimentalFeatureNames([switch]$SingleQuote, [switch]$DoubleQuote) { $features = (Get-ExperimentalFeature).Name - - if ($SingleQuote) { - return ($features | ForEach-Object { "'$_'" }) - } - elseif ($DoubleQuote) { - return ($features | ForEach-Object { """$_""" }) - } - + if ($SingleQuote) { return ($features | ForEach-Object { "'$_'" }) } + elseif ($DoubleQuote) { return ($features | ForEach-Object { """$_""" }) } return $features } @@ -1717,7 +1713,7 @@ param([ValidatePattern( } Context "Join-String -Separator & -FormatString parameter completion" { - BeforeAll { + BeforeDiscovery { if ($IsWindows) { $allSeparators = "',' ', ' ';' '; ' ""``r``n"" '-' ' '" $allFormatStrings = "'[{0}]' '{0:N2}' ""``r``n ```${0}"" ""``r``n [string] ```${0}""" @@ -1730,10 +1726,8 @@ param([ValidatePattern( $newlineSeparator = """``n""" $newlineFormatStrings = """``n ```${0}"" ""``n [string] ```${0}""" } - $commaSeparators = "',' ', '" $semiColonSeparators = "';' '; '" - $squareBracketFormatString = "'[{0}]'" $curlyBraceFormatString = "'{0:N2}'" } @@ -2140,34 +2134,37 @@ class InheritedClassTest : System.Attribute } Context "Script name completion" { + BeforeDiscovery { + $scriptWithWildcardCases = @( + @{ name = 'relative wildcard' } + @{ name = 'fully qualified wildcard' } + @{ name = 'question mark wildcard' } + @{ name = 'bracket range wildcard' } + ) + } + BeforeAll { - Setup -f 'install-powershell.ps1' -Content "" - Setup -f 'remove-powershell.ps1' -Content "" + Set-Content -Path (Join-Path $TestDrive 'install-powershell.ps1') -Value "" + Set-Content -Path (Join-Path $TestDrive 'remove-powershell.ps1') -Value "" - $scriptWithWildcardCases = @( - @{ + $scriptCaseMap = @{ + 'relative wildcard' = @{ command = '.\install-*.ps1' expectedCommand = Join-Path -Path '.' -ChildPath 'install-powershell.ps1' - name = "'$(Join-Path -Path '.' -ChildPath 'install-powershell.ps1')'" } - @{ - command = (Join-Path ${TestDrive} -ChildPath 'install-*.ps1') - expectedCommand = (Join-Path ${TestDrive} -ChildPath 'install-powershell.ps1') - name = "'$(Join-Path -Path '.' -ChildPath 'install-powershell.ps1')' by fully qualified path" + 'fully qualified wildcard' = @{ + command = (Join-Path ${TestDrive} -ChildPath 'install-*.ps1') + expectedCommand = (Join-Path ${TestDrive} -ChildPath 'install-powershell.ps1') } - @{ + 'question mark wildcard' = @{ command = '.\?emove-powershell.ps1' expectedCommand = Join-Path -Path '.' -ChildPath 'remove-powershell.ps1' - name = "'$(Join-Path -Path '.' -ChildPath '?emove-powershell.ps1')'" } - @{ - # [] cause the parser to create a new token. - # So, the command must be quoted to tab complete. + 'bracket range wildcard' = @{ command = "'.\[ra]emove-powershell.ps1'" expectedCommand = "'$(Join-Path -Path '.' -ChildPath 'remove-powershell.ps1')'" - name = "'$(Join-Path -Path '.' -ChildPath '[ra]emove-powershell.ps1')'" } - ) + } Push-Location ${TestDrive}\ } @@ -2177,20 +2174,21 @@ class InheritedClassTest : System.Attribute } It "Input should successfully complete" -TestCases $scriptWithWildcardCases { - param($command, $expectedCommand) - $res = TabExpansion2 -inputScript $command -cursorColumn $command.Length + param($name) + $case = $scriptCaseMap[$name] + $res = TabExpansion2 -inputScript $case.command -cursorColumn $case.command.Length $res.CompletionMatches.Count | Should -BeGreaterThan 0 - $res.CompletionMatches[0].CompletionText | Should -BeExactly $expectedCommand + $res.CompletionMatches[0].CompletionText | Should -BeExactly $case.expectedCommand } } Context "Script parameter completion" { BeforeAll { - Setup -File -Path 'ModuleReqTest.ps1' -Content @' + Set-Content -Path (Join-Path $TestDrive 'ModuleReqTest.ps1') -Value @' #requires -Modules ThisModuleDoesNotExist param ($Param1) '@ - Setup -File -Path 'AdminReqTest.ps1' -Content @' + Set-Content -Path (Join-Path $TestDrive 'AdminReqTest.ps1') -Value @' #requires -RunAsAdministrator param ($Param1) '@ @@ -2215,18 +2213,8 @@ param ($Param1) } Context "File name completion" { - BeforeAll { - $tempDir = Join-Path -Path $TestDrive -ChildPath "baseDir" - $oneSubDir = Join-Path -Path $tempDir -ChildPath "oneSubDir" - $oneSubDirPrime = Join-Path -Path $tempDir -ChildPath "prime" - $twoSubDir = Join-Path -Path $oneSubDir -ChildPath "twoSubDir" - $caseTestPath = Join-Path $testdrive "CaseTest" - - New-Item -Path $tempDir -ItemType Directory -Force > $null - New-Item -Path $oneSubDir -ItemType Directory -Force > $null - New-Item -Path $oneSubDirPrime -ItemType Directory -Force > $null - New-Item -Path $twoSubDir -ItemType Directory -Force > $null - + BeforeDiscovery { + $separator = [System.IO.Path]::DirectorySeparatorChar $testCases = @( @{ inputStr = "ab"; name = "abc"; localExpected = ".${separator}abc"; oneSubExpected = "..${separator}abc"; twoSubExpected = "..${separator}..${separator}abc" } @{ inputStr = "asaasas"; name = "asaasas!popee"; localExpected = ".${separator}asaasas!popee"; oneSubExpected = "..${separator}asaasas!popee"; twoSubExpected = "..${separator}..${separator}asaasas!popee" } @@ -2242,11 +2230,26 @@ param ($Param1) @{ inputStr = "bb"; name = "bb,"; localExpected = "& '.${separator}bb,'"; oneSubExpected = "& '..${separator}bb,'"; twoSubExpected = "& '..${separator}..${separator}bb,'" } @{ inputStr = "b"; name = "b;"; localExpected = "& '.${separator}b;'"; oneSubExpected = "& '..${separator}b;'"; twoSubExpected = "& '..${separator}..${separator}b;'" } ) + } + + BeforeAll { + $tempDir = Join-Path -Path $TestDrive -ChildPath "baseDir" + $oneSubDir = Join-Path -Path $tempDir -ChildPath "oneSubDir" + $oneSubDirPrime = Join-Path -Path $tempDir -ChildPath "prime" + $twoSubDir = Join-Path -Path $oneSubDir -ChildPath "twoSubDir" + $caseTestPath = Join-Path $testdrive "CaseTest" + + New-Item -Path $tempDir -ItemType Directory -Force > $null + New-Item -Path $oneSubDir -ItemType Directory -Force > $null + New-Item -Path $oneSubDirPrime -ItemType Directory -Force > $null + New-Item -Path $twoSubDir -ItemType Directory -Force > $null + $caseTestPath = Join-Path $testdrive "CaseTest" + $fileNames = @("abc", "asaasas!popee", 'bbbbbbbbbb`', "bbbbbbbbb#", "bbbbbbbb{", "bbbbbbb}", "bbbbbb(", "bbbbb)", 'bbbb$', "bbb'", "bb,", "b;") try { Push-Location -Path $tempDir - foreach ($entry in $testCases) { - New-Item -Path $tempDir -Name $entry.name -ItemType File -ErrorAction SilentlyContinue > $null + foreach ($name in $fileNames) { + New-Item -Path $tempDir -Name $name -ItemType File -ErrorAction SilentlyContinue > $null } } finally { Pop-Location @@ -2521,7 +2524,7 @@ param ($Param1) } Context "Cmdlet name completion" { - BeforeAll { + BeforeDiscovery { $testCases = @( @{ inputStr = "get-ch*item"; expected = "Get-ChildItem" } @{ inputStr = "set-alia?"; expected = "Set-Alias" } @@ -2551,7 +2554,7 @@ param ($Param1) } Context "Miscellaneous completion tests" { - BeforeAll { + BeforeDiscovery { $testCases = @( @{ inputStr = "get-childitem -"; expected = "-Path"; setup = $null } @{ inputStr = "get-childitem -Fil"; expected = "-Filter"; setup = $null } @@ -3246,11 +3249,8 @@ dir -Recurse ` } Context "Completion on 'comma', 'redirection' and 'minus' tokens" { - BeforeAll { - $tempDir = Join-Path -Path $TestDrive -ChildPath "CommaTest" - New-Item -Path $tempDir -ItemType Directory -Force > $null - New-Item -Path "$tempDir\commaA.txt" -ItemType File -Force > $null - + BeforeDiscovery { + $separator = [System.IO.Path]::DirectorySeparatorChar $redirectionTestCases = @( @{ inputStr = "gps >"; expected = ".${separator}commaA.txt" } @{ inputStr = "gps >>"; expected = ".${separator}commaA.txt" } @@ -3259,6 +3259,12 @@ dir -Recurse ` @{ inputStr = "gps 2>&1>"; expected = ".${separator}commaA.txt" } @{ inputStr = "gps 2>&1>>"; expected = ".${separator}commaA.txt" } ) + } + + BeforeAll { + $tempDir = Join-Path -Path $TestDrive -ChildPath "CommaTest" + New-Item -Path $tempDir -ItemType Directory -Force > $null + New-Item -Path "$tempDir\commaA.txt" -ItemType File -Force > $null Push-Location -Path $tempDir } @@ -3315,6 +3321,17 @@ dir -Recurse ` } Context "Folder/File path tab completion with special characters" { + BeforeDiscovery { + $testCases = @( + @{ name = 'cd My' } + @{ name = 'Get-Help relative path' } + @{ name = 'redirect My' } + @{ name = 'redirect relative path' } + @{ name = 'redirect tempDir My' } + @{ name = 'redirect tempDir path' } + ) + } + BeforeAll { $tempDir = Join-Path -Path $TestDrive -ChildPath "SpecialChar" New-Item -Path $tempDir -ItemType Directory -Force > $null @@ -3323,14 +3340,14 @@ dir -Recurse ` New-Item -Path "$tempDir\My [Path]\test.ps1" -ItemType File -Force > $null New-Item -Path "$tempDir\)file.txt" -ItemType File -Force > $null - $testCases = @( - @{ inputStr = "cd My"; expected = "'.${separator}My ``[Path``]'" } - @{ inputStr = "Get-Help '.\My ``[Path``]'\"; expected = "'.${separator}My ``[Path``]${separator}test.ps1'" } - @{ inputStr = "Get-Process >My"; expected = "'.${separator}My ``[Path``]'" } - @{ inputStr = "Get-Process >'.\My ``[Path``]\'"; expected = "'.${separator}My ``[Path``]${separator}test.ps1'" } - @{ inputStr = "Get-Process >${tempDir}\My"; expected = "'${tempDir}${separator}My ``[Path``]'" } - @{ inputStr = "Get-Process > '${tempDir}\My ``[Path``]\'"; expected = "'${tempDir}${separator}My ``[Path``]${separator}test.ps1'" } - ) + $caseMap = @{ + 'cd My' = @{ inputStr = "cd My"; expected = "'.${separator}My ``[Path``]'" } + 'Get-Help relative path' = @{ inputStr = "Get-Help '.\My ``[Path``]'\"; expected = "'.${separator}My ``[Path``]${separator}test.ps1'" } + 'redirect My' = @{ inputStr = "Get-Process >My"; expected = "'.${separator}My ``[Path``]'" } + 'redirect relative path' = @{ inputStr = "Get-Process >'.\My ``[Path``]\'"; expected = "'.${separator}My ``[Path``]${separator}test.ps1'" } + 'redirect tempDir My' = @{ inputStr = "Get-Process >${tempDir}\My"; expected = "'${tempDir}${separator}My ``[Path``]'" } + 'redirect tempDir path' = @{ inputStr = "Get-Process > '${tempDir}\My ``[Path``]\'"; expected = "'${tempDir}${separator}My ``[Path``]${separator}test.ps1'" } + } Push-Location -Path $tempDir } @@ -3340,12 +3357,12 @@ dir -Recurse ` Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue } - It "Complete special relative path ''" -TestCases $testCases { - param($inputStr, $expected) - - $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + It "Complete special relative path ''" -TestCases $testCases { + param($name) + $case = $caseMap[$name] + $res = TabExpansion2 -inputScript $case.inputStr -cursorColumn $case.inputStr.Length $res.CompletionMatches.Count | Should -BeGreaterThan 0 - $res.CompletionMatches[0].CompletionText | Should -BeExactly $expected + $res.CompletionMatches[0].CompletionText | Should -BeExactly $case.expected } It "Complete file name starting with special char" { @@ -3357,7 +3374,7 @@ dir -Recurse ` } Context "Local tab completion with AST" { - BeforeAll { + BeforeDiscovery { $testCases = @( @{ inputStr = '$p = Get-Process; $p | % ProcessN '; bareWord = 'ProcessN'; expected = 'ProcessName' } @{ inputStr = 'function bar { Get-Ali* }'; bareWord = 'Get-Ali*'; expected = 'Get-Alias' } @@ -3382,7 +3399,7 @@ dir -Recurse ` } Context "No tab completion tests" { - BeforeAll { + BeforeDiscovery { $testCases = @( @{ inputStr = 'function new-' } @{ inputStr = 'filter new-' } @@ -3406,9 +3423,7 @@ dir -Recurse ` } Context "Tab completion error tests" { - BeforeAll { - $ast = {}.Ast; - $tokens = [System.Management.Automation.Language.Token[]]@() + BeforeDiscovery { $testCases = @( @{ inputStr = {[System.Management.Automation.CommandCompletion]::MapStringInputToParsedInput('$PID.', 7)}; expected = "PSArgumentException" } @{ inputStr = {[System.Management.Automation.CommandCompletion]::CompleteInput($null, $null, $null, $null)}; expected = "PSArgumentNullException" } @@ -3423,6 +3438,11 @@ dir -Recurse ` ) } + BeforeAll { + $ast = {}.Ast; + $tokens = [System.Management.Automation.Language.Token[]]@() + } + It "Input '' should throw in tab completion" -TestCases $testCases { param($inputStr, $expected) $inputStr | Should -Throw -ErrorId $expected @@ -3438,7 +3458,7 @@ dir -Recurse ` } Context "DSC tab completion tests" { - BeforeAll { + BeforeDiscovery { $testCases = @( @{ inputStr = 'Configura'; expected = 'Configuration' } @{ inputStr = '$extension = New-Object [System.Collections.Generic.List[string]]; $extension.wh'; expected = "Where(" } @@ -3484,7 +3504,7 @@ dir -Recurse ` } Context "CIM cmdlet completion tests" { - BeforeAll { + BeforeDiscovery { $testCases = @( @{ inputStr = "Invoke-CimMethod -ClassName Win32_Process -MethodName Crea"; expected = "Create" } @{ inputStr = "Get-CimInstance -ClassName Win32_Process | Invoke-CimMethod -MethodName AttachDeb"; expected = "AttachDebugger" } @@ -3733,15 +3753,6 @@ dir -Recurse ` .REMOTEHELPRUNSPACE PSEditi^ #> '@ - } - @{ - Intent = 'Complete help keyword EXTERNALHELP argument' - Expected = Join-Path $TESTDRIVE "pwsh.xml" - TestString = @" -<# -.EXTERNALHELP $TESTDRIVE\pwsh.^ -#> -"@ } @{ Intent = 'Complete help keyword PARAMETER argument for script' @@ -3864,6 +3875,18 @@ function MyFunction ($param1, $param2) $res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1) $res.CompletionMatches.CompletionText | Should -BeExactly $Expected } + + It 'Complete help keyword EXTERNALHELP argument' { + $Expected = Join-Path $TESTDRIVE "pwsh.xml" + $TestString = @" +<# +.EXTERNALHELP $TESTDRIVE\pwsh.^ +#> +"@ + $CursorIndex = $TestString.IndexOf('^') + $res = TabExpansion2 -cursorColumn $CursorIndex -inputScript $TestString.Remove($CursorIndex, 1) + $res.CompletionMatches.CompletionText | Should -BeExactly $Expected + } } It 'Should complete module specification keys in using module statement' { @@ -3892,6 +3915,20 @@ Describe "TabCompletion elevated tests" -Tags CI, RequireAdminOnWindows { } Describe "Tab completion tests with remote Runspace" -Tags Feature,RequireAdminOnWindows { + BeforeDiscovery { + $testCases = @( + @{ inputStr = 'Get-Proc'; expected = 'Get-Process' } + @{ inputStr = 'Get-Process | % ProcessN'; expected = 'ProcessName' } + @{ inputStr = 'Get-ChildItem alias: | % { $_.Defini'; expected = 'Definition' } + ) + + $testCasesWithAst = @( + @{ inputStr = '$p = Get-Process; $p | % ProcessN '; bareWord = 'ProcessN'; expected = 'ProcessName' } + @{ inputStr = 'function bar { Get-Ali* }'; bareWord = 'Get-Ali*'; expected = 'Get-Alias' } + @{ inputStr = 'function baz ([string]$version, [consolecolor]$name){} baz version bl'; bareWord = 'bl'; expected = 'Black' } + ) + } + BeforeAll { $skipTest = -not $IsWindows $pendingTest = $IsWindows -and (Test-IsWinWow64) @@ -3900,18 +3937,6 @@ Describe "Tab completion tests with remote Runspace" -Tags Feature,RequireAdminO $session = New-RemoteSession $powershell = [powershell]::Create() $powershell.Runspace = $session.Runspace - - $testCases = @( - @{ inputStr = 'Get-Proc'; expected = 'Get-Process' } - @{ inputStr = 'Get-Process | % ProcessN'; expected = 'ProcessName' } - @{ inputStr = 'Get-ChildItem alias: | % { $_.Defini'; expected = 'Definition' } - ) - - $testCasesWithAst = @( - @{ inputStr = '$p = Get-Process; $p | % ProcessN '; bareWord = 'ProcessN'; expected = 'ProcessName' } - @{ inputStr = 'function bar { Get-Ali* }'; bareWord = 'Get-Ali*'; expected = 'Get-Alias' } - @{ inputStr = 'function baz ([string]$version, [consolecolor]$name){} baz version bl'; bareWord = 'bl'; expected = 'Black' } - ) } else { $defaultParameterValues = $PSDefaultParameterValues.Clone() diff --git a/test/powershell/Language/Classes/ProtectedAccess.Tests.ps1 b/test/powershell/Language/Classes/ProtectedAccess.Tests.ps1 index d9dc808b0d7..7a898e8254d 100644 --- a/test/powershell/Language/Classes/ProtectedAccess.Tests.ps1 +++ b/test/powershell/Language/Classes/ProtectedAccess.Tests.ps1 @@ -1,7 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -Add-Type -WarningAction Ignore @' +BeforeAll { + Add-Type -WarningAction Ignore @' public class Base { private int data; @@ -52,7 +53,7 @@ public class Base } '@ -$derived1,$derived2,$derived3 = Invoke-Expression @' + $derived1,$derived2,$derived3 = Invoke-Expression @' class Derived : Base { Derived() : Base() {} @@ -139,6 +140,7 @@ class Derived2 : Base {} [Derived]::new(20) [Derived2]::new() '@ +} Describe "Protected Member Access - w/ default ctor" -Tags "CI" { It "Method Access" { $derived1.TestMethodAccess() | Should -Be 42 } @@ -178,7 +180,9 @@ Describe "Protected Member Access - w/ non-default ctor" -Tags "CI" { } Describe "Protected Member Access - members not visible outside class" -Tags "CI" { - Set-StrictMode -v 3 + BeforeAll { + Set-StrictMode -v 3 + } It "Invalid protected field Get Access" { { $derived1.Field } | Should -Throw -ErrorId "PropertyNotFoundStrict" } It "Invalid protected property Get Access" { { $derived1.Property } | Should -Throw -ErrorId "PropertyNotFoundStrict" } It "Invalid protected field Set Access" { { $derived1.Field = 1 } | Should -Throw -ErrorId "PropertyAssignmentException"} @@ -187,4 +191,3 @@ Describe "Protected Member Access - members not visible outside class" -Tags "CI It "Invalid protected constructor Access" { { [Base]::new() } | Should -Throw -ErrorId "MethodCountCouldNotFindBest" } It "Invalid protected method Access" { { $derived1.Method() } | Should -Throw -ErrorId "MethodNotFound" } } - diff --git a/test/powershell/Language/Classes/Scripting.Classes.Attributes.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.Attributes.Tests.ps1 index 3df42931855..a98fa546c2d 100644 --- a/test/powershell/Language/Classes/Scripting.Classes.Attributes.Tests.ps1 +++ b/test/powershell/Language/Classes/Scripting.Classes.Attributes.Tests.ps1 @@ -51,9 +51,11 @@ namespace Dummy } Context 'Property.Instance.ValidateSet.String' { - class C1 { [ValidateSet("Present", "Absent")][string]$Ensure } - # This call should not throw exception - [C1]::new().Ensure = "Present" + BeforeAll { + class C1 { [ValidateSet("Present", "Absent")][string]$Ensure } + # This call should not throw exception + [C1]::new().Ensure = "Present" + } It 'Error when ValidateSet should be ExceptionWhenSetting' { { [C1]::new().Ensure = "foo" } | Should -Throw -ErrorId 'ExceptionWhenSetting' @@ -61,38 +63,46 @@ namespace Dummy } Context 'Property.Static.ValidateSet.String' { - class C1 { static [ValidateSet("Present", "Absent")][string]$Ensure } - # This call should not throw exception - [C1]::Ensure = "Present" + BeforeAll { + class C1 { static [ValidateSet("Present", "Absent")][string]$Ensure } + # This call should not throw exception + [C1]::Ensure = "Present" + } It 'Error when ValidateSet should be ExceptionWhenSetting'{ { [C1]::Ensure = "foo" } | Should -Throw -ErrorId 'ExceptionWhenSetting' } } Context 'Property.Instance.ValidateRange.Int' { - class C1 { [ValidateRange(1, 10)][int]$f } - # This call should not throw exception - [C1]::new().f = 10 - [C1]::new().f = 1 + BeforeAll { + class C1 { [ValidateRange(1, 10)][int]$f } + # This call should not throw exception + [C1]::new().f = 10 + [C1]::new().f = 1 + } It 'Error when ValidateSet should be ExceptionWhenSetting'{ { [C1]::new().f = 20 } | Should -Throw -ErrorId 'ExceptionWhenSetting' } } Context 'Property.Static.ValidateRange.Int' { - class C1 { static [ValidateRange(1, 10)][int]$f } - # This call should not throw exception - [C1]::f = 5 + BeforeAll { + class C1 { static [ValidateRange(1, 10)][int]$f } + # This call should not throw exception + [C1]::f = 5 + } It 'Error when ValidateSet should be ExceptionWhenSetting'{ { [C1]::f = 20 } | Should -Throw -ErrorId 'ExceptionWhenSetting' } } Context 'Property.Static.ValidateSet.ImplicitObject' { - class C1 { static [ValidateSet("abc", 5)]$o } - # This call should not throw exception - [C1]::o = "abc" - [C1]::o = 5 + BeforeAll { + class C1 { static [ValidateSet("abc", 5)]$o } + # This call should not throw exception + [C1]::o = "abc" + [C1]::o = 5 + } It 'Error when ValidateSet should be ExceptionWhenSetting'{ { [C1]::o = 1 } | Should -Throw -ErrorId 'ExceptionWhenSetting' } @@ -106,7 +116,9 @@ namespace Dummy # Context 'Property.Instance.Transformation.ImplicitObject' { - $c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()]$arg }; [C1]::new()').Invoke()[0] + BeforeAll { + $c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()]$arg }; [C1]::new()').Invoke()[0] + } It 'Implicitly Transform to 100' { $c.arg = 100 @@ -119,7 +131,9 @@ namespace Dummy } Context 'Property.Instance.Transformation.String' { - $c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()][string]$arg }; [C1]::new()').Invoke()[0] + BeforeAll { + $c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()][string]$arg }; [C1]::new()').Invoke()[0] + } It 'set to foo' { $c.arg = "foo" $c.arg | Should -BeExactly "foofoo" @@ -127,7 +141,9 @@ namespace Dummy } Context Property.Instance.Transformation.Int { - $c = [scriptblock]::Create('class C1 { [Dummy.DoubleInt()][int]$arg }; [C1]::new()').Invoke()[0] + BeforeAll { + $c = [scriptblock]::Create('class C1 { [Dummy.DoubleInt()][int]$arg }; [C1]::new()').Invoke()[0] + } It 'arg should be 200' { $c.arg = 100 $c.arg | Should -Be 200 @@ -138,7 +154,9 @@ namespace Dummy } Context Property.Instance.Transformation.Nullable { - $c = [scriptblock]::Create('class C1 { [Nullable[int]][Dummy.DoubleStringTransformation()]$arg }; [C1]::new()').Invoke()[0] + BeforeAll { + $c = [scriptblock]::Create('class C1 { [Nullable[int]][Dummy.DoubleStringTransformation()]$arg }; [C1]::new()').Invoke()[0] + } It 'arg should be 100' { $c.arg = 100 $c.arg | Should -Be 100 @@ -146,7 +164,9 @@ namespace Dummy } Context Property.Instance.Transformation.Order { - $c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()][Dummy.AppendStringTransformation()]$arg }; [C1]::new()').Invoke()[0] + BeforeAll { + $c = [scriptblock]::Create('class C1 { [Dummy.DoubleStringTransformation()][Dummy.AppendStringTransformation()]$arg }; [C1]::new()').Invoke()[0] + } It 'arg should be 100' { $c.arg = 100 $c.arg | Should -Be 100 diff --git a/test/powershell/Language/Classes/Scripting.Classes.BasicParsing.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.BasicParsing.Tests.ps1 index 6674697ca2f..ca22b2f3662 100644 --- a/test/powershell/Language/Classes/Scripting.Classes.BasicParsing.Tests.ps1 +++ b/test/powershell/Language/Classes/Scripting.Classes.BasicParsing.Tests.ps1 @@ -106,15 +106,18 @@ Describe 'Positive Parse Properties Tests' -Tags "CI" { } Context "Positive ParseMethods return type Test" { - # Method with return type of self + # Method with return type of self - classes at body scope for proper type resolution class C9 { [C9] f() { return [C9]::new() } } - $c9 = [C9]::new().f() - It "Expected a C9 returned" { $c9.GetType().Name | Should -Be C9 } class C9a { [C9a[]] f() { return [C9a]::new() } } - $c9a = [C9a]::new().f() - It "Expected a C9a[] returned" { $c9a.GetType().Name | Should -Be C9a[] } class C9b { [System.Collections.Generic.List[C9b]] f() { return [C9b]::new() } } - $c9b = [C9b]::new().f() + + BeforeAll { + $c9 = [C9]::new().f() + $c9a = [C9a]::new().f() + $c9b = [C9b]::new().f() + } + It "Expected a C9 returned" { $c9.GetType().Name | Should -Be C9 } + It "Expected a C9a[] returned" { $c9a.GetType().Name | Should -Be C9a[] } It "Expected a System.Collections.Generic.List[C9b] returned" { $c9b -is [System.Collections.Generic.List[C9b]] | Should -BeTrue } It 'Methods returning object should return $null if no output was produced' { class Foo { @@ -372,26 +375,30 @@ Describe 'Negative DscResources Tests' -Tags "CI" { } Describe 'Negative ClassAttributes Tests' -Tags "CI" { - [System.Management.Automation.Cmdlet("Get", "Thing")]class C{} - $t = [C].GetCustomAttributes($false) - - It "Should have one attribute (class C)" {$t.Count | Should -Be 1} - It "Should have instance of CmdletAttribute (class C)" {$t[0] | Should -BeOfType System.Management.Automation.CmdletAttribute } - - [System.Management.Automation.CmdletAttribute]$c = $t[0] - It "Verb should be Get (class C)" {$c.VerbName | Should -BeExactly 'Get'} - It "Noun should be Thing (class C)" {$c.NounName | Should -BeExactly 'Thing'} - - [System.Management.Automation.Cmdlet("Get", "Thing", SupportsShouldProcess = $true, SupportsPaging = $true)]class C2{} - $t = [C2].GetCustomAttributes($false) - It "Should have one attribute (class C2)" { $t.Count | Should -Be 1 } - It "Should have instance of CmdletAttribute (class C2)" { $t[0] | Should -BeOfType System.Management.Automation.CmdletAttribute } - [System.Management.Automation.CmdletAttribute]$c = $t[0] - It "Verb should be Get (class C2)" {$c.VerbName | Should -BeExactly 'Get'} - It "Noun should be Thing (class C2)" {$c.NounName | Should -BeExactly 'Thing'} - - It "SupportsShouldProcess should be $true" { $c.SupportsShouldProcess | Should -BeTrue } - It "SupportsPaging should be `$true" { $c.SupportsPaging | Should -BeTrue } + [System.Management.Automation.Cmdlet("Get", "Thing")]class C_NCA{} + [System.Management.Automation.Cmdlet("Get", "Thing", SupportsShouldProcess = $true, SupportsPaging = $true)]class C2_NCA{} + + BeforeAll { + $tC = [C_NCA].GetCustomAttributes($false) + [System.Management.Automation.CmdletAttribute]$cAttr = $tC[0] + + $tC2 = [C2_NCA].GetCustomAttributes($false) + [System.Management.Automation.CmdletAttribute]$c2Attr = $tC2[0] + } + + It "Should have one attribute (class C)" {$tC.Count | Should -Be 1} + It "Should have instance of CmdletAttribute (class C)" {$tC[0] | Should -BeOfType System.Management.Automation.CmdletAttribute } + + It "Verb should be Get (class C)" {$cAttr.VerbName | Should -BeExactly 'Get'} + It "Noun should be Thing (class C)" {$cAttr.NounName | Should -BeExactly 'Thing'} + + It "Should have one attribute (class C2)" { $tC2.Count | Should -Be 1 } + It "Should have instance of CmdletAttribute (class C2)" { $tC2[0] | Should -BeOfType System.Management.Automation.CmdletAttribute } + It "Verb should be Get (class C2)" {$c2Attr.VerbName | Should -BeExactly 'Get'} + It "Noun should be Thing (class C2)" {$c2Attr.NounName | Should -BeExactly 'Thing'} + + It "SupportsShouldProcess should be True" { $c2Attr.SupportsShouldProcess | Should -BeTrue } + It "SupportsPaging should be `$true" { $c2Attr.SupportsPaging | Should -BeTrue } Context "Support ConfirmImpact as an attribute" { It "ConfirmImpact should be high" { [System.Management.Automation.Cmdlet("Get", "Thing", SupportsShouldProcess = $true, ConfirmImpact = 'High', SupportsPaging = $true)]class C3{} @@ -406,11 +413,13 @@ Describe 'Negative ClassAttributes Tests' -Tags "CI" { } Describe 'Property Attributes Test' -Tags "CI" { - class C { [ValidateSet('a', 'b')]$p; } + class C_PA { [ValidateSet('a', 'b')]$p; } - $t = [C].GetProperty('p').GetCustomAttributes($false) + BeforeAll { + $t = [C_PA].GetProperty('p').GetCustomAttributes($false) + [ValidateSet]$v = $t[0] + } It "Should have one attribute" { $t.Count | Should -Be 1 } - [ValidateSet]$v = $t[0] It "Should have 2 valid values" { $v.ValidValues.Count | Should -Be 2 } It "first value should be a" { $v.ValidValues[0] | Should -Be 'a' } It "second value should be b" { $v.ValidValues[1] | Should -Be 'b' } @@ -530,25 +539,29 @@ Describe 'Testing Method Names can be Keywords' -Tags "CI" { } Describe 'Method Attributes Test' -Tags "CI" { - class C { [Obsolete("aaa")][int]f() { return 1 } } + BeforeAll { + class C { [Obsolete("aaa")][int]f() { return 1 } } - $t = [C].GetMethod('f').GetCustomAttributes($false) + $t = [C].GetMethod('f').GetCustomAttributes($false) + } It "Should have one attribute" {$t.Count | Should -Be 1 } It "Attribute type should be ObsoleteAttribute" { $t[0].GetType().FullName | Should -Be System.ObsoleteAttribute } } Describe 'Positive SelfClass Type As Parameter Test' -Tags "CI" { - class Point - { - Point($x, $y) { $this.x = $x; $this.y = $y } - Point() {} + BeforeAll { + class Point + { + Point($x, $y) { $this.x = $x; $this.y = $y } + Point() {} - [int] $x = 0 - [int] $y = 0 - Add([Point] $val) { $this.x += $val.x; $this.y += $val.y; } + [int] $x = 0 + [int] $y = 0 + Add([Point] $val) { $this.x += $val.x; $this.y += $val.y; } - Print() { Write-Host "[`$x=$($this.x) `$y=$($this.y)]" } - Set($x, $y) { $this.x = $x; $this.y = $y } + Print() { Write-Host "[`$x=$($this.x) `$y=$($this.y)]" } + Set($x, $y) { $this.x = $x; $this.y = $y } + } } It "[Point]::Add works construction via ::new" { $point = [Point]::new(100,200) @@ -570,26 +583,37 @@ Describe 'Positive SelfClass Type As Parameter Test' -Tags "CI" { } Describe 'PositiveReturnSelfClassTypeFromMemberFunction Test' -Tags "CI" { - class ReturnObjectFromMemberFunctionTest - { - [ReturnObjectFromMemberFunctionTest] CreateInstance() + BeforeAll { + class ReturnObjectFromMemberFunctionTest { - return [ReturnObjectFromMemberFunctionTest]::new() - } - [string] SayHello() - { - return "Hello1" + [ReturnObjectFromMemberFunctionTest] CreateInstance() + { + return [ReturnObjectFromMemberFunctionTest]::new() + } + [string] SayHello() + { + return "Hello1" + } } + $f = [ReturnObjectFromMemberFunctionTest]::new() + $z = $f.CreateInstance() # Line 13 } - $f = [ReturnObjectFromMemberFunctionTest]::new() - $z = $f.CreateInstance() # Line 13 It "CreateInstance works" { $z.SayHello() | Should -BeExactly 'Hello1' } } Describe 'TestMultipleArguments Test' -Tags "CI" { - if ( $IsCoreCLR ) { $maxCount = 14 } else { $maxCount = 16 } - for ($i = 0; $i -lt $maxCount; $i++) - { + BeforeDiscovery { + if ( $IsCoreCLR ) { $maxCount = 14 } else { $maxCount = 16 } + $testCases = 0..($maxCount - 1) | ForEach-Object { + $idx = $_ + @{ + i = $idx + expectedTotal = (0..$idx | Measure-Object -Sum).Sum + } + } + } + Context 'with arguments (expected: )' -ForEach $testCases { + BeforeAll { $properties = $(for ($j = 0; $j -le $i; $j++) { " [int]`$Prop$j" }) -join "`n" @@ -614,8 +638,6 @@ Describe 'TestMultipleArguments Test' -Tags "CI" { "`$inst.`Prop$j" }) -join " + " - $expectedTotal = (0..$i | Measure-Object -Sum).Sum - $class = @" class Foo { @@ -634,51 +656,55 @@ $ctorAssignments `$inst = [Foo]::new($methodArguments) `$sum = $addUpProperties - It "ExpectedTotal: Sum should be $expectedTotal" { `$sum | Should -Be $expectedTotal } - It "ExpectedTotal: Invocation should return $expectedTotal" { `$inst.DoSomething($methodArguments) | Should -Be $expectedTotal } + `$methodResult = `$inst.DoSomething($methodArguments) "@ Invoke-Expression $class } + It "ExpectedTotal: Sum should be " { $sum | Should -Be $expectedTotal } + It "ExpectedTotal: Invocation should return " { $methodResult | Should -Be $expectedTotal } + } } Describe 'Scopes Test' -Tags "CI" { - class C1 - { - static C1() { - $global:foo = $script:foo - } - C1() { - $script:bar = $global:foo - } - static [int] f1() { - return $script:bar + $global:bar - } - [int] f2() { - return $script:bar + $global:bar + BeforeAll { + class C1 + { + static C1() { + $global:foo = $script:foo + } + C1() { + $script:bar = $global:foo + } + static [int] f1() { + return $script:bar + $global:bar + } + [int] f2() { + return $script:bar + $global:bar + } } } } Describe 'Check PS Class Assembly Test' -Tags "CI" { - class C1 {} - $assem = [C1].Assembly - $attrs = @($assem.GetCustomAttributes($true)) - $expectedAttr = @($attrs | Where-Object { $_ -is [System.Management.Automation.DynamicClassImplementationAssemblyAttribute] }) + BeforeAll { + class C1 {} + $assem = [C1].Assembly + $attrs = @($assem.GetCustomAttributes($true)) + $expectedAttr = @($attrs | Where-Object { $_ -is [System.Management.Automation.DynamicClassImplementationAssemblyAttribute] }) + } It "Expected a DynamicClassImplementationAssembly attribute" { $expectedAttr.Length | Should -Be 1} } Describe 'ScriptScopeAccessFromClassMethod' -Tags "CI" { - Import-Module "$PSScriptRoot\MSFT_778492.psm1" - try - { + BeforeAll { + Import-Module "$PSScriptRoot\MSFT_778492.psm1" $c = Get-MSFT_778492 - It "Method should have found variable in module scope" { $c.F() | Should -BeExactly 'MSFT_778492 script scope'} } - finally - { - Remove-Module MSFT_778492 + AfterAll { + Remove-Module MSFT_778492 -ErrorAction SilentlyContinue } + It "Method should have found variable in module scope" { $c.F() | Should -BeExactly 'MSFT_778492 script scope'} } Describe 'Hidden Members Test ' -Tags "CI" { @@ -695,15 +721,20 @@ Describe 'Hidden Members Test ' -Tags "CI" { } } - class C1 - { - [int]$visibleX - [int]$visibleY - hidden [int]$hiddenZ - } + BeforeAll { + class C1 + { + [int]$visibleX + [int]$visibleY + hidden [int]$hiddenZ + } - # Create an instance - $instance = [C1]@{ visibleX = 10; visibleY = 12; hiddenZ = 42 } + $instance = [C1]@{ visibleX = 10; visibleY = 12; hiddenZ = 42 } + $memberWithoutForce = $instance | Get-Member hiddenZ + $memberWithForce = $instance | Get-Member hiddenZ -Force + $line = 'class C2 { hidden [int]$hiddenZ } [C2]::new().h' + $completions = [System.Management.Automation.CommandCompletion]::CompleteInput($line, $line.Length, $null) + } It "Access hidden property should still work" { $instance.hiddenZ | Should -Be 42 } @@ -721,17 +752,10 @@ visibleX visibleY $tableOutput.Replace("`r","") | Should -BeExactly $expectedTable.Replace("`r","") } - # Get-Member should not include hidden members by default - $member = $instance | Get-Member hiddenZ - It "Get-Member should not find hidden member w/o -Force" { $member | Should -BeNullOrEmpty } + It "Get-Member should not find hidden member w/o -Force" { $memberWithoutForce | Should -BeNullOrEmpty } - # Get-Member should include hidden members with -Force - $member = $instance | Get-Member hiddenZ -Force - It "Get-Member should find hidden member w/ -Force" { $member | Should -Not -BeNullOrEmpty } + It "Get-Member should find hidden member w/ -Force" { $memberWithForce | Should -Not -BeNullOrEmpty } - # Tab completion should not return a hidden member - $line = 'class C2 { hidden [int]$hiddenZ } [C2]::new().h' - $completions = [System.Management.Automation.CommandCompletion]::CompleteInput($line, $line.Length, $null) It "Tab completion should not return a hidden member" { $completions.CompletionMatches.Count | Should -Be 0 } } @@ -742,20 +766,22 @@ Describe 'BaseMethodCall Test ' -Tags "CI" { } Describe 'Scoped Types Test' -Tags "CI" { - class C1 { [string] GetContext() { return "Test scope" } } + BeforeAll { + class C1 { [string] GetContext() { return "Test scope" } } - filter f1 - { - class C1 { [string] GetContext() { return "f1 scope" } } + filter f1 + { + class C1 { [string] GetContext() { return "f1 scope" } } - return [C1]::new().GetContext() - } + return [C1]::new().GetContext() + } - filter f2 - { - class C1 { [string] GetContext() { return "f2 scope" } } + filter f2 + { + class C1 { [string] GetContext() { return "f2 scope" } } - return (New-Object C1).GetContext() + return (New-Object C1).GetContext() + } } It "New-Object at test scope" { (New-Object C1).GetContext() | Should -BeExactly "Test scope" } @@ -769,20 +795,18 @@ Describe 'Scoped Types Test' -Tags "CI" { } Describe 'ParameterOfClassTypeInModule Test' -Tags "CI" { - try - { + BeforeAll { $sb = [scriptblock]::Create(@' enum EE {one = 1} function test-it([EE]$ee){$ee} '@) $mod = New-Module $sb -Name MSFT_2081529 | Import-Module $result = test-it -ee one - It "Parameter of class/enum type defined in module should work" { $result | Should -Be 1 } } - finally - { + AfterAll { Remove-Module -ErrorAction ignore MSFT_2081529 } + It "Parameter of class/enum type defined in module should work" { $result | Should -Be 1 } } Describe 'Type building' -Tags "CI" { @@ -854,8 +878,9 @@ class Derived : Base Describe 'TypeTable lookups' -Tags "CI" { Context 'Call methods from a different thread' { - $b = [powershell]::Create().AddScript( -@' + BeforeAll { + $b = [powershell]::Create().AddScript( + @' class A {} class B { @@ -866,6 +891,7 @@ class B [B]::new() '@).Invoke()[0] + } It 'can do type lookup by name' { $b.getA1() | Should -BeExactly 'A' @@ -879,7 +905,8 @@ class B Describe 'Protected method access' -Tags "CI" { - Add-Type @' + BeforeAll { + Add-Type @' namespace Foo { public class Bar @@ -888,6 +915,7 @@ namespace Foo } } '@ + } It 'doesn''t allow protected methods access outside of inheritance chain' { $a = [scriptblock]::Create(@' diff --git a/test/powershell/Language/Classes/Scripting.Classes.Break.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.Break.Tests.ps1 index 20aab127abc..6dcfd3bf8a2 100644 --- a/test/powershell/Language/Classes/Scripting.Classes.Break.Tests.ps1 +++ b/test/powershell/Language/Classes/Scripting.Classes.Break.Tests.ps1 @@ -2,11 +2,13 @@ # Licensed under the MIT License. Describe 'Break statements with classes' -Tags "CI" { - function Get-Errors([string]$sourceCode) { - $tokens = $null - $errors = $null - $ast = [System.Management.Automation.Language.Parser]::ParseInput($sourceCode, [ref] $tokens, [ref] $errors) - return $errors + BeforeAll { + function Get-Errors([string]$sourceCode) { + $tokens = $null + $errors = $null + $ast = [System.Management.Automation.Language.Parser]::ParseInput($sourceCode, [ref] $tokens, [ref] $errors) + return $errors + } } Context 'break is inside a class method' { diff --git a/test/powershell/Language/Classes/Scripting.Classes.Exceptions.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.Exceptions.Tests.ps1 index d4b6e77a290..e2e2292456e 100644 --- a/test/powershell/Language/Classes/Scripting.Classes.Exceptions.Tests.ps1 +++ b/test/powershell/Language/Classes/Scripting.Classes.Exceptions.Tests.ps1 @@ -2,12 +2,14 @@ # Licensed under the MIT License. Describe 'Exceptions flow for classes' -Tags "CI" { - $canaryHashtable = @{} + BeforeAll { + $canaryHashtable = @{} - $iss = [initialsessionstate]::CreateDefault() - $iss.Variables.Add([System.Management.Automation.Runspaces.SessionStateVariableEntry]::new('canaryHashtable', $canaryHashtable, $null)) - $iss.Commands.Add([System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new('Get-Canary', '$canaryHashtable')) - $ps = [powershell]::Create($iss) + $iss = [initialsessionstate]::CreateDefault() + $iss.Variables.Add([System.Management.Automation.Runspaces.SessionStateVariableEntry]::new('canaryHashtable', $canaryHashtable, $null)) + $iss.Commands.Add([System.Management.Automation.Runspaces.SessionStateFunctionEntry]::new('Get-Canary', '$canaryHashtable')) + $ps = [powershell]::Create($iss) + } BeforeEach { $canaryHashtable.Clear() @@ -133,7 +135,8 @@ class C Context 'Class method call PS function' { - $body = @' + BeforeAll { + $body = @' class C { [void] m1() @@ -174,6 +177,7 @@ function ImThrow() } '@ + } It 'does not execute statements after function with exception called from instance method' { @@ -226,12 +230,14 @@ $canaryHashtable['canary'] += 100 } Describe "Exception error position" -Tags "CI" { - class MSFT_3090412 - { - static f1() { [MSFT_3090412]::bar = 42 } - static f2() { throw "an error in f2" } - static f3() { "".Substring(0, 10) } - static f4() { Get-ChildItem nosuchfile -ErrorAction Stop } + BeforeAll { + class MSFT_3090412 + { + static f1() { [MSFT_3090412]::bar = 42 } + static f2() { throw "an error in f2" } + static f3() { "".Substring(0, 10) } + static f4() { Get-ChildItem nosuchfile -ErrorAction Stop } + } } It "Setting a property that doesn't exist" { diff --git a/test/powershell/Language/Classes/Scripting.Classes.MiscOps.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.MiscOps.Tests.ps1 index 11880bf7dce..931ed93c26a 100644 --- a/test/powershell/Language/Classes/Scripting.Classes.MiscOps.Tests.ps1 +++ b/test/powershell/Language/Classes/Scripting.Classes.MiscOps.Tests.ps1 @@ -3,16 +3,18 @@ Describe 'Misc Test' -Tags "CI" { Context 'Where' { - class C1 { - [int[]] $Wheels = @(1,2,3); - [string] Foo() { - return (1..10).Where({ $PSItem -in $this.Wheels; }) -join ';' - } + BeforeAll { + class C1 { + [int[]] $Wheels = @(1,2,3); + [string] Foo() { + return (1..10).Where({ $PSItem -in $this.Wheels; }) -join ';' + } - [string] Bar() - { - return (1..10 | Where-Object { $PSItem -in $this.Wheels; }) -join ';' - } + [string] Bar() + { + return (1..10 | Where-Object { $PSItem -in $this.Wheels; }) -join ';' + } + } } It 'Invoke Where' { [C1]::new().Foo() | Should -Be "1;2;3" @@ -23,20 +25,22 @@ Describe 'Misc Test' -Tags "CI" { } Context 'ForEach' { - class C1 { - [int[]] $Wheels = @(1,2,3); - [string] Foo() { - $ret="" - Foreach($PSItem in $this.Wheels) { $ret +="$PSItem;"} - return $ret - } + BeforeAll { + class C1 { + [int[]] $Wheels = @(1,2,3); + [string] Foo() { + $ret="" + Foreach($PSItem in $this.Wheels) { $ret +="$PSItem;"} + return $ret + } - [string] Bar() - { - $ret = "" - $this.Wheels | ForEach-Object { $ret += "$_;" } - return $ret - } + [string] Bar() + { + $ret = "" + $this.Wheels | ForEach-Object { $ret += "$_;" } + return $ret + } + } } It 'Invoke Foreach' { [C1]::new().Foo() | Should -Be "1;2;3;" @@ -47,7 +51,7 @@ Describe 'Misc Test' -Tags "CI" { } Context 'Class instantiation' { - Class C1 { + Class C1_Inst { [string] Foo() { return (Get-TestText) } @@ -101,7 +105,7 @@ Describe 'Misc Test' -Tags "CI" { } It "Create instance that is bound to a SessionState" { - $instance = [C1]::new() + $instance = [C1_Inst]::new() ## For a bound class instance, the execution of an instance method is ## done in the Runspace/SessionState the instance is bound to. $instance.Foo() | Should -BeExactly $ExpectedTextFromBoundInstance @@ -109,7 +113,7 @@ Describe 'Misc Test' -Tags "CI" { } It "Create instance that is NOT bound to a SessionState" { - $instance = InstantiateInNewRunspace ([C1]) + $instance = InstantiateInNewRunspace ([C1_Inst]) ## For an unbound class instance, the execution of an instance method is done in ## the Runspace/SessionState where the call to the instance method is made. $instance.Foo() | Should -BeExactly $ExpectedTextFromBoundInstance diff --git a/test/powershell/Language/Classes/Scripting.Classes.Modules.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.Modules.Tests.ps1 index c2e376f10cf..eaaf1397872 100644 --- a/test/powershell/Language/Classes/Scripting.Classes.Modules.Tests.ps1 +++ b/test/powershell/Language/Classes/Scripting.Classes.Modules.Tests.ps1 @@ -8,33 +8,33 @@ Describe 'PSModuleInfo.GetExportedTypeDefinitions()' -Tags "CI" { } Describe 'use of a module from two runspaces' -Tags "CI" { - function New-TestModule { - param( - [string]$Name, - [string]$Content - ) + BeforeAll { + function New-TestModule { + param( + [string]$Name, + [string]$Content + ) - $TestModulePath = Join-Path -Path $TestDrive -ChildPath "TestModule" - $ModuleFolder = Join-Path -Path $TestModulePath -ChildPath $Name - New-Item -Path $ModuleFolder -ItemType Directory -Force > $null + $TestModulePath = Join-Path -Path $TestDrive -ChildPath "TestModule" + $ModuleFolder = Join-Path -Path $TestModulePath -ChildPath $Name + New-Item -Path $ModuleFolder -ItemType Directory -Force > $null - Set-Content -Path "$ModuleFolder\$Name.psm1" -Value $Content + Set-Content -Path "$ModuleFolder\$Name.psm1" -Value $Content - $manifestParams = @{ - Path = "$ModuleFolder\$Name.psd1" - RootModule = "$Name.psm1" - } - New-ModuleManifest @manifestParams + $manifestParams = @{ + Path = "$ModuleFolder\$Name.psd1" + RootModule = "$Name.psm1" + } + New-ModuleManifest @manifestParams - if ($env:PSModulePath -NotLike "*$TestModulePath*") { - $env:PSModulePath += "$([System.IO.Path]::PathSeparator)$TestModulePath" + if ($env:PSModulePath -NotLike "*$TestModulePath*") { + $env:PSModulePath += "$([System.IO.Path]::PathSeparator)$TestModulePath" + } } - } - $originalPSModulePath = $env:PSModulePath - try { + $originalPSModulePath = $env:PSModulePath - New-TestModule -Name 'Random' -Content @' + New-TestModule -Name 'Random' -Content @' $script:random = Get-Random class RandomWrapper { @@ -44,6 +44,11 @@ class RandomWrapper } } '@ + } + + AfterAll { + $env:PSModulePath = $originalPSModulePath + } It 'use different sessionStates for different modules' { $ps = 1..2 | ForEach-Object { $p = [powershell]::Create().AddScript(@' @@ -68,10 +73,6 @@ Import-Module Random $res[1] | Should -Be $res[3] } - } finally { - $env:PSModulePath = $originalPSModulePath - } - } Describe 'Module reloading with Class definition' -Tags "CI" { diff --git a/test/powershell/Language/Classes/Scripting.Classes.RunPath.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.RunPath.Tests.ps1 index e6545d0ed28..005dcc8a550 100644 --- a/test/powershell/Language/Classes/Scripting.Classes.RunPath.Tests.ps1 +++ b/test/powershell/Language/Classes/Scripting.Classes.RunPath.Tests.ps1 @@ -2,10 +2,12 @@ # Licensed under the MIT License. Describe "Script with a class definition run path" -Tags "CI" { - $TestCases = @( - @{ FileName = 'MyTest.ps1'; Name = 'path without a comma' } - @{ FileName = 'My,Test.ps1'; Name = 'path with a comma' } - ) + BeforeDiscovery { + $TestCases = @( + @{ FileName = 'MyTest.ps1'; Name = 'path without a comma' } + @{ FileName = 'My,Test.ps1'; Name = 'path with a comma' } + ) + } It "Script with a class definition can run from a " -TestCases $TestCases { param( $FileName ) diff --git a/test/powershell/Language/Classes/scripting.Classes.NestedModules.tests.ps1 b/test/powershell/Language/Classes/scripting.Classes.NestedModules.tests.ps1 index 97f69982e21..7293d759378 100644 --- a/test/powershell/Language/Classes/scripting.Classes.NestedModules.tests.ps1 +++ b/test/powershell/Language/Classes/scripting.Classes.NestedModules.tests.ps1 @@ -102,9 +102,11 @@ using module WithRoot Context 'execute type creation in the module context' { # let's define types to make it more fun - class A { [string] foo() { return "local"} } - class B { [string] foo() { return "local"} } - class C { [string] foo() { return "local"} } + BeforeAll { + class A { [string] foo() { return "local"} } + class B { [string] foo() { return "local"} } + class C { [string] foo() { return "local"} } + } # We need to think about it: should it work or not. # Currently, types are resolved in compile-time to the 'local' versions diff --git a/test/powershell/Language/Classes/scripting.Classes.inheritance.tests.ps1 b/test/powershell/Language/Classes/scripting.Classes.inheritance.tests.ps1 index 6b24b8b6ce0..d0061aef32b 100644 --- a/test/powershell/Language/Classes/scripting.Classes.inheritance.tests.ps1 +++ b/test/powershell/Language/Classes/scripting.Classes.inheritance.tests.ps1 @@ -502,16 +502,18 @@ Describe 'Classes methods with inheritance' -Tags "CI" { } Context 'base static method call' { - class A - { - static [string]ToStr([int]$a) {return "A" + $a} - } - class B : A - { - static [string]ToStr([int]$a) {return "B" + $a} - } + BeforeAll { + class A + { + static [string]ToStr([int]$a) {return "A" + $a} + } + class B : A + { + static [string]ToStr([int]$a) {return "B" + $a} + } - $b = [B]::new() + $b = [B]::new() + } # MSFT:1911652 # MSFT:2973835 @@ -718,8 +720,7 @@ Describe 'Base type has abstract properties' -Tags "CI" { Describe 'Classes inheritance with protected and protected internal members in base class' -Tags 'CI' { - BeforeAll { - Set-StrictMode -Version 3 + BeforeDiscovery { $c1DefinitionProtectedInternal = @' public class C1ProtectedInternal { @@ -778,6 +779,10 @@ Describe 'Classes inheritance with protected and protected internal members in b ) } + BeforeAll { + Set-StrictMode -Version 3 + } + AfterAll { Set-StrictMode -Off } @@ -872,7 +877,7 @@ Describe 'Classes inheritance with protected and protected internal members in b Context 'Base class members are not accessible outside class scope' { - BeforeAll { + BeforeDiscovery { $instanceTest = { $c2 = $derivedType::new() { $null = $c2.InstanceField } | Should -Throw -ErrorId 'PropertyNotFoundStrict' @@ -909,6 +914,27 @@ Describe 'Classes inheritance with protected and protected internal members in b }) } + BeforeAll { + $instanceTest = { + $c2 = $derivedType::new() + { $null = $c2.InstanceField } | Should -Throw -ErrorId 'PropertyNotFoundStrict' + { $null = $c2.InstanceProperty } | Should -Throw -ErrorId 'PropertyNotFoundStrict' + { $null = $c2.VirtualProperty1 } | Should -Throw -ErrorId 'PropertyNotFoundStrict' + { $c2.InstanceField = 'foo' } | Should -Throw -ErrorId 'PropertyAssignmentException' + { $c2.InstanceProperty = 'foo' } | Should -Throw -ErrorId 'PropertyAssignmentException' + { $c2.VirtualProperty1 = 'foo' } | Should -Throw -ErrorId 'PropertyAssignmentException' + { $derivedType::new().InstanceMethod() } | Should -Throw -ErrorId 'MethodNotFound' + { $derivedType::new().VirtualMethod1() } | Should -Throw -ErrorId 'MethodNotFound' + foreach ($name in @('InstanceField', 'InstanceProperty', 'VirtualProperty1')) { + { $null = $c2.$name } | Should -Throw -ErrorId 'PropertyNotFoundStrict' + { $c2.$name = 'foo' } | Should -Throw -ErrorId 'PropertyAssignmentException' + } + foreach ($name in @('InstanceMethod', 'VirtualMethod1')) { + { $c2.$name() } | Should -Throw -ErrorId 'MethodNotFound' + } + } + } + It 'cannot access instance base members in ' -TestCases $negativeTestCases { param($derivedType, $classScope) if ($null -eq $classScope) { diff --git a/test/powershell/Language/Interop/DotNet/DotNetInterop.Tests.ps1 b/test/powershell/Language/Interop/DotNet/DotNetInterop.Tests.ps1 index 42426b8279a..8e1a7efd1e8 100644 --- a/test/powershell/Language/Interop/DotNet/DotNetInterop.Tests.ps1 +++ b/test/powershell/Language/Interop/DotNet/DotNetInterop.Tests.ps1 @@ -3,7 +3,7 @@ Describe "Handle ByRef-like types gracefully" -Tags "CI" { - BeforeAll { + BeforeDiscovery { $code = @' using System; using System.Management.Automation; @@ -105,7 +105,9 @@ namespace DotNetInterop { Add-Type -TypeDefinition $code -IgnoreWarnings } + } + BeforeAll { $testObj = [DotNetInterop.Test]::new() $test2Obj = [DotNetInterop.Test2]::new() } diff --git a/test/powershell/Language/Operators/NullConditional.Tests.ps1 b/test/powershell/Language/Operators/NullConditional.Tests.ps1 index 237b4d8c987..4656474e159 100644 --- a/test/powershell/Language/Operators/NullConditional.Tests.ps1 +++ b/test/powershell/Language/Operators/NullConditional.Tests.ps1 @@ -2,7 +2,7 @@ # Licensed under the MIT License. Describe 'NullCoalesceOperations' -Tags 'CI' { - BeforeAll { + BeforeDiscovery { $someGuid = New-Guid $typesTests = @( @{ name = 'string'; valueToSet = 'hello' } diff --git a/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 b/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 index 15340dd18e6..60736596642 100644 --- a/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 +++ b/test/powershell/Language/Operators/PipelineChainOperator.Tests.ps1 @@ -2,34 +2,7 @@ # Licensed under the MIT License. Describe "Experimental Feature: && and || operators - Feature-Enabled" -Tag CI { - BeforeAll { - function Test-SuccessfulCommand - { - Write-Output "SUCCESS" - } - - filter Test-NonTerminatingError - { - [CmdletBinding()] - param( - [Parameter(ValueFromPipeline)] - [object[]] - $input - ) - - if ($input -ne 2) - { - return $input - } - - $exception = [System.Exception]::new("NTERROR") - $errorId = 'NTERROR' - $errorCategory = [System.Management.Automation.ErrorCategory]::NotSpecified - - $errorRecord = [System.Management.Automation.ErrorRecord]::new($exception, $errorId, $errorCategory, $null) - $PSCmdlet.WriteError($errorRecord) - } - + BeforeDiscovery { $simpleTestCases = @( # Two native commands @{ Statement = 'testexe -returncode -1 && testexe -echoargs "A"'; Output = @('-1') } @@ -126,6 +99,36 @@ Describe "Experimental Feature: && and || operators - Feature-Enabled" -Tag CI { ) } + BeforeAll { + function Test-SuccessfulCommand + { + Write-Output "SUCCESS" + } + + filter Test-NonTerminatingError + { + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline)] + [object[]] + $input + ) + + if ($input -ne 2) + { + return $input + } + + $exception = [System.Exception]::new("NTERROR") + $errorId = 'NTERROR' + $errorCategory = [System.Management.Automation.ErrorCategory]::NotSpecified + + $errorRecord = [System.Management.Automation.ErrorRecord]::new($exception, $errorId, $errorCategory, $null) + $PSCmdlet.WriteError($errorRecord) + } + + } + It "Gets the correct output with statement ''" -TestCases $simpleTestCases { param($Statement, $Output) @@ -168,15 +171,17 @@ Describe "Experimental Feature: && and || operators - Feature-Enabled" -Tag CI { } Context "File redirection with && and ||" { - BeforeAll { + BeforeDiscovery { + # $TestDrive unavailable during Discovery; use placeholder replaced at runtime + $tdPlaceholder = 'TESTDRIVE_PLACEHOLDER' $redirectionTestCases = @( - @{ Statement = "testexe -returncode 0 > '$TestDrive/1.txt' && testexe -returncode 1 > '$TestDrive/2.txt'"; Files = @{ "$TestDrive/1.txt" = '0'; "$TestDrive/2.txt" = '1' } } - @{ Statement = "testexe -returncode 1 > '$TestDrive/1.txt' && testexe -returncode 1 > '$TestDrive/2.txt'"; Files = @{ "$TestDrive/1.txt" = '1'; "$TestDrive/2.txt" = $null } } - @{ Statement = "testexe -returncode 1 > '$TestDrive/1.txt' || testexe -returncode 1 > '$TestDrive/2.txt'"; Files = @{ "$TestDrive/1.txt" = '1'; "$TestDrive/2.txt" = '1' } } - @{ Statement = "testexe -returncode 0 > '$TestDrive/1.txt' || testexe -returncode 1 > '$TestDrive/2.txt'"; Files = @{ "$TestDrive/1.txt" = '0'; "$TestDrive/2.txt" = $null } } - @{ Statement = "(testexe -returncode 0 && testexe -returncode 1) > '$TestDrive/3.txt'"; Files = @{ "$TestDrive/3.txt" = "0$([System.Environment]::NewLine)1$([System.Environment]::NewLine)" } } - @{ Statement = "(testexe -returncode 0 && testexe -returncode 1 > '$TestDrive/2.txt') > '$TestDrive/3.txt'"; Files = @{ "$TestDrive/2.txt" = '1'; "$TestDrive/3.txt" = '0' } } - @{ Statement = "(testexe -returncode 0 > '$TestDrive/1.txt' && testexe -returncode 1 > '$TestDrive/2.txt') > '$TestDrive/3.txt'"; Files = @{ "$TestDrive/1.txt" = '0'; "$TestDrive/2.txt" = '1'; "$TestDrive/3.txt" = '' } } + @{ Statement = "testexe -returncode 0 > '$tdPlaceholder/1.txt' && testexe -returncode 1 > '$tdPlaceholder/2.txt'"; Files = @{ "$tdPlaceholder/1.txt" = '0'; "$tdPlaceholder/2.txt" = '1' } } + @{ Statement = "testexe -returncode 1 > '$tdPlaceholder/1.txt' && testexe -returncode 1 > '$tdPlaceholder/2.txt'"; Files = @{ "$tdPlaceholder/1.txt" = '1'; "$tdPlaceholder/2.txt" = $null } } + @{ Statement = "testexe -returncode 1 > '$tdPlaceholder/1.txt' || testexe -returncode 1 > '$tdPlaceholder/2.txt'"; Files = @{ "$tdPlaceholder/1.txt" = '1'; "$tdPlaceholder/2.txt" = '1' } } + @{ Statement = "testexe -returncode 0 > '$tdPlaceholder/1.txt' || testexe -returncode 1 > '$tdPlaceholder/2.txt'"; Files = @{ "$tdPlaceholder/1.txt" = '0'; "$tdPlaceholder/2.txt" = $null } } + @{ Statement = "(testexe -returncode 0 && testexe -returncode 1) > '$tdPlaceholder/3.txt'"; Files = @{ "$tdPlaceholder/3.txt" = "0$([System.Environment]::NewLine)1$([System.Environment]::NewLine)" } } + @{ Statement = "(testexe -returncode 0 && testexe -returncode 1 > '$tdPlaceholder/2.txt') > '$tdPlaceholder/3.txt'"; Files = @{ "$tdPlaceholder/2.txt" = '1'; "$tdPlaceholder/3.txt" = '0' } } + @{ Statement = "(testexe -returncode 0 > '$tdPlaceholder/1.txt' && testexe -returncode 1 > '$tdPlaceholder/2.txt') > '$tdPlaceholder/3.txt'"; Files = @{ "$tdPlaceholder/1.txt" = '0'; "$tdPlaceholder/2.txt" = '1'; "$tdPlaceholder/3.txt" = '' } } ) } @@ -187,6 +192,14 @@ Describe "Experimental Feature: && and || operators - Feature-Enabled" -Tag CI { It "Handles redirection correctly with statement ''" -TestCases $redirectionTestCases { param($Statement, $Files) + # Resolve TESTDRIVE_PLACEHOLDER from Discovery with actual $TestDrive + $Statement = $Statement.Replace('TESTDRIVE_PLACEHOLDER', $TestDrive) + $resolvedFiles = @{} + foreach ($key in $Files.get_Keys()) { + $resolvedFiles[$key.Replace('TESTDRIVE_PLACEHOLDER', $TestDrive)] = $Files[$key] + } + $Files = $resolvedFiles + Invoke-Expression -Command $Statement foreach ($file in $Files.get_Keys()) @@ -212,6 +225,54 @@ Describe "Experimental Feature: && and || operators - Feature-Enabled" -Tag CI { } Context "Pipeline chain error semantics" { + BeforeDiscovery { + $errorSemanticsCases = @( + # Simple error semantics + @{ Statement = '1,2,3 | Test-NonTerminatingError || Write-Output 4'; Output = @(1, 3, 4); NTErrors = @('NTError') } + @{ Statement = '1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2'; Output = @(1, 3, 2); NTErrors = @('PIPELINE') } + @{ Statement = 'Test-FullyTerminatingError || Write-Output 2'; ThrownError = 'TERMINATE' } + @{ Statement = '1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4'; ThrownError = 'NTERROR,Test-NonTerminatingError' } + + # Assignment error semantics + @{ Statement = '$x = 1,2,3 | Test-NonTerminatingError || Write-Output 4; $x'; Output = @(1, 3, 4); NTErrors = @('NTError') } + @{ Statement = '$x = 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2; $x'; Output = @(1, 3, 2); NTErrors = @('PIPELINE') } + + # Try/catch semantics + @{ Statement = 'try { Write-Output 2 && Test-FullyTerminatingError } catch {}'; Output = @(2) } + @{ Statement = 'try { 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2 } catch {}'; Output = @(1, 3) } + @{ Statement = 'try { 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4 } catch {}'; Output = @(1) } + @{ Statement = 'try { 1,2,3 | Test-NonTerminatingError || Write-Output 4 } catch {}'; Output = @(1, 3, 4); NTErrors = @('NTError') } + @{ Statement = 'try { $result = Write-Output 2 && Test-FullyTerminatingError } catch {}; $result'; Output = @() } + @{ Statement = 'try { $result = 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2 } catch {}; $result'; Output = @() } + @{ Statement = 'try { $result = 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4 } catch {}; $result'; Output = @() } + @{ Statement = 'try { $result = 1,2,3 | Test-NonTerminatingError || Write-Output 4 } catch {}; $result'; Output = @(1, 3, 4); NTErrors = @('NTError') } + @{ Statement = 'try { "Hi" && "Bye" } catch { "Nothing" }'; Output = @("Hi", "Bye") } + @{ Statement = 'try { "Hi" && "Bye" } catch { "Nothing" } finally { "Final" }'; Output = @("Hi", "Bye", "Final") } + @{ Statement = 'try { "Hi" && Test-FullyTerminatingError || "Bye" } catch { "Nothing" } finally { "Final" }'; Output = @("Hi", "Nothing", "Final") } + + # Trap continue semantics + @{ Statement = 'trap { continue }; Write-Output 2 && Test-FullyTerminatingError'; Output = @(2) } + @{ Statement = 'trap { continue }; Test-FullyTerminatingError && Write-Output 2'; Output = @() } + @{ Statement = 'trap { continue }; Test-FullyTerminatingError || Write-Output 2'; Output = @(2) } + @{ Statement = 'trap { continue }; 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2'; Output = @(1,3,2) } + @{ Statement = 'trap { continue }; 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4'; Output = @(1,4) } + @{ Statement = 'trap { continue }; 1,2,3 | Test-NonTerminatingError || Write-Output 4'; Output = @(1,3,4); NTErrors = @('NTError') } + @{ Statement = 'trap { continue }; $result = Write-Output 2 && Test-FullyTerminatingError'; Output = @() } + @{ Statement = 'trap { continue }; $result = 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2; $result'; Output = @(1,3,2) } + @{ Statement = 'trap { continue }; $result = 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4; $result'; Output = @(1,4) } + @{ Statement = 'trap { continue }; $result = 1,2,3 | Test-NonTerminatingError || Write-Output 4; $result'; Output = @(1,3,4); NTErrors = @('NTError') } + + # Trap break semantics + @{ Statement = 'trap { break }; 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2'; ThrownError = 'PIPELINE,Test-PipelineTerminatingError' } + @{ Statement = 'trap { break }; 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4'; ThrownError = 'NTERROR,Test-NonTerminatingError' } + @{ Statement = 'trap { break }; 1,2,3 | Test-NonTerminatingError || Write-Output 4'; Output = @(1,3,4); NTErrors = @('NTError') } + @{ Statement = 'trap { break }; $result = Write-Output 2 && Test-FullyTerminatingError'; ThrownError = 'TERMINATE' } + @{ Statement = 'trap { break }; $result = 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2; $result'; ThrownError = 'PIPELINE,Test-PipelineTerminatingError' } + @{ Statement = 'trap { break }; $result = 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4; $result'; ThrownError = 'NTERROR,Test-NonTerminatingError' } + @{ Statement = 'trap { break }; $result = 1,2,3 | Test-NonTerminatingError || Write-Output 4; $result'; Output = @(1,3,4); NTErrors = @('NTError') } + ) + } + BeforeAll { $pwsh = [powershell]::Create() @@ -263,51 +324,6 @@ function Test-FullyTerminatingError } '@).Invoke() - $errorSemanticsCases = @( - # Simple error semantics - @{ Statement = '1,2,3 | Test-NonTerminatingError || Write-Output 4'; Output = @(1, 3, 4); NTErrors = @('NTError') } - @{ Statement = '1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2'; Output = @(1, 3, 2); NTErrors = @('PIPELINE') } - @{ Statement = 'Test-FullyTerminatingError || Write-Output 2'; ThrownError = 'TERMINATE' } - @{ Statement = '1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4'; ThrownError = 'NTERROR,Test-NonTerminatingError' } - - # Assignment error semantics - @{ Statement = '$x = 1,2,3 | Test-NonTerminatingError || Write-Output 4; $x'; Output = @(1, 3, 4); NTErrors = @('NTError') } - @{ Statement = '$x = 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2; $x'; Output = @(1, 3, 2); NTErrors = @('PIPELINE') } - - # Try/catch semantics - @{ Statement = 'try { Write-Output 2 && Test-FullyTerminatingError } catch {}'; Output = @(2) } - @{ Statement = 'try { 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2 } catch {}'; Output = @(1, 3) } - @{ Statement = 'try { 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4 } catch {}'; Output = @(1) } - @{ Statement = 'try { 1,2,3 | Test-NonTerminatingError || Write-Output 4 } catch {}'; Output = @(1, 3, 4); NTErrors = @('NTError') } - @{ Statement = 'try { $result = Write-Output 2 && Test-FullyTerminatingError } catch {}; $result'; Output = @() } - @{ Statement = 'try { $result = 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2 } catch {}; $result'; Output = @() } - @{ Statement = 'try { $result = 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4 } catch {}; $result'; Output = @() } - @{ Statement = 'try { $result = 1,2,3 | Test-NonTerminatingError || Write-Output 4 } catch {}; $result'; Output = @(1, 3, 4); NTErrors = @('NTError') } - @{ Statement = 'try { "Hi" && "Bye" } catch { "Nothing" }'; Output = @("Hi", "Bye") } - @{ Statement = 'try { "Hi" && "Bye" } catch { "Nothing" } finally { "Final" }'; Output = @("Hi", "Bye", "Final") } - @{ Statement = 'try { "Hi" && Test-FullyTerminatingError || "Bye" } catch { "Nothing" } finally { "Final" }'; Output = @("Hi", "Nothing", "Final") } - - # Trap continue semantics - @{ Statement = 'trap { continue }; Write-Output 2 && Test-FullyTerminatingError'; Output = @(2) } - @{ Statement = 'trap { continue }; Test-FullyTerminatingError && Write-Output 2'; Output = @() } - @{ Statement = 'trap { continue }; Test-FullyTerminatingError || Write-Output 2'; Output = @(2) } - @{ Statement = 'trap { continue }; 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2'; Output = @(1,3,2) } - @{ Statement = 'trap { continue }; 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4'; Output = @(1,4) } - @{ Statement = 'trap { continue }; 1,2,3 | Test-NonTerminatingError || Write-Output 4'; Output = @(1,3,4); NTErrors = @('NTError') } - @{ Statement = 'trap { continue }; $result = Write-Output 2 && Test-FullyTerminatingError'; Output = @() } - @{ Statement = 'trap { continue }; $result = 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2; $result'; Output = @(1,3,2) } - @{ Statement = 'trap { continue }; $result = 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4; $result'; Output = @(1,4) } - @{ Statement = 'trap { continue }; $result = 1,2,3 | Test-NonTerminatingError || Write-Output 4; $result'; Output = @(1,3,4); NTErrors = @('NTError') } - - # Trap break semantics - @{ Statement = 'trap { break }; 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2'; ThrownError = 'PIPELINE,Test-PipelineTerminatingError' } - @{ Statement = 'trap { break }; 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4'; ThrownError = 'NTERROR,Test-NonTerminatingError' } - @{ Statement = 'trap { break }; 1,2,3 | Test-NonTerminatingError || Write-Output 4'; Output = @(1,3,4); NTErrors = @('NTError') } - @{ Statement = 'trap { break }; $result = Write-Output 2 && Test-FullyTerminatingError'; ThrownError = 'TERMINATE' } - @{ Statement = 'trap { break }; $result = 1,3,4,5 | Test-PipelineTerminatingError || Write-Output 2; $result'; ThrownError = 'PIPELINE,Test-PipelineTerminatingError' } - @{ Statement = 'trap { break }; $result = 1,2,3 | Test-NonTerminatingError -ErrorAction Stop || Write-Output 4; $result'; ThrownError = 'NTERROR,Test-NonTerminatingError' } - @{ Statement = 'trap { break }; $result = 1,2,3 | Test-NonTerminatingError || Write-Output 4; $result'; Output = @(1,3,4); NTErrors = @('NTError') } - ) } AfterEach { diff --git a/test/powershell/Language/Operators/SplitOperator.Tests.ps1 b/test/powershell/Language/Operators/SplitOperator.Tests.ps1 index 1ebbbff4fa2..3c23623e97d 100644 --- a/test/powershell/Language/Operators/SplitOperator.Tests.ps1 +++ b/test/powershell/Language/Operators/SplitOperator.Tests.ps1 @@ -174,7 +174,7 @@ Describe "Split Operator" -Tags CI { } Context "Binary split operator options" { - BeforeAll { + BeforeDiscovery { # Add '%' in testText2 in order to second line doesn't start with 'b'. $testCases = @( @{ Name = '`n'; testText = "a12a`nb34b`nc56c`nd78d"; testText2 = "a12a`n%b34b`nc56c`nd78d"; newLine = "`n" } diff --git a/test/powershell/Language/Operators/TernaryOperator.Tests.ps1 b/test/powershell/Language/Operators/TernaryOperator.Tests.ps1 index 32928474144..28c2b8f3017 100644 --- a/test/powershell/Language/Operators/TernaryOperator.Tests.ps1 +++ b/test/powershell/Language/Operators/TernaryOperator.Tests.ps1 @@ -2,7 +2,7 @@ # Licensed under the MIT License. Describe "Using of ternary operator" -Tags CI { - BeforeAll { + BeforeDiscovery { $testCases = @( ## Condition: variable and constant expressions @{ Script = { $true ? 1 : 2 }; ExpectedValue = 1 } diff --git a/test/powershell/Language/Parser/BNotOperator.Tests.ps1 b/test/powershell/Language/Parser/BNotOperator.Tests.ps1 index 41930766dc6..bf573e51917 100644 --- a/test/powershell/Language/Parser/BNotOperator.Tests.ps1 +++ b/test/powershell/Language/Parser/BNotOperator.Tests.ps1 @@ -1,72 +1,72 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -$baseTypes = @{ - [SByte] = 'sbyte'; [Byte] = 'byte' - [Int16] = 'short'; [UInt16] = 'ushort' - [Int32] = 'int'; [UInt32] = 'uint' - [Int64] = 'long'; [UInt64] = 'ulong' -} +BeforeDiscovery { + $baseTypes = @{ + [SByte] = 'sbyte'; [Byte] = 'byte' + [Int16] = 'short'; [UInt16] = 'ushort' + [Int32] = 'int'; [UInt32] = 'uint' + [Int64] = 'long'; [UInt64] = 'ulong' + } -$ns = [Guid]::NewGuid() -replace '-','' + $ns = [Guid]::NewGuid() -replace '-','' -$typeDefinition = "namespace ns_$ns`n{" + $typeDefinition = "namespace ns_$ns`n{" -foreach ($baseType in $baseTypes.Keys) -{ - $baseTypeName = $baseTypes[$baseType] - $typeDefinition += @" + foreach ($bt in $baseTypes.Keys) + { + $baseTypeName = $baseTypes[$bt] + $typeDefinition += @" public enum E_$baseTypeName : $baseTypeName { - Min = $($baseType::MinValue), - MinPlus1 = $($baseType::MinValue + 1), - MaxMinus1 = $($baseType::MaxValue - 1), - Max = $($baseType::MaxValue) + Min = $($bt::MinValue), + MinPlus1 = $($bt::MinValue + 1), + MaxMinus1 = $($bt::MaxValue - 1), + Max = $($bt::MaxValue) } "@ -} + } + + $typeDefinition += "`n}" -$typeDefinition += "`n}" + $enumTypeNames = Add-Type $typeDefinition -PassThru -Write-Verbose $typeDefinition -verbose -$enumTypeNames = Add-Type $typeDefinition -Pass + $enumTypesForEach = [type[]]$enumTypeNames | ForEach-Object { @{ enumType = $_ } } + $baseTypesForEach = $baseTypes.Keys | ForEach-Object { @{ baseType = $_ } } +} Describe "bnot on enums" -Tags "CI" { - foreach ($enumType in [type[]]$enumTypeNames) - { - Context $enumType.Name { - It "max - 1" { - $res = -bnot $enumType::MaxMinus1 - $res | Should -Be $enumType::MinPlus1 - $res | Should -BeOfType $enumType - } + Context "" -ForEach $enumTypesForEach { + It "max - 1" { + $res = -bnot $enumType::MaxMinus1 + $res | Should -Be $enumType::MinPlus1 + $res | Should -BeOfType $enumType + } - It "min + 1" { - $res = -bnot $enumType::MinPlus1 - $res | Should -Be $enumType::MaxMinus1 - $res | Should -BeOfType $enumType - } + It "min + 1" { + $res = -bnot $enumType::MinPlus1 + $res | Should -Be $enumType::MaxMinus1 + $res | Should -BeOfType $enumType + } - It "Max" { - $res = -bnot $enumType::Max - $res | Should -Be $enumType::Min - $res | Should -BeOfType $enumType - } + It "Max" { + $res = -bnot $enumType::Max + $res | Should -Be $enumType::Min + $res | Should -BeOfType $enumType + } - It "Min" { - $res = -bnot $enumType::Min - $res | Should -Be $enumType::Max - $res | Should -BeOfType $enumType - } + It "Min" { + $res = -bnot $enumType::Min + $res | Should -Be $enumType::Max + $res | Should -BeOfType $enumType } } } Describe "bnot on integral types" -Tags "CI" { - foreach ($baseType in $baseTypes.Keys) - { - Context $baseType.Name { + Context "" -ForEach $baseTypesForEach { + BeforeAll { $max = $baseType::MaxValue $maxMinus1 = $max - 1 $min = $baseType::MinValue @@ -81,61 +81,47 @@ Describe "bnot on integral types" -Tags "CI" { $expectedResultType = $baseType } - if ($baseType -eq [byte] -or $baseType -eq [uint16]) - { - # Because of type promotion rules, unsigned types smaller than int - # don't quite follow the pattern of "flip all the bits", so our - # tests are a little different. - It "max - 1" { - $res = -bnot $maxMinus1 - $res | Should -Be (-bnot [int]$maxMinus1) - $res | Should -BeOfType $expectedResultType - } - - It "min + 1" { - $res = -bnot $minPlus1 - $res | Should -Be (-bnot [int]$minPlus1) - $res | Should -BeOfType $expectedResultType - } - - It "max" { - $res = -bnot $max - $res | Should -Be (-bnot [int]$max) - $res | Should -BeOfType $expectedResultType - } - - It "min" { - $res = -bnot $min - $res | Should -Be (-bnot [int]$min) - $res | Should -BeOfType $expectedResultType - } - return - } + $isUnsignedSmall = ($baseType -eq [byte] -or $baseType -eq [uint16]) + } - It "max - 1" { - $res = -bnot $maxMinus1 + It "max - 1" { + $res = -bnot $maxMinus1 + if ($isUnsignedSmall) { + $res | Should -Be (-bnot [int]$maxMinus1) + } else { $res | Should -Be $minPlus1 - $res | Should -BeOfType $expectedResultType } + $res | Should -BeOfType $expectedResultType + } - It "min + 1" { - $res = -bnot $minPlus1 + It "min + 1" { + $res = -bnot $minPlus1 + if ($isUnsignedSmall) { + $res | Should -Be (-bnot [int]$minPlus1) + } else { $res | Should -Be $maxMinus1 - $res | Should -BeOfType $expectedResultType } + $res | Should -BeOfType $expectedResultType + } - It "max" { - $res = -bnot $max + It "max" { + $res = -bnot $max + if ($isUnsignedSmall) { + $res | Should -Be (-bnot [int]$max) + } else { $res | Should -Be $min - $res | Should -BeOfType $expectedResultType } + $res | Should -BeOfType $expectedResultType + } - It "min" { - $res = -bnot $min + It "min" { + $res = -bnot $min + if ($isUnsignedSmall) { + $res | Should -Be (-bnot [int]$min) + } else { $res | Should -Be $max - $res | Should -BeOfType $expectedResultType } + $res | Should -BeOfType $expectedResultType } } } - diff --git a/test/powershell/Language/Parser/Conversions.Tests.ps1 b/test/powershell/Language/Parser/Conversions.Tests.ps1 index f6874b1db93..948875dba0e 100644 --- a/test/powershell/Language/Parser/Conversions.Tests.ps1 +++ b/test/powershell/Language/Parser/Conversions.Tests.ps1 @@ -25,7 +25,7 @@ Describe 'conversion syntax' -Tags "CI" { } Context "Cast object[] to more narrow generic collection" { - BeforeAll { + BeforeDiscovery { $testCases1 = @( ## It's intentional to have 'Command' to be `{$result = ...}` and run it with `. $Command`. ## This is because `$result = & {[List[int]]@(1,2)}` will cause the resulted List to be unraveled, @@ -142,313 +142,316 @@ namespace TestTypeResolution { } } -Describe 'method conversion' -Tags 'CI' { - class M { - [int] Twice([int] $value) { return 2 * $value } - [int] ThriceInstance([int] $value) { return 3 * $value } - static [int] Thrice([int] $value) { return 3 * $value } - static [int] Add([int] $i, [int16] $j) { return $i + $j } - - static [int] Apply([int] $value, [Func[int, int]] $function) { - return $function.Invoke($value) - } - - static [int] Apply([int] $v1, [Int16] $v2, [Func[int, int16, int]] $function) { - return $function.Invoke($v1, $v2) - } +class M_Conversion { + [int] Twice([int] $value) { return 2 * $value } + [int] ThriceInstance([int] $value) { return 3 * $value } + static [int] Thrice([int] $value) { return 3 * $value } + static [int] Add([int] $i, [int16] $j) { return $i + $j } + + static [int] Apply([int] $value, [Func[int, int]] $function) { + return $function.Invoke($value) + } + + static [int] Apply([int] $v1, [Int16] $v2, [Func[int, int16, int]] $function) { + return $function.Invoke($v1, $v2) + } + + # check that we can handle at least 72 overloads + static [char] Foo([char] $i) { return $i } + static [char] Foo([char] $i, [char] $j) { return $i } + static [char] Foo([char] $i, [char] $j, [char] $k) { return $i } + static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l) { return $i } + static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m) { return $i } + static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n) { return $i } + static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n, [char] $o) { return $i } + static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n, [char] $o, [char] $p) { return $i } + static [int16] Foo([int16] $i) { return $i } + static [int16] Foo([int16] $i, [int16] $j) { return $i } + static [int16] Foo([int16] $i, [int16] $j, [int16] $k) { return $i } + static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l) { return $i } + static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m) { return $i } + static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n) { return $i } + static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n, [int16] $o) { return $i } + static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n, [int16] $o, [int16] $p) { return $i } + static [int] Foo([int] $i) { return $i } + static [int] Foo([int] $i, [int] $j) { return $i } + static [int] Foo([int] $i, [int] $j, [int] $k) { return $i } + static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l) { return $i } + static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m) { return $i } + static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n) { return $i } + static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n, [int] $o) { return $i } + static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n, [int] $o, [int] $p) { return $i } + static [UInt32] Foo([UInt32] $i) { return $i } + static [UInt32] Foo([UInt32] $i, [UInt32] $j) { return $i } + static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k) { return $i } + static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l) { return $i } + static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m) { return $i } + static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n) { return $i } + static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n, [UInt32] $o) { return $i } + static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n, [UInt32] $o, [UInt32] $p) { return $i } + static [UInt64] Foo([UInt64] $i) { return $i } + static [UInt64] Foo([UInt64] $i, [UInt64] $j) { return $i } + static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k) { return $i } + static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l) { return $i } + static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m) { return $i } + static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n) { return $i } + static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n, [UInt64] $o) { return $i } + static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n, [UInt64] $o, [UInt64] $p) { return $i } + static [float] Foo([float] $i) { return $i } + static [float] Foo([float] $i, [float] $j) { return $i } + static [float] Foo([float] $i, [float] $j, [float] $k) { return $i } + static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l) { return $i } + static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m) { return $i } + static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n) { return $i } + static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n, [float] $o) { return $i } + static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n, [float] $o, [float] $p) { return $i } + static [double] Foo([double] $i) { return $i } + static [double] Foo([double] $i, [double] $j) { return $i } + static [double] Foo([double] $i, [double] $j, [double] $k) { return $i } + static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l) { return $i } + static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m) { return $i } + static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n) { return $i } + static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n, [double] $o) { return $i } + static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n, [double] $o, [double] $p) { return $i } + static [IntPtr] Foo([IntPtr] $i) { return $i } + static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j) { return $i } + static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k) { return $i } + static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l) { return $i } + static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m) { return $i } + static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n) { return $i } + static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n, [IntPtr] $o) { return $i } + static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n, [IntPtr] $o, [IntPtr] $p) { return $i } + static [timespan] Foo([timespan] $i) { return $i } + static [timespan] Foo([timespan] $i, [timespan] $j) { return $i } + static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k) { return $i } + static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l) { return $i } + static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m) { return $i } + static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n) { return $i } + static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n, [timespan] $o) { return $i } + static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n, [timespan] $o, [timespan] $p) { return $i } +} - # check that we can handle at least 72 overloads - static [char] Foo([char] $i) { return $i } - static [char] Foo([char] $i, [char] $j) { return $i } - static [char] Foo([char] $i, [char] $j, [char] $k) { return $i } - static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l) { return $i } - static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m) { return $i } - static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n) { return $i } - static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n, [char] $o) { return $i } - static [char] Foo([char] $i, [char] $j, [char] $k, [char] $l, [char] $m, [char] $n, [char] $o, [char] $p) { return $i } - static [int16] Foo([int16] $i) { return $i } - static [int16] Foo([int16] $i, [int16] $j) { return $i } - static [int16] Foo([int16] $i, [int16] $j, [int16] $k) { return $i } - static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l) { return $i } - static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m) { return $i } - static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n) { return $i } - static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n, [int16] $o) { return $i } - static [int16] Foo([int16] $i, [int16] $j, [int16] $k, [int16] $l, [int16] $m, [int16] $n, [int16] $o, [int16] $p) { return $i } - static [int] Foo([int] $i) { return $i } - static [int] Foo([int] $i, [int] $j) { return $i } - static [int] Foo([int] $i, [int] $j, [int] $k) { return $i } - static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l) { return $i } - static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m) { return $i } - static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n) { return $i } - static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n, [int] $o) { return $i } - static [int] Foo([int] $i, [int] $j, [int] $k, [int] $l, [int] $m, [int] $n, [int] $o, [int] $p) { return $i } - static [UInt32] Foo([UInt32] $i) { return $i } - static [UInt32] Foo([UInt32] $i, [UInt32] $j) { return $i } - static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k) { return $i } - static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l) { return $i } - static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m) { return $i } - static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n) { return $i } - static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n, [UInt32] $o) { return $i } - static [UInt32] Foo([UInt32] $i, [UInt32] $j, [UInt32] $k, [UInt32] $l, [UInt32] $m, [UInt32] $n, [UInt32] $o, [UInt32] $p) { return $i } - static [UInt64] Foo([UInt64] $i) { return $i } - static [UInt64] Foo([UInt64] $i, [UInt64] $j) { return $i } - static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k) { return $i } - static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l) { return $i } - static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m) { return $i } - static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n) { return $i } - static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n, [UInt64] $o) { return $i } - static [UInt64] Foo([UInt64] $i, [UInt64] $j, [UInt64] $k, [UInt64] $l, [UInt64] $m, [UInt64] $n, [UInt64] $o, [UInt64] $p) { return $i } - static [float] Foo([float] $i) { return $i } - static [float] Foo([float] $i, [float] $j) { return $i } - static [float] Foo([float] $i, [float] $j, [float] $k) { return $i } - static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l) { return $i } - static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m) { return $i } - static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n) { return $i } - static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n, [float] $o) { return $i } - static [float] Foo([float] $i, [float] $j, [float] $k, [float] $l, [float] $m, [float] $n, [float] $o, [float] $p) { return $i } - static [double] Foo([double] $i) { return $i } - static [double] Foo([double] $i, [double] $j) { return $i } - static [double] Foo([double] $i, [double] $j, [double] $k) { return $i } - static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l) { return $i } - static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m) { return $i } - static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n) { return $i } - static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n, [double] $o) { return $i } - static [double] Foo([double] $i, [double] $j, [double] $k, [double] $l, [double] $m, [double] $n, [double] $o, [double] $p) { return $i } - static [IntPtr] Foo([IntPtr] $i) { return $i } - static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j) { return $i } - static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k) { return $i } - static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l) { return $i } - static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m) { return $i } - static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n) { return $i } - static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n, [IntPtr] $o) { return $i } - static [IntPtr] Foo([IntPtr] $i, [IntPtr] $j, [IntPtr] $k, [IntPtr] $l, [IntPtr] $m, [IntPtr] $n, [IntPtr] $o, [IntPtr] $p) { return $i } - static [timespan] Foo([timespan] $i) { return $i } - static [timespan] Foo([timespan] $i, [timespan] $j) { return $i } - static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k) { return $i } - static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l) { return $i } - static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m) { return $i } - static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n) { return $i } - static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n, [timespan] $o) { return $i } - static [timespan] Foo([timespan] $i, [timespan] $j, [timespan] $k, [timespan] $l, [timespan] $m, [timespan] $n, [timespan] $o, [timespan] $p) { return $i } - } +Describe 'method conversion' -Tags 'CI' { It 'converts static method as Func does not throw' { - { [Func[int, int]] [M]::Thrice } | Should -Not -Throw + { [Func[int, int]] [M_Conversion]::Thrice } | Should -Not -Throw } It 'converts static method as Func is non null' { - ([Func[int, int]] [M]::Thrice) | Should -Not -BeNullOrEmpty + ([Func[int, int]] [M_Conversion]::Thrice) | Should -Not -BeNullOrEmpty } It 'calls static method as Func' { - $f = [Func[int, int]] [M]::Thrice - [M]::Apply(1, $f) | Should -Be 3 + $f = [Func[int, int]] [M_Conversion]::Thrice + [M_Conversion]::Apply(1, $f) | Should -Be 3 } It 'calls static method as Func' { - $f = [Func[int, int16, int]] [M]::Add - [M]::Apply(3, 4, $f) | Should -Be 7 + $f = [Func[int, int16, int]] [M_Conversion]::Add + [M_Conversion]::Apply(3, 4, $f) | Should -Be 7 } It 'calls static method as Func no cast' { - [M]::Apply(3, 4, [M]::Add) | Should -Be 7 + [M_Conversion]::Apply(3, 4, [M_Conversion]::Add) | Should -Be 7 } It 'converts instance psmethodinfo to Func' { - $m = [M]::new() + $m = [M_Conversion]::new() { [Func[int, int]] $m.Twice } | Should -Not -Throw - $f = [Func[int, int16, int]] [M]::Add + $f = [Func[int, int16, int]] [M_Conversion]::Add $f.Invoke(2, 6) | Should -Be 8 } It "can call all overloads of M::Foo" { - [Func[char, char]] $f1 = [M]::Foo + [Func[char, char]] $f1 = [M_Conversion]::Foo $f1.Invoke(10) | Should -Be 10 - [Func[char, char, char]] $f2 = [M]::Foo + [Func[char, char, char]] $f2 = [M_Conversion]::Foo $f2.Invoke(10, 1) | Should -Be 10 - [Func[char, char, char, char]] $f3 = [M]::Foo + [Func[char, char, char, char]] $f3 = [M_Conversion]::Foo $f3.Invoke(10, 1, 2) | Should -Be 10 - [Func[char, char, char, char, char]] $f4 = [M]::Foo + [Func[char, char, char, char, char]] $f4 = [M_Conversion]::Foo $f4.Invoke(10, 1, 2, 3) | Should -Be 10 - [Func[char, char, char, char, char, char]] $f5 = [M]::Foo + [Func[char, char, char, char, char, char]] $f5 = [M_Conversion]::Foo $f5.Invoke(10, 1, 2, 3, 4) | Should -Be 10 - [Func[char, char, char, char, char, char, char]] $f6 = [M]::Foo + [Func[char, char, char, char, char, char, char]] $f6 = [M_Conversion]::Foo $f6.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10 - [Func[char, char, char, char, char, char, char, char]] $f7 = [M]::Foo + [Func[char, char, char, char, char, char, char, char]] $f7 = [M_Conversion]::Foo $f7.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10 - [Func[char, char, char, char, char, char, char, char, char]] $f8 = [M]::Foo + [Func[char, char, char, char, char, char, char, char, char]] $f8 = [M_Conversion]::Foo $f8.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10 - [Func[int16, int16]] $f9 = [M]::Foo + [Func[int16, int16]] $f9 = [M_Conversion]::Foo $f9.Invoke(10) | Should -Be 10 - [Func[int16, int16, int16]] $f10 = [M]::Foo + [Func[int16, int16, int16]] $f10 = [M_Conversion]::Foo $f10.Invoke(10, 1) | Should -Be 10 - [Func[int16, int16, int16, int16]] $f11 = [M]::Foo + [Func[int16, int16, int16, int16]] $f11 = [M_Conversion]::Foo $f11.Invoke(10, 1, 2) | Should -Be 10 - [Func[int16, int16, int16, int16, int16]] $f12 = [M]::Foo + [Func[int16, int16, int16, int16, int16]] $f12 = [M_Conversion]::Foo $f12.Invoke(10, 1, 2, 3) | Should -Be 10 - [Func[int16, int16, int16, int16, int16, int16]] $f13 = [M]::Foo + [Func[int16, int16, int16, int16, int16, int16]] $f13 = [M_Conversion]::Foo $f13.Invoke(10, 1, 2, 3, 4) | Should -Be 10 - [Func[int16, int16, int16, int16, int16, int16, int16]] $f14 = [M]::Foo + [Func[int16, int16, int16, int16, int16, int16, int16]] $f14 = [M_Conversion]::Foo $f14.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10 - [Func[int16, int16, int16, int16, int16, int16, int16, int16]] $f15 = [M]::Foo + [Func[int16, int16, int16, int16, int16, int16, int16, int16]] $f15 = [M_Conversion]::Foo $f15.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10 - [Func[int16, int16, int16, int16, int16, int16, int16, int16, int16]] $f16 = [M]::Foo + [Func[int16, int16, int16, int16, int16, int16, int16, int16, int16]] $f16 = [M_Conversion]::Foo $f16.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10 - [Func[int, int]] $f17 = [M]::Foo + [Func[int, int]] $f17 = [M_Conversion]::Foo $f17.Invoke(10) | Should -Be 10 - [Func[int, int, int]] $f18 = [M]::Foo + [Func[int, int, int]] $f18 = [M_Conversion]::Foo $f18.Invoke(10, 1) | Should -Be 10 - [Func[int, int, int, int]] $f19 = [M]::Foo + [Func[int, int, int, int]] $f19 = [M_Conversion]::Foo $f19.Invoke(10, 1, 2) | Should -Be 10 - [Func[int, int, int, int, int]] $f20 = [M]::Foo + [Func[int, int, int, int, int]] $f20 = [M_Conversion]::Foo $f20.Invoke(10, 1, 2, 3) | Should -Be 10 - [Func[int, int, int, int, int, int]] $f21 = [M]::Foo + [Func[int, int, int, int, int, int]] $f21 = [M_Conversion]::Foo $f21.Invoke(10, 1, 2, 3, 4) | Should -Be 10 - [Func[int, int, int, int, int, int, int]] $f22 = [M]::Foo + [Func[int, int, int, int, int, int, int]] $f22 = [M_Conversion]::Foo $f22.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10 - [Func[int, int, int, int, int, int, int, int]] $f23 = [M]::Foo + [Func[int, int, int, int, int, int, int, int]] $f23 = [M_Conversion]::Foo $f23.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10 - [Func[int, int, int, int, int, int, int, int, int]] $f24 = [M]::Foo + [Func[int, int, int, int, int, int, int, int, int]] $f24 = [M_Conversion]::Foo $f24.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10 - [Func[UInt32, UInt32]] $f25 = [M]::Foo + [Func[UInt32, UInt32]] $f25 = [M_Conversion]::Foo $f25.Invoke(10) | Should -Be 10 - [Func[UInt32, UInt32, UInt32]] $f26 = [M]::Foo + [Func[UInt32, UInt32, UInt32]] $f26 = [M_Conversion]::Foo $f26.Invoke(10, 1) | Should -Be 10 - [Func[UInt32, UInt32, UInt32, UInt32]] $f27 = [M]::Foo + [Func[UInt32, UInt32, UInt32, UInt32]] $f27 = [M_Conversion]::Foo $f27.Invoke(10, 1, 2) | Should -Be 10 - [Func[UInt32, UInt32, UInt32, UInt32, UInt32]] $f28 = [M]::Foo + [Func[UInt32, UInt32, UInt32, UInt32, UInt32]] $f28 = [M_Conversion]::Foo $f28.Invoke(10, 1, 2, 3) | Should -Be 10 - [Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f29 = [M]::Foo + [Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f29 = [M_Conversion]::Foo $f29.Invoke(10, 1, 2, 3, 4) | Should -Be 10 - [Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f30 = [M]::Foo + [Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f30 = [M_Conversion]::Foo $f30.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10 - [Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f31 = [M]::Foo + [Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f31 = [M_Conversion]::Foo $f31.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10 - [Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f32 = [M]::Foo + [Func[UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32]] $f32 = [M_Conversion]::Foo $f32.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10 - [Func[UInt64, UInt64]] $f33 = [M]::Foo + [Func[UInt64, UInt64]] $f33 = [M_Conversion]::Foo $f33.Invoke(10) | Should -Be 10 - [Func[UInt64, UInt64, UInt64]] $f34 = [M]::Foo + [Func[UInt64, UInt64, UInt64]] $f34 = [M_Conversion]::Foo $f34.Invoke(10, 1) | Should -Be 10 - [Func[UInt64, UInt64, UInt64, UInt64]] $f35 = [M]::Foo + [Func[UInt64, UInt64, UInt64, UInt64]] $f35 = [M_Conversion]::Foo $f35.Invoke(10, 1, 2) | Should -Be 10 - [Func[UInt64, UInt64, UInt64, UInt64, UInt64]] $f36 = [M]::Foo + [Func[UInt64, UInt64, UInt64, UInt64, UInt64]] $f36 = [M_Conversion]::Foo $f36.Invoke(10, 1, 2, 3) | Should -Be 10 - [Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f37 = [M]::Foo + [Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f37 = [M_Conversion]::Foo $f37.Invoke(10, 1, 2, 3, 4) | Should -Be 10 - [Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f38 = [M]::Foo + [Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f38 = [M_Conversion]::Foo $f38.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10 - [Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f39 = [M]::Foo + [Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f39 = [M_Conversion]::Foo $f39.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10 - [Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f40 = [M]::Foo + [Func[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]] $f40 = [M_Conversion]::Foo $f40.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10 - [Func[float, float]] $f41 = [M]::Foo + [Func[float, float]] $f41 = [M_Conversion]::Foo $f41.Invoke(10) | Should -Be 10 - [Func[float, float, float]] $f42 = [M]::Foo + [Func[float, float, float]] $f42 = [M_Conversion]::Foo $f42.Invoke(10, 1) | Should -Be 10 - [Func[float, float, float, float]] $f43 = [M]::Foo + [Func[float, float, float, float]] $f43 = [M_Conversion]::Foo $f43.Invoke(10, 1, 2) | Should -Be 10 - [Func[float, float, float, float, float]] $f44 = [M]::Foo + [Func[float, float, float, float, float]] $f44 = [M_Conversion]::Foo $f44.Invoke(10, 1, 2, 3) | Should -Be 10 - [Func[float, float, float, float, float, float]] $f45 = [M]::Foo + [Func[float, float, float, float, float, float]] $f45 = [M_Conversion]::Foo $f45.Invoke(10, 1, 2, 3, 4) | Should -Be 10 - [Func[float, float, float, float, float, float, float]] $f46 = [M]::Foo + [Func[float, float, float, float, float, float, float]] $f46 = [M_Conversion]::Foo $f46.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10 - [Func[float, float, float, float, float, float, float, float]] $f47 = [M]::Foo + [Func[float, float, float, float, float, float, float, float]] $f47 = [M_Conversion]::Foo $f47.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10 - [Func[float, float, float, float, float, float, float, float, float]] $f48 = [M]::Foo + [Func[float, float, float, float, float, float, float, float, float]] $f48 = [M_Conversion]::Foo $f48.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10 - [Func[double, double]] $f49 = [M]::Foo + [Func[double, double]] $f49 = [M_Conversion]::Foo $f49.Invoke(10) | Should -Be 10 - [Func[double, double, double]] $f50 = [M]::Foo + [Func[double, double, double]] $f50 = [M_Conversion]::Foo $f50.Invoke(10, 1) | Should -Be 10 - [Func[double, double, double, double]] $f51 = [M]::Foo + [Func[double, double, double, double]] $f51 = [M_Conversion]::Foo $f51.Invoke(10, 1, 2) | Should -Be 10 - [Func[double, double, double, double, double]] $f52 = [M]::Foo + [Func[double, double, double, double, double]] $f52 = [M_Conversion]::Foo $f52.Invoke(10, 1, 2, 3) | Should -Be 10 - [Func[double, double, double, double, double, double]] $f53 = [M]::Foo + [Func[double, double, double, double, double, double]] $f53 = [M_Conversion]::Foo $f53.Invoke(10, 1, 2, 3, 4) | Should -Be 10 - [Func[double, double, double, double, double, double, double]] $f54 = [M]::Foo + [Func[double, double, double, double, double, double, double]] $f54 = [M_Conversion]::Foo $f54.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10 - [Func[double, double, double, double, double, double, double, double]] $f55 = [M]::Foo + [Func[double, double, double, double, double, double, double, double]] $f55 = [M_Conversion]::Foo $f55.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10 - [Func[double, double, double, double, double, double, double, double, double]] $f56 = [M]::Foo + [Func[double, double, double, double, double, double, double, double, double]] $f56 = [M_Conversion]::Foo $f56.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10 - [Func[IntPtr, IntPtr]] $f57 = [M]::Foo + [Func[IntPtr, IntPtr]] $f57 = [M_Conversion]::Foo $f57.Invoke(10) | Should -Be 10 - [Func[IntPtr, IntPtr, IntPtr]] $f58 = [M]::Foo + [Func[IntPtr, IntPtr, IntPtr]] $f58 = [M_Conversion]::Foo $f58.Invoke(10, 1) | Should -Be 10 - [Func[IntPtr, IntPtr, IntPtr, IntPtr]] $f59 = [M]::Foo + [Func[IntPtr, IntPtr, IntPtr, IntPtr]] $f59 = [M_Conversion]::Foo $f59.Invoke(10, 1, 2) | Should -Be 10 - [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f60 = [M]::Foo + [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f60 = [M_Conversion]::Foo $f60.Invoke(10, 1, 2, 3) | Should -Be 10 - [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f61 = [M]::Foo + [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f61 = [M_Conversion]::Foo $f61.Invoke(10, 1, 2, 3, 4) | Should -Be 10 - [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f62 = [M]::Foo + [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f62 = [M_Conversion]::Foo $f62.Invoke(10, 1, 2, 3, 4, 5) | Should -Be 10 - [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f63 = [M]::Foo + [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f63 = [M_Conversion]::Foo $f63.Invoke(10, 1, 2, 3, 4, 5, 6) | Should -Be 10 - [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f64 = [M]::Foo + [Func[IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr]] $f64 = [M_Conversion]::Foo $f64.Invoke(10, 1, 2, 3, 4, 5, 6, 7) | Should -Be 10 $timespan = [timespan]::FromMinutes(62) - [Func[timespan, timespan]] $f65 = [M]::Foo + [Func[timespan, timespan]] $f65 = [M_Conversion]::Foo $f65.Invoke($timeSpan) | Should -Be $timeSpan - [Func[timespan, timespan, timespan]] $f66 = [M]::Foo + [Func[timespan, timespan, timespan]] $f66 = [M_Conversion]::Foo $f66.Invoke($timeSpan, [Timespan]::Zero) | Should -Be $timeSpan - [Func[timespan, timespan, timespan, timespan]] $f67 = [M]::Foo + [Func[timespan, timespan, timespan, timespan]] $f67 = [M_Conversion]::Foo $f67.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan - [Func[timespan, timespan, timespan, timespan, timespan]] $f68 = [M]::Foo + [Func[timespan, timespan, timespan, timespan, timespan]] $f68 = [M_Conversion]::Foo $f68.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan - [Func[timespan, timespan, timespan, timespan, timespan, timespan]] $f69 = [M]::Foo + [Func[timespan, timespan, timespan, timespan, timespan, timespan]] $f69 = [M_Conversion]::Foo $f69.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan - [Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f70 = [M]::Foo + [Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f70 = [M_Conversion]::Foo $f70.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan - [Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f71 = [M]::Foo + [Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f71 = [M_Conversion]::Foo $f71.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan - [Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f72 = [M]::Foo + [Func[timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan, timespan]] $f72 = [M_Conversion]::Foo $f72.Invoke($timeSpan, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero, [Timespan]::Zero) | Should -Be $timeSpan } - enum E { - Day; - Week; - Year; - } + BeforeAll { + enum E { + Day; + Week; + Year; + } - class N { - ## Attempt to convert methods to Func. + class N { + ## Attempt to convert methods to Func. - ## Different methods with same overload signatures. - ## The second and third overloads match the target delegate with variance. - [string] GetA([int] $i, [string] $s) { return "GetA-int-string-string" } - [string] GetA([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetA-filesysteminfo-object-string" } - [string] GetA([System.IO.FileInfo] $finfo, [object] $o) { return "GetA-fileinfo-object-string" } + ## Different methods with same overload signatures. + ## The second and third overloads match the target delegate with variance. + [string] GetA([int] $i, [string] $s) { return "GetA-int-string-string" } + [string] GetA([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetA-filesysteminfo-object-string" } + [string] GetA([System.IO.FileInfo] $finfo, [object] $o) { return "GetA-fileinfo-object-string" } - [string] GetAPrime([int] $i, [string] $s) { return "GetAPrime-int-string-string" } - [string] GetAPrime([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetAPrime-filesysteminfo-object-string" } - [string] GetAPrime([System.IO.FileInfo] $finfo, [object] $o) { return "GetAPrime-fileinfo-object-string" } + [string] GetAPrime([int] $i, [string] $s) { return "GetAPrime-int-string-string" } + [string] GetAPrime([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetAPrime-filesysteminfo-object-string" } + [string] GetAPrime([System.IO.FileInfo] $finfo, [object] $o) { return "GetAPrime-fileinfo-object-string" } - static [string] GetAStatic([int] $i, [string] $s) { return "GetAStatic-int-string-string" } - static [string] GetAStatic([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetAStatic-filesysteminfo-object-string" } - static [string] GetAStatic([System.IO.FileInfo] $finfo, [object] $o) { return "GetAStatic-fileinfo-object-string" } + static [string] GetAStatic([int] $i, [string] $s) { return "GetAStatic-int-string-string" } + static [string] GetAStatic([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetAStatic-filesysteminfo-object-string" } + static [string] GetAStatic([System.IO.FileInfo] $finfo, [object] $o) { return "GetAStatic-fileinfo-object-string" } - ## Different methods with same overload signatures. - ## The first overload matches the target delegate with variance, - ## while the second overload matches the target delegate exactly. - [string] GetB([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetB-filesysteminfo-object-string" } - [object] GetB([System.IO.FileInfo] $finfo, [string] $s) { return "GetB-fileinfo-string-object" } - [string] GetB([datetime] $d) { return "GetB-datetime-string" } + ## Different methods with same overload signatures. + ## The first overload matches the target delegate with variance, + ## while the second overload matches the target delegate exactly. + [string] GetB([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetB-filesysteminfo-object-string" } + [object] GetB([System.IO.FileInfo] $finfo, [string] $s) { return "GetB-fileinfo-string-object" } + [string] GetB([datetime] $d) { return "GetB-datetime-string" } - [string] GetBPrime([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetBPrime-filesysteminfo-object-string" } - [object] GetBPrime([System.IO.FileInfo] $finfo, [string] $s) { return "GetBPrime-fileinfo-string-object" } - [string] GetBPrime([datetime] $d) { return "GetBPrime-datetime-string" } + [string] GetBPrime([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetBPrime-filesysteminfo-object-string" } + [object] GetBPrime([System.IO.FileInfo] $finfo, [string] $s) { return "GetBPrime-fileinfo-string-object" } + [string] GetBPrime([datetime] $d) { return "GetBPrime-datetime-string" } - static [string] GetBStatic([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetBStatic-filesysteminfo-object-string" } - static [object] GetBStatic([System.IO.FileInfo] $finfo, [string] $s) { return "GetBStatic-fileinfo-string-object" } - static [string] GetBStatic([datetime] $d) { return "GetBStatic-datetime-string" } + static [string] GetBStatic([System.IO.FileSystemInfo] $fsinfo, [object] $o) { return "GetBStatic-filesysteminfo-object-string" } + static [object] GetBStatic([System.IO.FileInfo] $finfo, [string] $s) { return "GetBStatic-fileinfo-string-object" } + static [string] GetBStatic([datetime] $d) { return "GetBStatic-datetime-string" } - ## Test enum parameter type - [object] GetC([E] $e) { return $e.ToString() } + ## Test enum parameter type + [object] GetC([E] $e) { return $e.ToString() } + } } It "Different method overloads with same signatures/orders should have same PSMethod type" { @@ -499,18 +502,20 @@ Describe 'method conversion' -Tags 'CI' { { [System.Management.Automation.LanguagePrimitives]::ConvertTo($n.GetC, [Func[[int], [object]]]) } | Should -Throw -ErrorId "PSInvalidCastException" } - $TestCases = @( - @{ Number = "100y"; Value = "100"; Type = [int] } - @{ Number = "100uy"; Value = "100"; Type = [double] } - @{ Number = "1200u"; Value = "1200"; Type = [short] } - @{ Number = "1200L"; Value = "1200"; Type = [int] } - @{ Number = "127ul"; Value = "127"; Type = [ulong] } - @{ Number = "127d"; Value = "127"; Type = [byte] } - @{ Number = "127s"; Value = "127"; Type = [sbyte] } - @{ Number = "127y"; Value = "127"; Type = [uint] } - @{ Number = "100n"; Value = "100"; Type = [int] } - @{ Number = "1234s"; Value = "1234"; Type = [bigint] } - ) + BeforeDiscovery { + $TestCases = @( + @{ Number = "100y"; Value = "100"; Type = [int] } + @{ Number = "100uy"; Value = "100"; Type = [double] } + @{ Number = "1200u"; Value = "1200"; Type = [short] } + @{ Number = "1200L"; Value = "1200"; Type = [int] } + @{ Number = "127ul"; Value = "127"; Type = [ulong] } + @{ Number = "127d"; Value = "127"; Type = [byte] } + @{ Number = "127s"; Value = "127"; Type = [sbyte] } + @{ Number = "127y"; Value = "127"; Type = [uint] } + @{ Number = "100n"; Value = "100"; Type = [int] } + @{ Number = "1234s"; Value = "1234"; Type = [bigint] } + ) + } It "Correctly casts to value as type " -TestCases $TestCases { param($Number, $Value, $Type) @@ -519,12 +524,14 @@ Describe 'method conversion' -Tags 'CI' { $Result | Should -BeOfType $Type } - $TestCases = @( - @{ Number = "200y" } - @{ Number = "300uy" } - @{ Number = "70000us" } - @{ Number = "40000s" } - ) + BeforeDiscovery { + $TestCases = @( + @{ Number = "200y" } + @{ Number = "300uy" } + @{ Number = "70000us" } + @{ Number = "40000s" } + ) + } It "Fails to cast invalid PowerShell-Style suffixed numeral " -TestCases $TestCases { param($Number) @@ -560,7 +567,7 @@ Describe 'float/double precision when converting to string' -Tags "CI" { Describe 'Casting Behaviour of Boolean/Null to Numeral' -Tags CI { - BeforeAll { + BeforeDiscovery { $NullToNumeral = @( @{ Type = [sbyte]; ExpectedResult = 0y } @{ Type = [byte]; ExpectedResult = 0uy } @@ -575,7 +582,6 @@ Describe 'Casting Behaviour of Boolean/Null to Numeral' -Tags CI { @{ Type = [double]; ExpectedResult = 0.0 } @{ Type = [bigint]; ExpectedResult = 0n } ) - $BoolToNumeral = @( @{ Type = [sbyte]; Value = $true; ExpectedResult = 1y } @{ Type = [sbyte]; Value = $false; ExpectedResult = 0y } diff --git a/test/powershell/Language/Parser/ExtensibleCompletion.Tests.ps1 b/test/powershell/Language/Parser/ExtensibleCompletion.Tests.ps1 index 3514f389718..15cb38dd67d 100644 --- a/test/powershell/Language/Parser/ExtensibleCompletion.Tests.ps1 +++ b/test/powershell/Language/Parser/ExtensibleCompletion.Tests.ps1 @@ -8,232 +8,239 @@ using namespace System.Management.Automation.Language using namespace System.Collections using namespace System.Collections.Generic -#region Testcase infrastructure +BeforeDiscovery { + #region Testcase infrastructure - Discovery -class CompletionTestResult -{ - [string]$CompletionText - [string]$ListItemText - [CompletionResultType]$ResultType - [string]$ToolTip - [bool]$Found - - [bool] Equals($Other) + class CompletionTestResult { - if ($Other -isnot [CompletionTestResult] -and - $Other -isnot [CompletionResult]) - { - return $false - } + [string]$CompletionText + [string]$ListItemText + [CompletionResultType]$ResultType + [string]$ToolTip + [bool]$Found - # Comparison is intentionally fuzzy - CompletionText and ResultType must be specified - # but the other properties don't need to match if they aren't specified - - if ($this.CompletionText -cne $Other.CompletionText -or - $this.ResultType -ne $Other.ResultType) + [bool] Equals($Other) { - return $false - } - - if ($this.ListItemText -cne $Other.ListItemText -and - ![string]::IsNullOrEmpty($this.ListItemText) -and ![string]::IsNullOrEmpty($Other.ListItemText)) - { - return $false - } - - if ($this.ToolTip -cne $Other.ToolTip -and - ![string]::IsNullOrEmpty($this.ToolTip) -and ![string]::IsNullOrEmpty($Other.ToolTip)) - { - return $false - } - - return $true - } -} + if ($Other -isnot [CompletionTestResult] -and + $Other -isnot [CompletionResult]) + { + return $false + } -class CompletionTestCase -{ - [CompletionTestResult[]]$ExpectedResults - [string[]]$NotExpectedResults - [string]$TestInput -} + # Comparison is intentionally fuzzy - CompletionText and ResultType must be specified + # but the other properties don't need to match if they aren't specified -function Get-Completions -{ - param([string]$inputScript, [int]$cursorColumn = $inputScript.Length) + if ($this.CompletionText -cne $Other.CompletionText -or + $this.ResultType -ne $Other.ResultType) + { + return $false + } - $results = [System.Management.Automation.CommandCompletion]::CompleteInput( - <#inputScript#> $inputScript, - <#cursorColumn#> $cursorColumn, - <#options#> $null) + if ($this.ListItemText -cne $Other.ListItemText -and + ![string]::IsNullOrEmpty($this.ListItemText) -and ![string]::IsNullOrEmpty($Other.ListItemText)) + { + return $false + } - return $results -} + if ($this.ToolTip -cne $Other.ToolTip -and + ![string]::IsNullOrEmpty($this.ToolTip) -and ![string]::IsNullOrEmpty($Other.ToolTip)) + { + return $false + } -function Get-CompletionTestCaseData -{ - param( - [Parameter(ValueFromPipeline)] - [hashtable[]]$Data) + return $true + } + } - process + class CompletionTestCase { - Write-Output ([CompletionTestCase[]]$Data) + [CompletionTestResult[]]$ExpectedResults + [string[]]$NotExpectedResults + [string]$TestInput } -} -function Test-Completions -{ - param( - [Parameter(ValueFromPipeline)] - [CompletionTestCase[]]$TestCases) + function Get-CompletionTestCaseData + { + param( + [Parameter(ValueFromPipeline)] + [hashtable[]]$Data) + + process + { + Write-Output ([CompletionTestCase[]]$Data) + } + } - process + function Test-Completions { - foreach ($test in $TestCases) + param( + [Parameter(ValueFromPipeline)] + [CompletionTestCase[]]$TestCases) + + process { - Context ("Command line: <" + $test.TestInput + ">") { - $results = Get-Completions $test.TestInput - foreach ($result in $results.CompletionMatches) - { + foreach ($test in $TestCases) + { + Context ("Command line: [" + $test.TestInput + "]") -ForEach @{ testInput = $test.TestInput } { + BeforeAll { + $results = Get-Completions $testInput + } + foreach ($expected in $test.ExpectedResults) { - if ($expected.Equals($result)) - { - $expected.Found = $true + $skip = $false + if ( $expected.CompletionText -Match "System.Management.Automation.PerformanceData|System.Management.Automation.Security" ) { $skip = $true } + It ($expected.CompletionText) -TestCases @{ expected = $expected } -Skip:$skip { + $found = $false + foreach ($result in $results.CompletionMatches) { + if ($expected.Equals($result)) { + $found = $true + break + } + } + $found | Should -BeTrue } } - } - foreach ($expected in $test.ExpectedResults) - { - $skip = $false - if ( $expected.CompletionText -Match "System.Management.Automation.PerformanceData|System.Management.Automation.Security" ) { $skip = $true } - It ($expected.CompletionText) -Skip:$skip { - $expected.Found | Should -BeTrue - } - } - foreach ($notExpected in $test.NotExpectedResults) - { - It "Not expected: $notExpected" { - foreach ($result in $results.CompletionMatches) - { - $result.CompletionText | Should -Not -Be $notExpected + foreach ($notExpected in $test.NotExpectedResults) + { + It "Not expected: $notExpected" -TestCases @{ notExpected = $notExpected } { + foreach ($result in $results.CompletionMatches) + { + $result.CompletionText | Should -Not -Be $notExpected + } } } } } } } -} -#endregion Testcase infrastructure - -function AlphaArgumentCompleter -{ - param( - [string] $CommandName, - [string] $parameterName, - [string] $wordToComplete, - [CommandAst] $commandAst, - [IDictionary] $fakeBoundParameters) - - $beta = $fakeBoundParameters['beta'] - $gamma = $fakeBoundParameters['Gamma'] - $result = "beta: $beta gamma: $gamma command: $commandName parameterName: $parameterName wordToComplete: $wordToComplete" - [CompletionResult]::new($result, $result, "ParameterValue", $result) + #endregion Testcase infrastructure - Discovery } -class BetaArgumentCompleter : IArgumentCompleter -{ - [IEnumerable[CompletionResult]] CompleteArgument( - [string] $CommandName, - [string] $parameterName, - [string] $wordToComplete, - [CommandAst] $commandAst, - [IDictionary] $fakeBoundParameters) +BeforeAll { + #region Testcase infrastructure - Run + + function Get-Completions { - $resultList = [List[CompletionResult]]::new() + param([string]$inputScript, [int]$cursorColumn = $inputScript.Length) - $alpha = $fakeBoundParameters['Alpha'] - $gamma = $fakeBoundParameters['Gamma'] - $result = "alpha: $alpha gamma: $gamma command: $commandName parameterName: $parameterName wordToComplete: $wordToComplete" - $resultList.Add([CompletionResult]::new($result, $result, "ParameterValue", $result)) + $results = [System.Management.Automation.CommandCompletion]::CompleteInput( + <#inputScript#> $inputScript, + <#cursorColumn#> $cursorColumn, + <#options#> $null) - return $resultList + return $results } -} -function TestFunction -{ - param( - [ArgumentCompleter({ AlphaArgumentCompleter @args })] - $Alpha, - [ArgumentCompleter([BetaArgumentCompleter])] - $Beta, - $Gamma - ) -} + function AlphaArgumentCompleter + { + param( + [string] $CommandName, + [string] $parameterName, + [string] $wordToComplete, + [CommandAst] $commandAst, + [IDictionary] $fakeBoundParameters) + $beta = $fakeBoundParameters['beta'] + $gamma = $fakeBoundParameters['Gamma'] + $result = "beta: $beta gamma: $gamma command: $commandName parameterName: $parameterName wordToComplete: $wordToComplete" + [CompletionResult]::new($result, $result, "ParameterValue", $result) + } -class NumberCompleter : IArgumentCompleter -{ + function TestFunction + { + param( + [ArgumentCompleter({ AlphaArgumentCompleter @args })] + $Alpha, + [ArgumentCompleter([BetaArgumentCompleter])] + $Beta, + $Gamma + ) + } - [int] $From - [int] $To - [int] $Step + function FactoryCompletionAdd { + param( + [NumberCompletion(0, 50, Step = 5)] + [int] $Number + ) + } - NumberCompleter([int] $from, [int] $to, [int] $step) + class BetaArgumentCompleter : IArgumentCompleter { - if ($from -gt $to) { - throw [ArgumentOutOfRangeException]::new("from") + [IEnumerable[CompletionResult]] CompleteArgument( + [string] $CommandName, + [string] $parameterName, + [string] $wordToComplete, + [CommandAst] $commandAst, + [IDictionary] $fakeBoundParameters) + { + $resultList = [List[CompletionResult]]::new() + + $alpha = $fakeBoundParameters['Alpha'] + $gamma = $fakeBoundParameters['Gamma'] + $result = "alpha: $alpha gamma: $gamma command: $commandName parameterName: $parameterName wordToComplete: $wordToComplete" + $resultList.Add([CompletionResult]::new($result, $result, "ParameterValue", $result)) + + return $resultList } - $this.From = $from - $this.To = $to - $this.Step = if($step -lt 1) { 1 } else { $step } } - [IEnumerable[CompletionResult]] CompleteArgument( - [string] $CommandName, - [string] $parameterName, - [string] $wordToComplete, - [CommandAst] $commandAst, - [IDictionary] $fakeBoundParameters) + class NumberCompleter : IArgumentCompleter { - $resultList = [List[CompletionResult]]::new() - $local:to = $this.To - for ($i = $this.From; $i -le $to; $i += $this.Step) { - if ($i.ToString().StartsWith($wordToComplete, [System.StringComparison]::Ordinal)) { - $num = $i.ToString() - $resultList.Add([CompletionResult]::new($num, $num, "ParameterValue", $num)) + + [int] $From + [int] $To + [int] $Step + + NumberCompleter([int] $from, [int] $to, [int] $step) + { + if ($from -gt $to) { + throw [ArgumentOutOfRangeException]::new("from") } + $this.From = $from + $this.To = $to + $this.Step = if($step -lt 1) { 1 } else { $step } } - return $resultList - } -} + [IEnumerable[CompletionResult]] CompleteArgument( + [string] $CommandName, + [string] $parameterName, + [string] $wordToComplete, + [CommandAst] $commandAst, + [IDictionary] $fakeBoundParameters) + { + $resultList = [List[CompletionResult]]::new() + $local:to = $this.To + for ($i = $this.From; $i -le $to; $i += $this.Step) { + if ($i.ToString().StartsWith($wordToComplete, [System.StringComparison]::Ordinal)) { + $num = $i.ToString() + $resultList.Add([CompletionResult]::new($num, $num, "ParameterValue", $num)) + } + } -class NumberCompletionAttribute : ArgumentCompleterAttribute, IArgumentCompleterFactory -{ - [int] $From - [int] $To - [int] $Step + return $resultList + } + } - NumberCompletionAttribute([int] $from, [int] $to) + class NumberCompletionAttribute : ArgumentCompleterAttribute, IArgumentCompleterFactory { - $this.From = $from - $this.To = $to - $this.Step = 1 - } + [int] $From + [int] $To + [int] $Step - [IArgumentCompleter] Create() { return [NumberCompleter]::new($this.From, $this.To, $this.Step) } -} + NumberCompletionAttribute([int] $from, [int] $to) + { + $this.From = $from + $this.To = $to + $this.Step = 1 + } + + [IArgumentCompleter] Create() { return [NumberCompleter]::new($this.From, $this.To, $this.Step) } + } -function FactoryCompletionAdd { - param( - [NumberCompletion(0, 50, Step = 5)] - [int] $Number - ) + #endregion Testcase infrastructure - Run } Describe "Factory based extensible completion" -Tags "CI" { @@ -265,18 +272,20 @@ Describe "Test class based extensible completion" -Tags "CI" { } Describe "Test registration based extensible completion" -Tags "CI" { - Register-ArgumentCompleter -Command TestFunction -Parameter Gamma -ScriptBlock { - param( - [string] $CommandName, - [string] $parameterName, - [string] $wordToComplete, - [CommandAst] $commandAst, - [IDictionary] $fakeBoundParameters) - - $beta = $fakeBoundParameters['beta'] - $alpha = $fakeBoundParameters['alpha'] - $result = "beta: $beta alpha: $alpha command: $commandName parameterName: $parameterName wordToComplete: $wordToComplete" - [CompletionResult]::new($result, $result, "ParameterValue", $result) + BeforeAll { + Register-ArgumentCompleter -Command TestFunction -Parameter Gamma -ScriptBlock { + param( + [string] $CommandName, + [string] $parameterName, + [string] $wordToComplete, + [CommandAst] $commandAst, + [IDictionary] $fakeBoundParameters) + + $beta = $fakeBoundParameters['beta'] + $alpha = $fakeBoundParameters['alpha'] + $result = "beta: $beta alpha: $alpha command: $commandName parameterName: $parameterName wordToComplete: $wordToComplete" + [CompletionResult]::new($result, $result, "ParameterValue", $result) + } } @{ @@ -288,9 +297,11 @@ Describe "Test registration based extensible completion" -Tags "CI" { } Describe "Test extensible completion of native commands" -Tags "CI" { - Register-ArgumentCompleter -Command netsh -Native -ScriptBlock { - [CompletionResult]::new('advfirewall', 'advfirewall', "ParameterValue", 'advfirewall') - [CompletionResult]::new('bridge', 'bridge', "ParameterValue", 'bridge') + BeforeAll { + Register-ArgumentCompleter -Command netsh -Native -ScriptBlock { + [CompletionResult]::new('advfirewall', 'advfirewall', "ParameterValue", 'advfirewall') + [CompletionResult]::new('bridge', 'bridge', "ParameterValue", 'bridge') + } } @{ @@ -303,15 +314,17 @@ Describe "Test extensible completion of native commands" -Tags "CI" { } Describe "Test completion of parameters for native commands" -Tags "CI" { - Register-ArgumentCompleter -Native -CommandName foo -ScriptBlock { - Param($wordToComplete) - - @("-dir", "-verbose", "-help", "-version") | - Where-Object { - $_ -Match "$wordToComplete*" - } | - ForEach-Object { - [CompletionResult]::new($_, $_, [CompletionResultType]::ParameterName, $_) + BeforeAll { + Register-ArgumentCompleter -Native -CommandName foo -ScriptBlock { + Param($wordToComplete) + + @("-dir", "-verbose", "-help", "-version") | + Where-Object { + $_ -Match "$wordToComplete*" + } | + ForEach-Object { + [CompletionResult]::new($_, $_, [CompletionResultType]::ParameterName, $_) + } } } @@ -431,6 +444,17 @@ Describe "Additional type name completion tests" -Tags "CI" { Describe "ArgumentCompletionsAttribute tests" -Tags "CI" { + BeforeDiscovery { + $testCasesScript = @( + @{ attributeName = "ArgumentCompletions" ; cmdletName = "TestArgumentCompletionsAttribute" }, + @{ attributeName = "ArgumentCompletionsAttribute"; cmdletName = "TestArgumentCompletionsAttribute1" } + ) + $testCasesCSharp = @( + @{ attributeName = "ArgumentCompletions" ; cmdletName = "Get-ArgumentCompletions" }, + @{ attributeName = "ArgumentCompletionsAttribute"; cmdletName = "Get-ArgumentCompletions1" } + ) + } + BeforeAll { function TestArgumentCompletionsAttribute { @@ -486,16 +510,6 @@ Describe "ArgumentCompletionsAttribute tests" -Tags "CI" { '@ $cls = Add-Type -TypeDefinition $cmdletSrc -PassThru | Select-Object -First 1 $testModule = Import-Module $cls.Assembly -PassThru - - $testCasesScript = @( - @{ attributeName = "ArgumentCompletions" ; cmdletName = "TestArgumentCompletionsAttribute" }, - @{ attributeName = "ArgumentCompletionsAttribute"; cmdletName = "TestArgumentCompletionsAttribute1" } - ) - - $testCasesCSharp = @( - @{ attributeName = "ArgumentCompletions" ; cmdletName = "Get-ArgumentCompletions" }, - @{ attributeName = "ArgumentCompletionsAttribute"; cmdletName = "Get-ArgumentCompletions1" } - ) } AfterAll { diff --git a/test/powershell/Language/Parser/LanguageAndParser.TestFollowup.Tests.ps1 b/test/powershell/Language/Parser/LanguageAndParser.TestFollowup.Tests.ps1 index b5a80b23155..9ec5a43eade 100644 --- a/test/powershell/Language/Parser/LanguageAndParser.TestFollowup.Tests.ps1 +++ b/test/powershell/Language/Parser/LanguageAndParser.TestFollowup.Tests.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -$powershellexe = (Get-Process -Id $PID).mainmodule.filename +BeforeAll { + $powershellexe = (Get-Process -Id $PID).mainmodule.filename +} Describe "Clone array" -Tags "CI" { It "Cast in target expr" { @@ -19,9 +21,11 @@ Describe "Clone array" -Tags "CI" { } Describe "Set fields through PSMemberInfo" -Tags "CI" { - Add-Type @" + BeforeAll { + Add-Type @" public struct AStruct { public string s; } "@ + } It "via cast" { ([AStruct]@{s = "abc" }).s | Should -BeExactly "abc" @@ -67,7 +71,9 @@ Describe "MSFT:3309783" -Tags "CI" { Describe "ScriptBlockAst.GetScriptBlock throws on error" -Tags "CI" { - $e = $null + BeforeAll { + $e = $null + } It "with parse error" { $ast = [System.Management.Automation.Language.Parser]::ParseInput('function ', [ref]$null, [ref]$e) @@ -83,7 +89,8 @@ Describe "ScriptBlockAst.GetScriptBlock throws on error" -Tags "CI" { } Describe "Hashtable key property syntax" -Tags "CI" { - $script = @' + BeforeAll { + $script = @' # First create a hashtable wrapped in PSObject $hash = New-Object hashtable $key = [ConsoleColor]::Red @@ -93,6 +100,7 @@ Describe "Hashtable key property syntax" -Tags "CI" { # works in PS 2,3,4. Fails in PS 5: $hash.$key '@ + } It "In current process" { # Run in current process, but something that ran earlier could influence @@ -109,23 +117,24 @@ Describe "Hashtable key property syntax" -Tags "CI" { Describe "Assign automatic variables" -Tags "CI" { - $autos = '_', 'args', 'this', 'input', 'pscmdlet', 'psboundparameters', 'myinvocation', 'psscriptroot', 'pscommandpath' - - foreach ($auto in $autos) - { - It "Assign auto w/ invalid type constraint - $auto" { - { & ([ScriptBlock]::Create("[datetime]`$$auto = 1")) } | Should -Throw $auto - { . ([ScriptBlock]::Create("[datetime]`$$auto = 1")) } | Should -Throw $auto - { & ([ScriptBlock]::Create("[runspace]`$$auto = 1")) } | Should -Throw $auto - { . ([ScriptBlock]::Create("[runspace]`$$auto = 1")) } | Should -Throw $auto - { & ([ScriptBlock]::Create("[notexist]`$$auto = 1")) } | Should -Throw $auto - { . ([ScriptBlock]::Create("[notexist]`$$auto = 1")) } | Should -Throw $auto + BeforeDiscovery { + $autos = '_', 'args', 'this', 'input', 'pscmdlet', 'psboundparameters', 'myinvocation', 'psscriptroot', 'pscommandpath' + $autosCases = $autos | ForEach-Object { @{ auto = $_ } } + } + + Context "Invalid type constraint - " -ForEach $autosCases { + It "Assign auto w/ invalid type constraint" { + { & ([ScriptBlock]::Create("[datetime]`$$auto = 1")) } | Should -Throw -ExpectedMessage "*$auto*" + { . ([ScriptBlock]::Create("[datetime]`$$auto = 1")) } | Should -Throw -ExpectedMessage "*$auto*" + { & ([ScriptBlock]::Create("[runspace]`$$auto = 1")) } | Should -Throw -ExpectedMessage "*$auto*" + { . ([ScriptBlock]::Create("[runspace]`$$auto = 1")) } | Should -Throw -ExpectedMessage "*$auto*" + { & ([ScriptBlock]::Create("[notexist]`$$auto = 1")) } | Should -Throw -ExpectedMessage "*$auto*" + { . ([ScriptBlock]::Create("[notexist]`$$auto = 1")) } | Should -Throw -ExpectedMessage "*$auto*" } } - foreach ($auto in $autos) - { - It "Assign auto w/o type constraint - $auto" { + Context "No type constraint - " -ForEach $autosCases { + It "Assign auto w/o type constraint" { & ([ScriptBlock]::Create("`$$auto = 1; `$$auto")) | Should -Be 1 . ([ScriptBlock]::Create("`$$auto = 1; `$$auto")) | Should -Be 1 } @@ -145,24 +154,26 @@ Describe "Assign automatic variables" -Tags "CI" { Describe "Assign readonly/constant variables" -Tags "CI" { - $testCase = @( - @{ sb_wo_conversion = { $? = 1 }; name = '$? = 1' } - @{ sb_wo_conversion = { $HOME = 1 }; name = '$HOME = 1' } - @{ sb_wo_conversion = { $PID = 1 }; name = '$PID = 1' } - ) - + BeforeDiscovery { + $testCase = @( + @{ sb_wo_conversion = { $? = 1 }; name = '$? = 1' } + @{ sb_wo_conversion = { $HOME = 1 }; name = '$HOME = 1' } + @{ sb_wo_conversion = { $PID = 1 }; name = '$PID = 1' } + ) + } It "Assign readonly/constant variables w/o type constraint - ''" -TestCases $testCase { param($sb_wo_conversion) { & $sb_wo_conversion } | Should -Throw -ErrorId "VariableNotWritable" { . $sb_wo_conversion } | Should -Throw -ErrorId "VariableNotWritable" } - $testCase = @( - @{ sb_w_conversion = { [datetime]$? = 1 }; name = '[datetime]$? = 1' } - @{ sb_w_conversion = { [datetime]$HOME = 1 }; name = '[datetime]$HOME = 1' } - @{ sb_w_conversion = { [datetime]$PID = 1 }; name = '[datetime]$PID = 1' } - ) - + BeforeDiscovery { + $testCase = @( + @{ sb_w_conversion = { [datetime]$? = 1 }; name = '[datetime]$? = 1' } + @{ sb_w_conversion = { [datetime]$HOME = 1 }; name = '[datetime]$HOME = 1' } + @{ sb_w_conversion = { [datetime]$PID = 1 }; name = '[datetime]$PID = 1' } + ) + } It "Assign readonly/constant variables w/ type constraint - ''" -TestCases $testCase { param($sb_w_conversion) { & $sb_w_conversion } | Should -Throw -ErrorId "VariableNotWritable" @@ -214,8 +225,7 @@ Describe "Members of System.Type" -Tags "CI" { } Describe "Hash expression with if statement as value" -Tags "CI" { - BeforeAll { - # With no extra new lines after if-statement + BeforeDiscovery { $hash1 = @{ a = if (1) {'a'} b = 'b' @@ -226,8 +236,6 @@ Describe "Hash expression with if statement as value" -Tags "CI" { g = if (0) {2} else {'g'} h = 'h' } - - # With extra new lines after if-statement $hash2 = @{ a = if (1) {'a'} @@ -242,8 +250,6 @@ Describe "Hash expression with if statement as value" -Tags "CI" { h = 'h' } - - # With expanded if-statement $hash3 = @{ a = if (1) { @@ -282,7 +288,6 @@ Describe "Hash expression with if statement as value" -Tags "CI" { } h = 'h' } - $testCases = @( @{ name = "No extra new lines"; hash = $hash1 } @{ name = "With extra new lines"; hash = $hash2 } diff --git a/test/powershell/Language/Parser/MethodInvocation.Tests.ps1 b/test/powershell/Language/Parser/MethodInvocation.Tests.ps1 index a9641951473..89d4776204a 100644 --- a/test/powershell/Language/Parser/MethodInvocation.Tests.ps1 +++ b/test/powershell/Language/Parser/MethodInvocation.Tests.ps1 @@ -3,7 +3,7 @@ Describe 'Generic Method invocation' -Tags 'CI' { - BeforeAll { + BeforeDiscovery { $EmptyArrayCases = @( @{ Script = '[Array]::Empty[string]()' @@ -260,12 +260,13 @@ Describe 'Generic Method invocation' -Tags 'CI' { Describe "Interface inheritance with remoting proxies" -Tags "CI" { - if ( $IsCoreCLR ) { - Write-Verbose -Verbose "Skip this test because it's .NET Framework dependency." - return - } + BeforeAll { + if ( $IsCoreCLR ) { + Write-Verbose -Verbose "Skip this test because it's .NET Framework dependency." + return + } - $src = @" + $src = @" using System; using System.ServiceModel; @@ -318,7 +319,8 @@ namespace MSFT_716893 } "@ - Add-Type -TypeDefinition $src -ReferencedAssemblies System.ServiceModel.dll + Add-Type -TypeDefinition $src -ReferencedAssemblies System.ServiceModel.dll + } BeforeEach { [MSFT_716893.Service]::Init() diff --git a/test/powershell/Language/Parser/ParameterBinding.Tests.ps1 b/test/powershell/Language/Parser/ParameterBinding.Tests.ps1 index 493ea9083a0..9945af32db3 100644 --- a/test/powershell/Language/Parser/ParameterBinding.Tests.ps1 +++ b/test/powershell/Language/Parser/ParameterBinding.Tests.ps1 @@ -2,7 +2,8 @@ # Licensed under the MIT License. Describe 'Argument transformation attribute on optional argument with explicit $null' -Tags "CI" { - $tdefinition = @' + BeforeAll { + $tdefinition = @' using System; using System.Management.Automation; using System.Reflection; @@ -47,26 +48,27 @@ Describe 'Argument transformation attribute on optional argument with explicit $ } } '@ - $mod = Add-Type -PassThru -TypeDefinition $tdefinition + $mod = Add-Type -PassThru -TypeDefinition $tdefinition - Import-Module $mod[0].Assembly -ErrorVariable ErrorImportingModule + Import-Module $mod[0].Assembly -ErrorVariable ErrorImportingModule - function Invoke-ScriptFunctionTakesObject - { - param([MSFT_1407291.AddressTransformation()] - [Parameter(Mandatory = $false)] - [object]$Address = "passed in null") + function Invoke-ScriptFunctionTakesObject + { + param([MSFT_1407291.AddressTransformation()] + [Parameter(Mandatory = $false)] + [object]$Address = "passed in null") - return $Address - } + return $Address + } - function Invoke-ScriptFunctionTakesUInt64 - { - param([MSFT_1407291.AddressTransformation()] - [Parameter(Mandatory = $false)] - [Uint64]$Address = 11) + function Invoke-ScriptFunctionTakesUInt64 + { + param([MSFT_1407291.AddressTransformation()] + [Parameter(Mandatory = $false)] + [Uint64]$Address = 11) - return $Address + return $Address + } } It "There was no error importing the in-memory module" { @@ -434,7 +436,7 @@ Describe "Custom type conversion in parameter binding" -Tags 'Feature' { Describe 'Roundtrippable Conversions for Bare-string Numeric Literals passed to [string] Parameters' -Tags CI { - BeforeAll { + BeforeDiscovery { $TestValues = @( @{ Argument = "34uy" } @{ Argument = "48y" } @@ -447,7 +449,9 @@ Describe 'Roundtrippable Conversions for Bare-string Numeric Literals passed to @{ Argument = "20d" } @{ Argument = "6n" } ) + } + BeforeAll { function Test-SimpleStringValue([string] $Value) { $Value } function Test-AdvancedStringValue { [CmdletBinding()] diff --git a/test/powershell/Language/Parser/Parser.Tests.ps1 b/test/powershell/Language/Parser/Parser.Tests.ps1 index e0f90d3a326..38a624b2d13 100644 --- a/test/powershell/Language/Parser/Parser.Tests.ps1 +++ b/test/powershell/Language/Parser/Parser.Tests.ps1 @@ -715,17 +715,19 @@ foo``u{2195}abc } Context "Boolean Tests (starting at line 1723 to line 1772)" { - $testData = @( - @{ Script = '"False"'; Expected = $true } - @{ Script = 'if ("A") { $true } else { $false }'; Expected = $true } - @{ Script = 'if (" ") { $true } else { $false }'; Expected = $true } - @{ Script = 'if ("String with spaces") { $true } else { $false }'; Expected = $true } - @{ Script = 'if ("DoubleQuoted") { $true } else { $false }'; Expected = $true } - @{ Script = 'if ("StringWithNullVar$aEmbedded") { $true } else { $false }'; Expected = $true } - @{ Script = 'if (0) { $true } else { $false }'; Expected = $false } - @{ Script = '$a = $(0);if ($a) { $true } else { $false }'; Expected = $false } - @{ Script = '$obj = testcmd-parserBVT -ReturnType object;if ($obj) { $true } else { $false }'; Expected = $true } - ) + BeforeDiscovery { + $testData = @( + @{ Script = '"False"'; Expected = $true } + @{ Script = 'if ("A") { $true } else { $false }'; Expected = $true } + @{ Script = 'if (" ") { $true } else { $false }'; Expected = $true } + @{ Script = 'if ("String with spaces") { $true } else { $false }'; Expected = $true } + @{ Script = 'if ("DoubleQuoted") { $true } else { $false }'; Expected = $true } + @{ Script = 'if ("StringWithNullVar$aEmbedded") { $true } else { $false }'; Expected = $true } + @{ Script = 'if (0) { $true } else { $false }'; Expected = $false } + @{ Script = '$a = $(0);if ($a) { $true } else { $false }'; Expected = $false } + @{ Script = '$obj = testcmd-parserBVT -ReturnType object;if ($obj) { $true } else { $false }'; Expected = $true } + ) + } It "