diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs
index ae9e83bce1..09a6a88693 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/common/GetFormatDataCommand.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Management.Automation;
+using System.Management.Automation.Remoting;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell.Commands.Internal.Format;
@@ -97,9 +98,28 @@ namespace Microsoft.PowerShell.Commands
///
protected override void ProcessRecord()
{
- bool writeOldWay = PowerShellVersion == null ||
- PowerShellVersion.Major < 5 ||
- (PowerShellVersion.Major == 5 && PowerShellVersion.Minor < 1);
+ // Remoting detection:
+ // * Automatic variable $PSSenderInfo is defined in true remoting contexts as well as in background jobs.
+ // * $PSSenderInfo.ApplicationArguments.PSVersionTable.PSVersion contains the client version, as a [version] instance.
+ // Note: Even though $PSVersionTable.PSVersion is of type [semver] in PowerShell 6+, it is of type [version] here,
+ // presumably because only the latter type deserializes type-faithfully.
+ var clientVersion = PowerShellVersion;
+ PSSenderInfo remotingClientInfo = GetVariableValue("PSSenderInfo") as PSSenderInfo;
+ if (clientVersion == null && remotingClientInfo != null)
+ {
+ clientVersion = PSObject.Base((PSObject.Base(remotingClientInfo.ApplicationArguments["PSVersionTable"]) as PSPrimitiveDictionary)?["PSVersion"]) as Version;
+ }
+
+ // During remoting, remain compatible with v5.0- clients by default.
+ // Passing a -PowerShellVersion argument allows overriding the client version.
+ bool writeOldWay =
+ (remotingClientInfo != null && clientVersion == null) // To be safe: Remoting client version could unexpectedly not be determined.
+ ||
+ (clientVersion != null
+ &&
+ (clientVersion.Major < 5
+ ||
+ (clientVersion.Major == 5 && clientVersion.Minor < 1)));
TypeInfoDataBase db = this.Context.FormatDBManager.Database;
diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs
index 6c12255199..a188a091e3 100644
--- a/src/System.Management.Automation/engine/Utils.cs
+++ b/src/System.Management.Automation/engine/Utils.cs
@@ -14,6 +14,7 @@ using System.Linq;
using System.Management.Automation.Configuration;
using System.Management.Automation.Internal;
using System.Management.Automation.Language;
+using System.Management.Automation.Remoting;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Security;
using System.Numerics;
@@ -2098,6 +2099,25 @@ namespace System.Management.Automation.Internal
{
return Utils.TryRunAsImplicitBatch(commandPipeline, runspace);
}
+
+ ///
+ /// Constructs a custom PSSenderInfo instance that can be assigned to $PSSenderInfo
+ /// in order to simulate a remoting session with respect to the $PSSenderInfo.ConnectionString (connection URL)
+ /// and $PSSenderInfo.ApplicationArguments.PSVersionTable.PSVersion (the remoting client's PowerShell version).
+ /// See Get-FormatDataTest.ps1.
+ ///
+ /// The connection URL to reflect in the returned instance's ConnectionString property.
+ /// The version number to report as the remoting client's PowerShell version.
+ /// The newly constructed custom PSSenderInfo instance.
+ public static PSSenderInfo GetCustomPSSenderInfo(string url, Version clientVersion)
+ {
+ var dummyPrincipal = new PSPrincipal(new PSIdentity("none", true, "someuser", null), null);
+ var pssi = new PSSenderInfo(dummyPrincipal, url);
+ pssi.ApplicationArguments = new PSPrimitiveDictionary();
+ pssi.ApplicationArguments.Add("PSVersionTable", new PSObject(new PSPrimitiveDictionary()));
+ ((PSPrimitiveDictionary)PSObject.Base(pssi.ApplicationArguments["PSVersionTable"])).Add("PSVersion", new PSObject(clientVersion));
+ return pssi;
+ }
}
///
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-FormatData.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-FormatData.Tests.ps1
index 12af02aa27..42b69c6fa1 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-FormatData.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Export-FormatData.Tests.ps1
@@ -2,7 +2,8 @@
# Licensed under the MIT License.
Describe "Export-FormatData" -Tags "CI" {
BeforeAll {
- $fd = Get-FormatData
+ $clientVersion = '5.0' # Preliminarily preserve the original test semantics in place before https://github.com/PowerShell/PowerShell/pull/11270
+ $fd = Get-FormatData -PowerShellVersion $clientVersion
$testOutput = Join-Path -Path $TestDrive -ChildPath "outputfile"
}
@@ -23,7 +24,7 @@ Describe "Export-FormatData" -Tags "CI" {
$runspace.Open()
$runspace.CreatePipeline("Update-FormatData -AppendPath $TESTDRIVE\allformat.ps1xml").Invoke()
- $actualAllFormat = $runspace.CreatePipeline("Get-FormatData -TypeName *").Invoke()
+ $actualAllFormat = $runspace.CreatePipeline("Get-FormatData -PowerShellVersion $clientVersion").Invoke()
$fd.Count | Should -Be $actualAllFormat.Count
Compare-Object $fd $actualAllFormat | Should -Be $null
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-FormatData.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-FormatData.Tests.ps1
index baf349e107..04bffe8df8 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-FormatData.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Get-FormatData.Tests.ps1
@@ -3,24 +3,64 @@
Describe "Get-FormatData" -Tags "CI" {
Context "Check return type of Get-FormatData" {
-
It "Should return an object[] as the return type" {
$result = Get-FormatData
- ,$result | Should -BeOfType System.Object[]
+ , $result | Should -BeOfType "System.Object[]"
}
}
- It "Can get format data requiring '-PowerShellVersion 5.1'" {
- $format = Get-FormatData System.IO.FileInfo -PowerShellVersion 5.1
- $format.TypeNames | Should -HaveCount 2
- $format.TypeNames[0] | Should -BeExactly "System.IO.DirectoryInfo"
- $format.TypeNames[1] | Should -BeExactly "System.IO.FileInfo"
+ # Note: Format data for [System.IO.FileInfo] (among others) is not to be
+ # returned to v5.0- remoting clients.
- $isUnixStatEnabled = $EnabledExperimentalFeatures -contains 'PSUnixFileStat'
- $format.FormatViewDefinition | Should -HaveCount ( $isUnixStatEnabled ? 5 : 4)
+ Context "Local use: Can get format data requiring v5.1+ by default" {
+ BeforeAll {
+ $cmds = @(
+ @{ cmd = { Get-FormatData System.IO.FileInfo } }
+ @{ cmd = { (Get-FormatData System.IO.FileInfo &) | Receive-Job -Wait -AutoRemoveJob } }
+ )
+ }
+ It "Can get format data requiring v5.1+ with " -TestCases $cmds {
+ param([scriptblock] $cmd)
+ $format = & $cmd
+ $format.TypeNames | Should -HaveCount 2
+ $format.TypeNames[0] | Should -BeExactly "System.IO.DirectoryInfo"
+ $format.TypeNames[1] | Should -BeExactly "System.IO.FileInfo"
+
+ $isUnixStatEnabled = $EnabledExperimentalFeatures -contains 'PSUnixFileStat'
+ $format.FormatViewDefinition | Should -HaveCount ($isUnixStatEnabled ? 5 : 4)
+ }
}
- It "Should return nothing for format data requiring '-PowerShellVersion 5.1' and not provided" {
- Get-FormatData System.IO.FileInfo | Should -BeNullOrEmpty
+ Context "Can override client version with -PowerShellVersion" {
+ BeforeAll {
+ $cmds = @(
+ @{ shouldBeNull = $true; cmd = { Get-FormatData System.IO.FileInfo -PowerShellVersion 5.0 } }
+ @{ shouldBeNull = $false; cmd = { Get-FormatData System.IO.FileInfo -PowerShellVersion 5.1 } }
+ @{ shouldBeNull = $false; cmd = { $PSSenderInfo = [System.Management.Automation.Internal.InternalTestHooks]::GetCustomPSSenderInfo('foo', [version] '5.0'); Get-FormatData System.IO.FileInfo -PowerShellVersion 5.1 } }
+ )
+ }
+ It " should return for a null-output test" -TestCases $cmds {
+ param([scriptblock] $cmd, [bool] $shouldBeNull)
+ $null -eq $(& $cmd) | Should -Be $shouldBeNull
+ }
}
+
+ Context "Remote use: By default, don't get format data requiring v5.1+ for v5.0- clients" {
+ BeforeAll {
+ # Simulated PSSenderInfo instances for various PowerShell versions.
+ $pssiV50 = [System.Management.Automation.Internal.InternalTestHooks]::GetCustomPSSenderInfo('foo', [version] '5.0')
+ $pssiV51 = [System.Management.Automation.Internal.InternalTestHooks]::GetCustomPSSenderInfo('foo', [version] '5.1')
+ $pssiV70 = [System.Management.Automation.Internal.InternalTestHooks]::GetCustomPSSenderInfo('foo', [version] '7.0')
+ $cmds = @(
+ @{ shouldBeNull = $true; cmd = { $PSSenderInfo = $pssiV50; Get-FormatData System.IO.FileInfo } }
+ @{ shouldBeNull = $false; cmd = { $PSSenderInfo = $pssiV51; Get-FormatData System.IO.FileInfo } }
+ @{ shouldBeNull = $false; cmd = { $PSSenderInfo = $pssiV70; Get-FormatData System.IO.FileInfo } }
+ )
+ }
+ It "When remoting, should return for a null-output test" -TestCases $cmds {
+ param([scriptblock] $cmd, [bool] $shouldBeNull)
+ $null -eq $(& $cmd) | Should -Be $shouldBeNull
+ }
+ }
+
}