Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Comment thread
iSazonov marked this conversation as resolved.
{
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 } }
Comment thread
mklement0 marked this conversation as resolved.
)
}
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
}
}

}