Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,42 @@ private void ProcessCachedGroupOnWide(WideViewHeaderInfo wvhi, List<PacketInfoDa
_formattingHint = hint;
}

/// <summary>
/// In cases like implicit remoting, there is no console so reading the console width results in an exception.
/// Instead of handling exception every time we cache this value to increase performance.
/// </summary>
static private bool _noConsole = false;

/// <summary>
/// Tables and Wides need to use spaces for padding to maintain table look even if console window is resized.
/// For all other output, we use int.MaxValue if the user didn't explicitly specify a width.
/// If we detect that int.MaxValue is used, first we try to get the current console window width.
/// However, if we can't read that (for example, implicit remoting has no console window), we default
/// to something reasonable: 120 columns.
/// </summary>
static private int GetConsoleWindowWidth(int columnNumber)
{
const int defaultConsoleWidth = 120;

if (columnNumber == int.MaxValue)
{
if (_noConsole)
{
return defaultConsoleWidth;
}
try
{
return Console.WindowWidth;
}
catch
{
_noConsole = true;
return defaultConsoleWidth;
}
}
return columnNumber;
}

/// <summary>
/// base class for all the formatting hints
/// </summary>
Expand Down Expand Up @@ -910,23 +946,7 @@ internal override void Initialize()
columnWidthsHint = tableHint.columnWidths;
}

int columnsOnTheScreen = this.InnerCommand._lo.ColumnNumber;
// Tables need to use spaces for padding to maintain table look even if console window is resized.
// For all other output, we use int.MaxValue if the user didn't explicitly specify a width.
// If we detect that int.MaxValue is used, first we try to get the current console window width.
// However, if we can't read that (for example, implicit remoting has no console window), we default
// to something reasonable: 120 columns.
if (columnsOnTheScreen == int.MaxValue)
{
try
{
columnsOnTheScreen = Console.WindowWidth;
}
catch
{
columnsOnTheScreen = 120;
}
}
int columnsOnTheScreen = GetConsoleWindowWidth(this.InnerCommand._lo.ColumnNumber);

int columns = this.CurrentTableHeaderInfo.tableColumnInfoList.Count;
if (columns == 0)
Expand Down Expand Up @@ -1132,10 +1152,12 @@ internal override void Initialize()
// get the header info and the view hint
WideFormattingHint hint = this.InnerCommand.RetrieveFormattingHint() as WideFormattingHint;

int columnsOnTheScreen = GetConsoleWindowWidth(this.InnerCommand._lo.ColumnNumber);

// give a preference to the hint, if there
if (hint != null && hint.maxWidth > 0)
{
itemsPerRow = TableWriter.ComputeWideViewBestItemsPerRowFit(hint.maxWidth, this.InnerCommand._lo.ColumnNumber);
itemsPerRow = TableWriter.ComputeWideViewBestItemsPerRowFit(hint.maxWidth, columnsOnTheScreen);
}
else if (this.CurrentWideHeaderInfo.columns > 0)
{
Expand All @@ -1155,7 +1177,7 @@ internal override void Initialize()
alignment[k] = TextAlignment.Left;
}

this.Writer.Initialize(0, this.InnerCommand._lo.ColumnNumber, columnWidths, alignment, false);
this.Writer.Initialize(0, columnsOnTheScreen, columnWidths, alignment, false);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Describe "Format-Wide" -Tags "CI" {

It "Should have the same output between the alias and the unaliased function" {
$nonaliased = Get-ChildItem | Format-Wide
$aliased = Get-ChildItem | fw

$($nonaliased | Out-String).CompareTo($($aliased | Out-String)) | Should -Be 0
BeforeAll {
1..2 | ForEach-Object { New-Item -Path ("TestDrive:\Testdir{0:00}" -f $_) -ItemType Directory }
1..2 | ForEach-Object { New-Item -Path ("TestDrive:\TestFile{0:00}.txt" -f $_) -ItemType File }
$pathList = Get-ChildItem $TestDrive
}

It "Should be able to specify the columns in output using the column switch" {
{ Get-ChildItem | Format-Wide -Column 3 } | Should -Not -Throw
{ $pathList | Format-Wide -Column 3 } | Should -Not -Throw
}

It "Should be able to use the autosize switch" {
{ Get-ChildItem | Format-Wide -Autosize } | Should -Not -Throw
{ $pathList | Format-Wide -Autosize } | Should -Not -Throw
{ $pathList | Format-Wide -Autosize | Out-String } | Should -Not -Throw
}

It "Should be able to take inputobject instead of pipe" {
{ Format-Wide -InputObject $(Get-ChildItem) } | Should -Not -Throw
{ Format-Wide -InputObject $pathList } | Should -Not -Throw
}

It "Should be able to use the property switch" {
{ Format-Wide -InputObject $(Get-ChildItem) -Property Mode } | Should -Not -Throw
{ Format-Wide -InputObject $pathList -Property Mode } | Should -Not -Throw
}

It "Should throw an error when property switch and view switch are used together" {
{ Format-Wide -InputObject $(Get-ChildItem) -Property CreationTime -View aoeu } |
Should -Throw -ErrorId "FormatCannotSpecifyViewAndProperty,Microsoft.PowerShell.Commands.FormatWideCommand"
{ Format-Wide -InputObject $pathList -Property CreationTime -View aoeu } |
Should -Throw -ErrorId "FormatCannotSpecifyViewAndProperty,Microsoft.PowerShell.Commands.FormatWideCommand"
}

It "Should throw and suggest proper input when view is used with invalid input without the property switch" {
Expand All @@ -36,74 +35,74 @@ Describe "Format-Wide" -Tags "CI" {
}

Describe "Format-Wide DRT basic functionality" -Tags "CI" {
It "Format-Wide with array should work" {
$al = (0..255)
$info = @{}
$info.array = $al
$result = $info | Format-Wide | Out-String
$result | Should -Match "array"
}
It "Format-Wide with array should work" {
$al = (0..255)
$info = @{}
$info.array = $al
$result = $info | Format-Wide | Out-String
$result | Should -Match "array"
}

It "Format-Wide with No Objects for End-To-End should work"{
$p = @{}
$result = $p | Format-Wide | Out-String
$result | Should -BeNullOrEmpty
}
It "Format-Wide with No Objects for End-To-End should work" {
$p = @{}
$result = $p | Format-Wide | Out-String
$result | Should -BeNullOrEmpty
}

It "Format-Wide with Null Objects for End-To-End should work"{
$p = $null
$result = $p | Format-Wide | Out-String
$result | Should -BeNullOrEmpty
}
It "Format-Wide with Null Objects for End-To-End should work" {
$p = $null
$result = $p | Format-Wide | Out-String
$result | Should -BeNullOrEmpty
}

It "Format-Wide with single line string for End-To-End should work"{
$p = "single line string"
$result = $p | Format-Wide | Out-String
$result | Should -Match $p
}
It "Format-Wide with single line string for End-To-End should work" {
$p = "single line string"
$result = $p | Format-Wide | Out-String
$result | Should -Match $p
}

It "Format-Wide with multiple line string for End-To-End should work"{
$p = "Line1\nLine2"
$result = $p | Format-Wide | Out-String
$result | Should -Match "Line1"
$result | Should -Match "Line2"
}
It "Format-Wide with multiple line string for End-To-End should work" {
$p = "Line1\nLine2"
$result = $p | Format-Wide | Out-String
$result | Should -Match "Line1"
$result | Should -Match "Line2"
}

It "Format-Wide with string sequence for End-To-End should work"{
$p = "Line1","Line2"
$result = $p |Format-Wide | Out-String
$result | Should -Match "Line1"
$result | Should -Match "Line2"
}
It "Format-Wide with string sequence for End-To-End should work" {
$p = "Line1", "Line2"
$result = $p |Format-Wide | Out-String
$result | Should -Match "Line1"
$result | Should -Match "Line2"
}

It "Format-Wide with complex object for End-To-End should work" {
Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thu,Fri,Sat}"
$eto = New-Object MyDayOfWeek
$info = @{}
$info.intArray = 1,2,3,4
$info.arrayList = "string1","string2"
$info.enumerable = [MyDayOfWeek]$eto
$info.enumerableTestObject = $eto
$result = $info|Format-Wide|Out-String
$result | Should -Match "intArray"
$result | Should -Match "arrayList"
$result | Should -Match "enumerable"
$result | Should -Match "enumerableTestObject"
}
It "Format-Wide with complex object for End-To-End should work" {
Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thu,Fri,Sat}"
$eto = New-Object MyDayOfWeek
$info = @{}
$info.intArray = 1, 2, 3, 4
$info.arrayList = "string1", "string2"
$info.enumerable = [MyDayOfWeek]$eto
$info.enumerableTestObject = $eto
$result = $info|Format-Wide|Out-String
$result | Should -Match "intArray"
$result | Should -Match "arrayList"
$result | Should -Match "enumerable"
$result | Should -Match "enumerableTestObject"
}

It "Format-Wide with multiple same class object with grouping should work"{
Add-Type -TypeDefinition "public class TestGroupingClass{public TestGroupingClass(string name,int length){Name = name;Length = length;}public string Name;public int Length;public string GroupingKey;}"
$testobject1 = [TestGroupingClass]::New('name1',1)
$testobject1.GroupingKey = "foo"
$testobject2 = [TestGroupingClass]::New('name2',2)
$testobject1.GroupingKey = "bar"
$testobject3 = [TestGroupingClass]::New('name3',3)
$testobject1.GroupingKey = "bar"
$testobjects = @($testobject1,$testobject2,$testobject3)
$result = $testobjects|Format-Wide -GroupBy GroupingKey|Out-String
$result | Should -Match "GroupingKey: bar"
$result | Should -Match "name1"
$result | Should -Match " GroupingKey:"
$result | Should -Match "name2\s+name3"
}
It "Format-Wide with multiple same class object with grouping should work" {
Add-Type -TypeDefinition "public class TestGroupingClass{public TestGroupingClass(string name,int length){Name = name;Length = length;}public string Name;public int Length;public string GroupingKey;}"
$testobject1 = [TestGroupingClass]::New('name1', 1)
$testobject1.GroupingKey = "foo"
$testobject2 = [TestGroupingClass]::New('name2', 2)
$testobject1.GroupingKey = "bar"
$testobject3 = [TestGroupingClass]::New('name3', 3)
$testobject1.GroupingKey = "bar"
$testobjects = @($testobject1, $testobject2, $testobject3)
$result = $testobjects|Format-Wide -GroupBy GroupingKey|Out-String
$result | Should -Match "GroupingKey: bar"
$result | Should -Match "name1"
$result | Should -Match " GroupingKey:"
$result | Should -Match "name2\s+name3"
}
}