Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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;
Expand Down Expand Up @@ -97,9 +98,28 @@ private static Dictionary<string, List<string>> GetTypeGroupMap(IEnumerable<Type
/// </summary>
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;

Expand Down
20 changes: 20 additions & 0 deletions src/System.Management.Automation/engine/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
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;
Expand Down Expand Up @@ -2098,6 +2099,25 @@ public static bool TestImplicitRemotingBatching(string commandPipeline, System.M
{
return Utils.TryRunAsImplicitBatch(commandPipeline, runspace);
}

/// <summary>
/// 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.
/// </summary>
/// <param name="url">The connection URL to reflect in the returned instance's ConnectionString property.</param>
/// <param name="clientVersion">The version number to report as the remoting client's PowerShell version.</param>
/// <returns>The newly constructed custom PSSenderInfo instance.</returns>
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;
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.

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 <cmd>" -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)
$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 "<cmd> should return <shouldBeNull> 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, <cmd> should return <shouldBeNull> for a null-output test" -TestCases $cmds {
param([scriptblock] $cmd, [bool] $shouldBeNull)
$null -eq $(& $cmd) | Should -Be $shouldBeNull
}
}

}