diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs index 6bb1f37a9c..810534a9ff 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -17,12 +17,30 @@ namespace Microsoft.PowerShell.Commands /// Displays the hexadecimal equivalent of the input data. /// [Cmdlet(VerbsCommon.Format, "Hex", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=526919")] - [OutputType(typeof(Microsoft.PowerShell.Commands.ByteCollection))] + [OutputType(typeof(ByteCollection))] [Alias("fhx")] public sealed class FormatHex : PSCmdlet { private const int BUFFERSIZE = 16; + /// + /// For cases where a homogenous collection of bytes or other items are directly piped in, we collect all the + /// bytes in a List<byte> and then output the formatted result all at once in EndProcessing(). + /// + private readonly List _inputBuffer = new List(); + + /// + /// Expect to group s by default. When receiving input that should not be grouped, + /// e.g., arrays, strings, FileInfo objects, this flag will be disabled until the next groupable + /// is received over the pipeline. + /// + private bool _groupInput = true; + + /// + /// Keep track of prior input types to determine if we're given a heterogenous collection. + /// + private Type _lastInputType; + #region Parameters /// @@ -85,19 +103,28 @@ namespace Microsoft.PowerShell.Commands /// protected override void ProcessRecord() { - if (string.Equals(this.ParameterSetName, "ByInputObject", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(ParameterSetName, "ByInputObject", StringComparison.OrdinalIgnoreCase)) { - ProcessObjectContent(InputObject); + ProcessInputObjects(InputObject); } else { - List pathsToProcess = string.Equals(this.ParameterSetName, "LiteralPath", StringComparison.OrdinalIgnoreCase) ? - ResolvePaths(LiteralPath, true) : ResolvePaths(Path, false); + List pathsToProcess = string.Equals(ParameterSetName, "LiteralPath", StringComparison.OrdinalIgnoreCase) + ? ResolvePaths(LiteralPath, true) + : ResolvePaths(Path, false); ProcessPath(pathsToProcess); } } + /// + /// Implements the EndProcessing method for the FormatHex command. + /// + protected override void EndProcessing() + { + FlushInputBuffer(); + } + #endregion #region Paths @@ -114,7 +141,6 @@ namespace Microsoft.PowerShell.Commands { List pathsToProcess = new List(); ProviderInfo provider = null; - PSDriveInfo drive = null; foreach (string currentPath in path) { @@ -122,7 +148,7 @@ namespace Microsoft.PowerShell.Commands if (literalPath) { - newPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(currentPath, out provider, out drive)); + newPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(currentPath, out provider, out _)); } else { @@ -183,34 +209,32 @@ namespace Microsoft.PowerShell.Commands try { - using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))) + using var reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)); + long offset = Offset; + int bytesRead = 0; + long count = 0; + + reader.BaseStream.Position = Offset; + + while ((bytesRead = reader.Read(buffer)) > 0) { - long offset = Offset; - int bytesRead = 0; - long count = 0; - - reader.BaseStream.Position = Offset; - - while ((bytesRead = reader.Read(buffer)) > 0) + count += bytesRead; + if (count > Count) { - count += bytesRead; - if (count > Count) - { - bytesRead -= (int)(count - Count); - WriteHexadecimal(buffer.Slice(0, bytesRead), path, offset); - break; - } - + bytesRead -= (int)(count - Count); WriteHexadecimal(buffer.Slice(0, bytesRead), path, offset); - - offset += bytesRead; + break; } + + WriteHexadecimal(buffer.Slice(0, bytesRead), path, offset); + + offset += bytesRead; } } - catch (IOException ioException) + catch (IOException fileException) { // IOException takes care of FileNotFoundException, DirectoryNotFoundException, and PathTooLongException - WriteError(new ErrorRecord(ioException, "FormatHexIOError", ErrorCategory.WriteError, path)); + WriteError(new ErrorRecord(fileException, "FormatHexIOError", ErrorCategory.WriteError, path)); } catch (ArgumentException argException) { @@ -218,11 +242,19 @@ namespace Microsoft.PowerShell.Commands } catch (NotSupportedException notSupportedException) { - WriteError(new ErrorRecord(notSupportedException, "FormatHexPathRefersToANonFileDevice", ErrorCategory.InvalidArgument, path)); + WriteError(new ErrorRecord( + notSupportedException, + "FormatHexPathRefersToANonFileDevice", + ErrorCategory.InvalidArgument, + path)); } catch (SecurityException securityException) { - WriteError(new ErrorRecord(securityException, "FormatHexUnauthorizedAccessError", ErrorCategory.PermissionDenied, path)); + WriteError(new ErrorRecord( + securityException, + "FormatHexUnauthorizedAccessError", + ErrorCategory.PermissionDenied, + path)); } } @@ -230,37 +262,95 @@ namespace Microsoft.PowerShell.Commands #region InputObjects + private void ProcessString(string originalString) + { + Span bytes = Encoding.GetBytes(originalString); + + int offset = Math.Min(bytes.Length, Offset < int.MaxValue ? (int)Offset : int.MaxValue); + int count = Math.Min(bytes.Length - offset, Count < int.MaxValue ? (int)Count : int.MaxValue); + + if (offset != 0 || count != bytes.Length) + { + WriteHexadecimal(bytes.Slice(offset, count), offset: 0, label: GetGroupLabel(typeof(string))); + } + else + { + WriteHexadecimal(bytes, offset: 0, label: GetGroupLabel(typeof(string))); + } + } + + private static readonly Random _idGenerator = new Random(); + private static string GetGroupLabel(Type inputType) + => string.Format("{0} ({1}) <{2:X8}>", inputType.Name, inputType.FullName, _idGenerator.Next()); + + private void FlushInputBuffer() + { + if (_inputBuffer.Count == 0) + { + return; + } + + int offset = Math.Min(_inputBuffer.Count, Offset < int.MaxValue ? (int)Offset : int.MaxValue); + int count = Math.Min(_inputBuffer.Count - offset, Count < int.MaxValue ? (int)Count : int.MaxValue); + + if (offset != 0 || count != _inputBuffer.Count) + { + WriteHexadecimal( + _inputBuffer.GetRange(offset, count).ToArray(), + offset: 0, + label: GetGroupLabel(_lastInputType)); + } + else + { + WriteHexadecimal( + _inputBuffer.ToArray(), + offset: 0, + label: GetGroupLabel(_lastInputType)); + } + + // Reset flags so we can go back to filling up the buffer when needed. + _lastInputType = null; + _groupInput = true; + _inputBuffer.Clear(); + } + /// /// Creates a byte array from the object passed to the cmdlet (based on type) and passes /// that array on to the WriteHexadecimal method to output. /// /// The pipeline input object being processed. - private void ProcessObjectContent(PSObject inputObject) + private void ProcessInputObjects(PSObject inputObject) { object obj = inputObject.BaseObject; - if (obj is System.IO.FileSystemInfo fsi) + if (obj is FileSystemInfo fsi) { + // Output already processed objects first, then process the file input. + FlushInputBuffer(); string[] path = { fsi.FullName }; List pathsToProcess = ResolvePaths(path, true); ProcessPath(pathsToProcess); return; } - byte[] inputBytes = ConvertToByteArray(obj); + if (obj is string str) + { + // Output already processed objects first, then process the string input. + FlushInputBuffer(); + ProcessString(str); + return; + } + + byte[] inputBytes = ConvertToBytes(obj); + + if (!_groupInput) + { + FlushInputBuffer(); + } if (inputBytes != null) { - int offset = Math.Min(inputBytes.Length, Offset < (long)int.MaxValue ? (int)Offset : int.MaxValue); - int count = Math.Min(inputBytes.Length - offset, Count < (long)int.MaxValue ? (int)Count : int.MaxValue); - if (offset != 0 || count != inputBytes.Length) - { - WriteHexadecimal(inputBytes.AsSpan().Slice(offset, count), null, 0); - } - else - { - WriteHexadecimal(inputBytes, null, 0); - } + _inputBuffer.AddRange(inputBytes); } else { @@ -280,14 +370,9 @@ namespace Microsoft.PowerShell.Commands /// /// The object to convert. /// Returns a byte array of the input values, or null if there is no available conversion path. - private byte[] ConvertToByteArray(object inputObject) + private byte[] ConvertToBytes(object inputObject) { - if (inputObject is string str) - { - return Encoding.GetBytes(str); - } - - var baseType = inputObject.GetType(); + Type baseType = inputObject.GetType(); byte[] result = null; int elements = 1; bool isArray = false; @@ -295,6 +380,10 @@ namespace Microsoft.PowerShell.Commands bool isEnum = false; if (baseType.IsArray) { + FlushInputBuffer(); + _lastInputType = baseType; + _groupInput = false; + baseType = baseType.GetElementType(); dynamic dynamicObject = inputObject; elements = (int)dynamicObject.Length; @@ -309,6 +398,16 @@ namespace Microsoft.PowerShell.Commands if (baseType.IsPrimitive && elements > 0) { + if (_groupInput) + { + if (_lastInputType != null && baseType != _lastInputType) + { + _groupInput = false; + } + + _lastInputType = baseType; + } + if (baseType == typeof(bool)) { isBool = true; @@ -364,21 +463,44 @@ namespace Microsoft.PowerShell.Commands #region Output /// - /// Outputs the hexadecimial representation of the input data. + /// Outputs the hexadecimal representation of the input data. /// - /// Bytes for the hexadecimial representation. + /// Bytes for the hexadecimal representation. /// File path. /// Offset in the file. private void WriteHexadecimal(Span inputBytes, string path, long offset) { - ByteCollection byteCollectionObject = new ByteCollection((ulong)offset, inputBytes.ToArray(), path); - WriteObject(byteCollectionObject); + var bytesPerObject = 16; + for (int index = 0; index < inputBytes.Length; index += bytesPerObject) + { + var count = inputBytes.Length - index < bytesPerObject + ? inputBytes.Length - index + : bytesPerObject; + var bytes = inputBytes.Slice(index, count); + WriteObject(new ByteCollection((ulong)index + (ulong)offset, bytes.ToArray(), path)); + } } - private void WriteHexadecimal(byte[] inputBytes, string path, long offset) + /// + /// Outputs the hexadecimal representation of the input data. + /// + /// Bytes for the hexadecimal representation. + /// Offset in the file. + /// + /// The label for the byte group. This may be a file path, a string value, or a + /// formatted identifying string for the group. + /// + private void WriteHexadecimal(Span inputBytes, long offset, string label) { - ByteCollection byteCollectionObject = new ByteCollection((ulong)offset, inputBytes, path); - WriteObject(byteCollectionObject); + var bytesPerObject = 16; + for (int index = 0; index < inputBytes.Length; index += bytesPerObject) + { + var count = inputBytes.Length - index < bytesPerObject + ? inputBytes.Length - index + : bytesPerObject; + var bytes = inputBytes.Slice(index, count); + WriteObject(new ByteCollection((ulong)index + (ulong)offset, label, bytes.ToArray())); + } } #endregion diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 6e167f6182..f3c9d86a8e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -95,26 +95,24 @@ namespace Microsoft.PowerShell.Commands public class ByteCollection { /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. /// Indicates the path of the file whose contents are wrapped in the ByteCollection. [Obsolete("The constructor is deprecated.", true)] - public ByteCollection(UInt32 offset, byte[] value, string path) + public ByteCollection(uint offset, byte[] value, string path) + : this((ulong)offset, value, path) { - Offset64 = offset; - Bytes = value; - Path = path; } /// - /// Initializes a new instance of ByteCollection. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. /// Indicates the path of the file whose contents are wrapped in the ByteCollection. - public ByteCollection(UInt64 offset, byte[] value, string path) + public ByteCollection(ulong offset, byte[] value, string path) { if (value == null) { @@ -124,31 +122,26 @@ namespace Microsoft.PowerShell.Commands Offset64 = offset; Bytes = value; Path = path; + Label = path; } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. [Obsolete("The constructor is deprecated.", true)] - public ByteCollection(UInt32 offset, byte[] value) + public ByteCollection(uint offset, byte[] value) + : this((ulong)offset, value) { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - Offset64 = offset; - Bytes = value; } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. - public ByteCollection(UInt64 offset, byte[] value) + public ByteCollection(ulong offset, byte[] value) { if (value == null) { @@ -160,7 +153,21 @@ namespace Microsoft.PowerShell.Commands } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. + /// + /// The Offset address to be used while displaying the bytes in the collection. + /// + /// The label for the byte group. This may be a file path or a formatted identifying string for the group. + /// + /// Underlying bytes stored in the collection. + public ByteCollection(ulong offset, string label, byte[] value) + : this(offset, value) + { + Label = label; + } + + /// + /// Initializes a new instance of the class. /// /// Underlying bytes stored in the collection. public ByteCollection(byte[] value) @@ -206,6 +213,82 @@ namespace Microsoft.PowerShell.Commands /// public string Path { get; private set; } + /// + /// Gets the hexadecimal representation of the value. + /// + public string HexOffset { get => string.Format(CultureInfo.CurrentCulture, "{0:X16}", Offset64); } + + /// + /// Gets the type of the input objects used to create the . + /// + public string Label { get; private set; } + + private const int BytesPerLine = 16; + + private string _hexBytes = string.Empty; + + /// + /// Gets a space-delimited string of the in this + /// in hexadecimal format. + /// + public string HexBytes + { + get + { + if (_hexBytes == string.Empty) + { + StringBuilder line = new StringBuilder(BytesPerLine * 3); + + foreach (var currentByte in Bytes) + { + line.AppendFormat(CultureInfo.CurrentCulture, "{0:X2} ", currentByte); + } + + _hexBytes = line.ToString().Trim(); + } + + return _hexBytes; + } + } + + private string _ascii = string.Empty; + + /// + /// Gets the ASCII string representation of the in this . + /// + /// + public string Ascii + { + get + { + if (_ascii == string.Empty) + { + StringBuilder ascii = new StringBuilder(BytesPerLine); + + foreach (var currentByte in Bytes) + { + var currentChar = (char)currentByte; + if (currentChar == 0x0) + { + ascii.Append(' '); + } + else if (char.IsControl(currentChar)) + { + ascii.Append((char)0xFFFD); + } + else + { + ascii.Append(currentChar); + } + } + + _ascii = ascii.ToString(); + } + + return _ascii; + } + } + /// /// Displays the hexadecimal format of the bytes stored in the collection. /// @@ -213,11 +296,11 @@ namespace Microsoft.PowerShell.Commands public override string ToString() { const int BytesPerLine = 16; - const string LineFormat = "{0:X20} "; + const string LineFormat = "{0:X16} "; - // '20 + 3' comes from format "{0:X20} ". - // '20' comes from '[Uint64]::MaxValue.ToString().Length'. - StringBuilder nextLine = new StringBuilder(20 + 3 + (BytesPerLine * 3)); + // '16 + 3' comes from format "{0:X16} ". + // '16' comes from '[Uint64]::MaxValue.ToString("X").Length'. + StringBuilder nextLine = new StringBuilder(16 + 3 + (BytesPerLine * 3)); StringBuilder asciiEnd = new StringBuilder(BytesPerLine); // '+1' comes from 'result.Append(nextLine.ToString() + " " + asciiEnd.ToString());' below. @@ -225,10 +308,8 @@ namespace Microsoft.PowerShell.Commands if (Bytes.Length > 0) { - Int64 charCounter = 0; + long charCounter = 0; - // ToString() in invoked thrice by the F&O for the same content. - // Hence making sure that Offset is not getting incremented thrice for the same bytes being displayed. var currentOffset = Offset64; nextLine.AppendFormat(CultureInfo.InvariantCulture, LineFormat, currentOffset); @@ -240,13 +321,18 @@ namespace Microsoft.PowerShell.Commands // If the character is printable, add its ascii representation to // the right-hand side. Otherwise, add a dot to the right hand side. - if ((currentByte >= 0x20) && (currentByte <= 0xFE)) + var currentChar = (char)currentByte; + if (currentChar == 0x0) { - asciiEnd.Append((char)currentByte); + asciiEnd.Append(' '); + } + else if (char.IsControl(currentChar)) + { + asciiEnd.Append((char)0xFFFD); } else { - asciiEnd.Append('.'); + asciiEnd.Append(currentChar); } charCounter++; @@ -262,7 +348,7 @@ namespace Microsoft.PowerShell.Commands nextLine.AppendFormat(CultureInfo.InvariantCulture, LineFormat, currentOffset); // Adding a newline to support long inputs strings flowing through InputObject parameterset. - if ((charCounter <= Bytes.Length) && string.IsNullOrEmpty(this.Path)) + if ((charCounter <= Bytes.Length) && string.IsNullOrEmpty(Path)) { result.AppendLine(); } diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 6771c91c22..30118933b9 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -18,22 +18,8 @@ namespace System.Management.Automation.Runspaces .EndEntry() .EndControl(); - var ByteCollection_GroupHeader = CustomControl.Create() - .StartEntry() - .StartFrame() - .AddScriptBlockExpressionBinding(@" - $header = "" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"" - if($_.Path) { $header = "" "" + [Microsoft.PowerShell.Commands.UtilityResources]::FormatHexPathPrefix + $_.Path + ""`r`n`r`n"" + $header } - - $header - ") - .EndFrame() - .EndEntry() - .EndControl(); - var sharedControls = new CustomControl[] { - AvailableModules_GroupingFormat, - ByteCollection_GroupHeader + AvailableModules_GroupingFormat }; yield return new ExtendedTypeDefinition( @@ -167,10 +153,6 @@ namespace System.Management.Automation.Runspaces "System.Management.Automation.InformationRecord", ViewsOf_System_Management_Automation_InformationRecord()); - yield return new ExtendedTypeDefinition( - "Microsoft.PowerShell.Commands.ByteCollection", - ViewsOf_Microsoft_PowerShell_Commands_ByteCollection(sharedControls)); - yield return new ExtendedTypeDefinition( "System.Exception", ViewsOf_System_Exception()); @@ -254,6 +236,10 @@ namespace System.Management.Automation.Runspaces yield return new ExtendedTypeDefinition( "Microsoft.PowerShell.MarkdownRender.PSMarkdownOptionInfo", ViewsOf_Microsoft_PowerShell_MarkdownRender_MarkdownOptionInfo()); + + yield return new ExtendedTypeDefinition( + "Microsoft.PowerShell.Commands.ByteCollection", + ViewsOf_Microsoft_PowerShell_Commands_ByteCollection()); } private static IEnumerable ViewsOf_System_RuntimeType() @@ -1077,19 +1063,6 @@ namespace System.Management.Automation.Runspaces .EndControl()); } - private static IEnumerable ViewsOf_Microsoft_PowerShell_Commands_ByteCollection(CustomControl[] sharedControls) - { - yield return new FormatViewDefinition("ByteCollection", - CustomControl.Create() - .GroupByScriptBlock("if($_.Path) { $_.Path } else { $_.GetHashCode() }", customControl: sharedControls[1]) - .StartEntry() - .StartFrame() - .AddScriptBlockExpressionBinding(@"$_.ToString()") - .EndFrame() - .EndEntry() - .EndControl()); - } - private static IEnumerable ViewsOf_System_Exception() { yield return new FormatViewDefinition("Exception", @@ -1630,5 +1603,22 @@ namespace System.Management.Automation.Runspaces .EndEntry() .EndList()); } + + private static IEnumerable ViewsOf_Microsoft_PowerShell_Commands_ByteCollection() + { + yield return new FormatViewDefinition( + "Microsoft.PowerShell.Commands.ByteCollection", + TableControl.Create() + .AddHeader(Alignment.Right, label: "Offset", width: 16) + .AddHeader(Alignment.Left, label: "Bytes\n00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F", width: 47) + .AddHeader(Alignment.Left, label: "Ascii", width: 16) + .StartRowDefinition() + .AddPropertyColumn("HexOffset") + .AddPropertyColumn("HexBytes") + .AddPropertyColumn("Ascii") + .EndRowDefinition() + .GroupByProperty("Label") + .EndTable()); + } } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 index baba808a43..875273acac 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Hex.Tests.ps1 @@ -6,10 +6,8 @@ <# Purpose: Verify Format-Hex displays the Hexadecimal value for the input data. - Action: Run Format-Hex. - Expected Result: Hexadecimal equivalent of the input data is displayed. #> @@ -21,21 +19,27 @@ Describe "FormatHex" -tags "CI" { $newline = [Environment]::Newline Setup -d FormatHexDataDir + $inputFile1 = New-Item -Path "$TestDrive/SourceFile-1.txt" $inputText1 = 'Hello World' + Set-Content -LiteralPath $inputFile1.FullName -Value $inputText1 -NoNewline + + $inputFile2 = New-Item -Path "$TestDrive/SourceFile-2.txt" $inputText2 = 'More text' + Set-Content -LiteralPath $inputFile2.FullName -Value $inputText2 -NoNewline + + $inputFile3 = New-Item -Path "$TestDrive/SourceFile literal [3].txt" $inputText3 = 'Literal path' + Set-Content -LiteralPath $inputFile3.FullName -Value $inputText3 -NoNewline + + $inputFile4 = New-Item -Path "$TestDrive/SourceFile-4.txt" $inputText4 = 'Now is the winter of our discontent' - $inputFile1 = setup -f "FormatHexDataDir/SourceFile-1.txt" -content $inputText1 -pass - $inputFile2 = setup -f "FormatHexDataDir/SourceFile-2.txt" -content $inputText2 -pass - $inputFile3 = setup -f "FormatHexDataDir/SourceFile literal [3].txt" -content $inputText3 -pass - $inputFile4 = setup -f "FormatHexDataDir/SourceFile-4.txt" -content $inputText4 -pass + Set-Content -LiteralPath $inputFile4.FullName -Value $inputText4 -NoNewline $certificateProvider = Get-ChildItem Cert:\CurrentUser\My\ -ErrorAction SilentlyContinue $thumbprint = $null $certProviderAvailable = $false - if ($certificateProvider.Count -gt 0) - { + if ($certificateProvider.Count -gt 0) { $thumbprint = $certificateProvider[0].Thumbprint $certProviderAvailable = $true } @@ -44,100 +48,90 @@ Describe "FormatHex" -tags "CI" { } Context "InputObject Paramater" { + BeforeAll { + enum TestEnum { + TestOne = 1; TestTwo = 2; TestThree = 3; TestFour = 4 + } + Add-Type -TypeDefinition @' +public enum TestSByteEnum : sbyte { + One = -1, + Two = -2, + Three = -3, + Four = -4 +} +'@ + } + $testCases = @( @{ - Name = "Can process byte type 'fhx -InputObject [byte]5'" - InputObject = [byte]5 - Count = 1 + Name = "Can process byte type 'fhx -InputObject [byte]5'" + InputObject = [byte]5 + Count = 1 ExpectedResult = "00000000 05" } @{ - Name = "Can process byte[] type 'fhx -InputObject [byte[]](1,2,3,4,5)'" - InputObject = [byte[]](1,2,3,4,5) - Count = 1 + Name = "Can process byte[] type 'fhx -InputObject [byte[]](1,2,3,4,5)'" + InputObject = [byte[]](1, 2, 3, 4, 5) + Count = 1 ExpectedResult = "00000000 01 02 03 04 05 ....." } @{ - Name = "Can process int type 'fhx -InputObject 7'" - InputObject = 7 - Count = 1 + Name = "Can process int type 'fhx -InputObject 7'" + InputObject = 7 + Count = 1 ExpectedResult = "00000000 07 00 00 00 ...." } @{ - Name = "Can process int[] type 'fhx -InputObject [int[]](5,6,7,8)'" - InputObject = [int[]](5,6,7,8) - Count = 1 + Name = "Can process int[] type 'fhx -InputObject [int[]](5,6,7,8)'" + InputObject = [int[]](5, 6, 7, 8) + Count = 1 ExpectedResult = "00000000 05 00 00 00 06 00 00 00 07 00 00 00 08 00 00 00 ................" } @{ - Name = "Can process int32 type 'fhx -InputObject [int32]2032'" - InputObject = [int32]2032 - Count = 1 + Name = "Can process int32 type 'fhx -InputObject [int32]2032'" + InputObject = [int32]2032 + Count = 1 ExpectedResult = "00000000 F0 07 00 00 ð..." } @{ - Name = "Can process int32[] type 'fhx -InputObject [int32[]](2032, 2033, 2034)'" - InputObject = [int32[]](2032, 2033, 2034) - Count = 1 - ExpectedResult = "00000000000000000000 F0 07 00 00 F1 07 00 00 F2 07 00 00 ð...ñ...ò..." + Name = "Can process int32[] type 'fhx -InputObject [int32[]](2032, 2033, 2034)'" + InputObject = [int32[]](2032, 2033, 2034) + Count = 1 + ExpectedResult = "0000000000000000 F0 07 00 00 F1 07 00 00 F2 07 00 00 ð...ñ...ò..." } @{ - Name = "Can process Int64 type 'fhx -InputObject [Int64]9223372036854775807'" - InputObject = [Int64]9223372036854775807 - Count = 1 - ExpectedResult = "00000000000000000000 FF FF FF FF FF FF FF 7F ......." + Name = "Can process Int64 type 'fhx -InputObject [Int64]9223372036854775807'" + InputObject = [Int64]9223372036854775807 + Count = 1 + ExpectedResult = "0000000000000000 FF FF FF FF FF FF FF 7F ÿÿÿÿÿÿÿ�" } @{ - Name = "Can process Int64[] type 'fhx -InputObject [Int64[]](9223372036852,9223372036853)'" - InputObject = [Int64[]](9223372036852,9223372036853) - Count = 1 - ExpectedResult = "00000000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c...õZÐ{c..." + Name = "Can process Int64[] type 'fhx -InputObject [Int64[]](9223372036852,9223372036853)'" + InputObject = [Int64[]](9223372036852, 9223372036853) + Count = 1 + ExpectedResult = "0000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c...õZÐ{c..." } @{ - Name = "Can process string type 'fhx -InputObject hello world'" - InputObject = "hello world" - Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + Name = "Can process string type 'fhx -InputObject hello world'" + InputObject = "hello world" + Count = 1 + ExpectedResult = "0000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } @{ - Name = "Can process enum type 'fhx -InputObject ([DisplayHintType]::DateTime)'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType]::DateTime - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." + Name = "Can process PS-native enum array '[TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') | fhx'" + InputObject = [TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') + Count = 1 + ExpectedResult = "0000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................" } @{ - Name = "Can process DisplayHintType[] type 'fhx -InputObject ([DisplayHintType[]](DateTime, Time))'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType[]]([Microsoft.PowerShell.Commands.DisplayHintType]::DateTime) - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." - } - @{ - Name = "Can process DisplayHintType[] type 'fhx -InputObject ([DisplayHintType[]](DateTime, Time))'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType[]]([Microsoft.PowerShell.Commands.DisplayHintType]::DateTime, [Microsoft.PowerShell.Commands.DisplayHintType]::Time) - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 01 00 00 00 ........" - } - @{ - Name = "Can process char type 'fhx -InputObject ([char]`'A`')'" - InputObject = [char]'A' - Count = 1 - ExpectedResult = "00000000000000000000 41 A" - } - @{ - Name = "Can process char[] type 'fhx -InputObject ([char[]](`'A`'))'" - InputObject = [char[]]('A') - Count = 1 - ExpectedResult = "00000000000000000000 41 A" - } - @{ - Name = "Can process char[] type 'fhx -InputObject ([char[]](`'A`', 'B'))'" - InputObject = [char[]]('A', 'B') - Count = 1 - ExpectedResult = "00000000000000000000 41 42 AB" + Name = "Can process C#-native sbyte enum array '[TestSByteEnum[]]('One', 'Two', 'Three', 'Four') | fhx'" + InputObject = [TestSByteEnum[]]('One', 'Two', 'Three', 'Four') + Count = 1 + ExpectedResult = "0000000000000000 FF FE FD FC .þýü" } ) - It "" -TestCase $testCases{ + It "" -TestCase $testCases { param ($Name, $InputObject, $Count, $ExpectedResult) @@ -150,120 +144,167 @@ Describe "FormatHex" -tags "CI" { } Context "InputObject From Pipeline" { + BeforeAll { + enum TestEnum { + TestOne = 1; TestTwo = 2; TestThree = 3; TestFour = 4 + } + Add-Type -TypeDefinition @' +public enum TestSByteEnum : sbyte { + One = -1, + Two = -2, + Three = -3, + Four = -4 +} +'@ + } + $testCases = @( @{ - Name = "Can process byte type '[byte]5 | fhx'" - InputObject = [byte]5 - Count = 1 - ExpectedResult = "00000000000000000000 05" + Name = "Can process byte type '[byte]5 | fhx'" + InputObject = [byte]5 + Count = 1 + ExpectedResult = "0000000000000000 05" } @{ - Name = "Can process byte[] type '[byte[]](1,2) | fhx'" - InputObject = [byte[]](1,2) - Count = 2 - ExpectedResult = "00000000000000000000 01 ." - ExpectedSecondResult = "00000000000000000000 02 ." + Name = "Can process byte[] type '[byte[]](1,2) | fhx'" + InputObject = [byte[]](1, 2) + Count = 1 + ExpectedResult = "0000000000000000 01 02 ��" } @{ - Name = "Can process int type '7 | fhx'" - InputObject = 7 - Count = 1 - ExpectedResult = "00000000000000000000 07 00 00 00 ...." + Name = "Can process int type '7 | fhx'" + InputObject = 7 + Count = 1 + ExpectedResult = "0000000000000000 07 00 00 00 � " } @{ - Name = "Can process int[] type '[int[]](5,6) | fhx'" - InputObject = [int[]](5,6) - Count = 2 - ExpectedResult = "00000000000000000000 05 00 00 00 ...." - ExpectedSecondResult = "00000000000000000000 06 00 00 00 ...." + Name = "Can process int[] type '[int[]](5,6) | fhx'" + InputObject = [int[]](5, 6) + Count = 1 + ExpectedResult = "0000000000000000 05 00 00 00 06 00 00 00 � � " } @{ - Name = "Can process int32 type '[int32]2032 | fhx'" - InputObject = [int32]2032 - Count = 1 - ExpectedResult = "00000000000000000000 F0 07 00 00 ð..." + Name = "Can process int32 type '[int32]2032 | fhx'" + InputObject = [int32]2032 + Count = 1 + ExpectedResult = "0000000000000000 F0 07 00 00 ð� " } @{ - Name = "Can process int32[] type '[int32[]](2032, 2033) | fhx'" - InputObject = [int32[]](2032, 2033) - Count = 2 - ExpectedResult = "00000000000000000000 F0 07 00 00 ð..." - ExpectedSecondResult = "00000000000000000000 F1 07 00 00 ñ..." + Name = "Can process int32[] type '[int32[]](2032, 2033) | fhx'" + InputObject = [int32[]](2032, 2033) + Count = 1 + ExpectedResult = "0000000000000000 F0 07 00 00 F1 07 00 00 ð� ñ� " } @{ - Name = "Can process Int64 type '[Int64]9223372036854775807 | fhx'" - InputObject = [Int64]9223372036854775807 - Count = 1 - ExpectedResult = "00000000000000000000 FF FF FF FF FF FF FF 7F ......." + Name = "Can process Int64 type '[Int64]9223372036854775807 | fhx'" + InputObject = [Int64]9223372036854775807 + Count = 1 + ExpectedResult = "0000000000000000 FF FF FF FF FF FF FF 7F ÿÿÿÿÿÿÿ�" } @{ - Name = "Can process Int64[] type '[Int64[]](9223372036852,9223372036853) | fhx'" - InputObject = [Int64[]](9223372036852,9223372036853) - Count = 2 - ExpectedResult = "00000000000000000000 F4 5A D0 7B 63 08 00 00 ôZÐ{c..." - ExpectedSecondResult = "00000000000000000000 F5 5A D0 7B 63 08 00 00 õZÐ{c..." + Name = "Can process Int64[] type '[Int64[]](9223372036852,9223372036853) | fhx'" + InputObject = [Int64[]](9223372036852, 9223372036853) + Count = 1 + ExpectedResult = "0000000000000000 F4 5A D0 7B 63 08 00 00 F5 5A D0 7B 63 08 00 00 ôZÐ{c� õZÐ{c� " } @{ - Name = "Can process string type 'hello world | fhx'" - InputObject = "hello world" - Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + Name = "Can process string type 'hello world | fhx'" + InputObject = "hello world" + Count = 1 + ExpectedResult = "0000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } @{ - Name = "Can process enum type '[DisplayHintType]::DateTime | fhx" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType]::DateTime - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." + Name = "Can process string type amidst other types { 1, 2, 3, 'hello world' | fhx }" + InputObject = 1, 2, 3, "hello world" + Count = 2 + ExpectedResult = "0000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 � � � " + ExpectedSecondResult = "0000000000000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" } @{ - Name = "Can process DisplayHintType[] type '[DisplayHintType[]](DateTime) | fhx'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType[]]([Microsoft.PowerShell.Commands.DisplayHintType]::DateTime) - Count = 1 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." + Name = "Can process jagged array type '[sbyte[]](-15, 18, 21, -5), [byte[]](1, 2, 3, 4, 5, 6) | fhx'" + InputObject = [sbyte[]](-15, 18, 21, -5), [byte[]](1, 2, 3, 4, 5, 6) + Count = 2 + ExpectedResult = "0000000000000000 F1 12 15 FB ñ��û" + ExpectedSecondResult = "0000000000000000 01 02 03 04 05 06 ������" } @{ - Name = "Can process DisplayHintType[] type '[DisplayHintType[]](DateTime, Time) | fhx'" - InputObject = [Microsoft.PowerShell.Commands.DisplayHintType[]]([Microsoft.PowerShell.Commands.DisplayHintType]::DateTime, [Microsoft.PowerShell.Commands.DisplayHintType]::Time) - Count = 2 - ExpectedResult = "00000000000000000000 02 00 00 00 ...." - ExpectedSecondResult = "00000000000000000000 01 00 00 00 ...." + Name = "Can process PS-native enum array '[TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') | fhx'" + InputObject = [TestEnum[]]('TestOne', 'TestTwo', 'TestThree', 'TestFour') + Count = 1 + ExpectedResult = "0000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 � � � � " } @{ - Name = "Can process char type '[char]`'A`' | fhx'" - InputObject = [char]'A' - Count = 1 - ExpectedResult = "00000000000000000000 41 A" - } - @{ - Name = "Can process char[] type '[char[]](`'A`') | fhx'" - InputObject = [char[]]('A') - Count = 1 - ExpectedResult = "00000000000000000000 41 A" - } - @{ - Name = "Can process char[] type '[char[]](`'A`', 'B') | fhx'" - InputObject = [char[]]('A', 'B') - Count = 2 - ExpectedResult = "00000000000000000000 41 A" - ExpectedSecondResult = "00000000000000000000 42 B" + Name = "Can process C#-native sbyte enum array '[TestSByteEnum[]]('One', 'Two', 'Three', 'Four') | fhx'" + InputObject = [TestSByteEnum[]]('One', 'Two', 'Three', 'Four') + Count = 1 + ExpectedResult = "0000000000000000 FF FE FD FC ÿþýü" } ) - It "" -Testcase $testCases { + It "" -TestCases $testCases { param ($Name, $InputObject, $Count, $ExpectedResult, $ExpectedSecondResult) $result = $InputObject | Format-Hex - $result.count | Should -Be $Count + $result.Count | Should -Be $Count $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $result[0].ToString() | Should -MatchExactly $ExpectedResult - if ($result.count -gt 1) - { + if ($result.count -gt 1) { $result[1].ToString() | Should -MatchExactly $ExpectedSecondResult } } + + $heterogenousInputCases = @( + @{ + InputScript = { [sbyte[]](-15, 18, 21, -5), "hello", [byte[]](1..6), 1, 2, 3, 4 } + Count = 4 + ExpectedResults = @( + "0000000000000000 F1 12 15 FB ñ��û" + "0000000000000000 68 65 6C 6C 6F hello" + "0000000000000000 01 02 03 04 05 06 ������" + "0000000000000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 � � � � " + ) + ExpectedLabels = @( + "System.SByte[]" + "System.String" + "System.Byte" + "System.Int32" + ).ForEach{ [regex]::Escape($_) } -join '|' + } + @{ + InputScript = { $inputFile1, "Mountains are merely mountains", 1, 4, 5, 3, [ushort[]](1..10) } + Count = 6 + ExpectedResults = @( + "0000000000000000 48 65 6C 6C 6F 20 57 6F 72 6C 64 Hello World" + "0000000000000000 4D 6F 75 6E 74 61 69 6E 73 20 61 72 65 20 6D 65 Mountains are me" + "0000000000000010 72 65 6C 79 20 6D 6F 75 6E 74 61 69 6E 73 rely mountains" + "0000000000000000 01 00 00 00 04 00 00 00 05 00 00 00 03 00 00 00 � � � � " + "0000000000000000 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 � � � � � � � � " + "0000000000000010 09 00 0A 00 � � " + ) + ExpectedLabels = @( + $inputFile1.FullName + "System.String" + "System.Int32" + "System.UInt16[]" + ).ForEach{ [regex]::Escape($_) } -join '|' + } + ) + + It 'can process jagged input: ' -TestCases $heterogenousInputCases { + param($InputScript, $Count, $ExpectedResults, $ExpectedLabels) + + $Results = & $InputScript | Format-Hex + $Results | Should -HaveCount $Count + $ExpectedResults | Should -HaveCount $Count + + for ($Number = 0; $Number -lt $Results.Count; $Number++) { + $Results[$Number] | Should -MatchExactly $ExpectedResults[$Number] + $Results[$Number].Label | Should -MatchExactly $ExpectedLabels + } + } } Context "Path and LiteralPath Parameters" { @@ -272,39 +313,39 @@ Describe "FormatHex" -tags "CI" { $testCases = @( @{ - Name = "Can process file content from given file path 'fhx -Path `$inputFile1'" - PathCase = $true - Path = $inputFile1 - Count = 1 + Name = "Can process file content from given file path 'fhx -Path `$inputFile1'" + PathCase = $true + Path = $inputFile1 + Count = 1 ExpectedResult = $inputText1 } @{ - Name = "Can process file content from all files in array of file paths 'fhx -Path `$inputFile1, `$inputFile2'" - PathCase = $true - Path = @($inputFile1, $inputFile2) - Count = 2 - ExpectedResult = $inputText1 + Name = "Can process file content from all files in array of file paths 'fhx -Path `$inputFile1, `$inputFile2'" + PathCase = $true + Path = @($inputFile1, $inputFile2) + Count = 2 + ExpectedResult = $inputText1 ExpectedSecondResult = $inputText2 } @{ - Name = "Can process file content from all files when resolved to multiple paths 'fhx -Path '`$testDirectory\SourceFile-*''" - PathCase = $true - Path = "$testDirectory\SourceFile-*" - Count = 2 - ExpectedResult = $inputText1 + Name = "Can process file content from all files when resolved to multiple paths 'fhx -Path '`$testDirectory\SourceFile-*''" + PathCase = $true + Path = "$testDirectory\SourceFile-*" + Count = 2 + ExpectedResult = $inputText1 ExpectedSecondResult = $inputText2 } @{ - Name = "Can process file content from given file path 'fhx -LiteralPath `$inputFile3'" - Path = $inputFile3 - Count = 1 + Name = "Can process file content from given file path 'fhx -LiteralPath `$inputFile3'" + Path = $inputFile3 + Count = 1 ExpectedResult = $inputText3 } @{ - Name = "Can process file content from all files in array of file paths 'fhx -LiteralPath `$inputFile1, `$inputFile3'" - Path = @($inputFile1, $inputFile3) - Count = 2 - ExpectedResult = $inputText1 + Name = "Can process file content from all files in array of file paths 'fhx -LiteralPath `$inputFile1, `$inputFile3'" + Path = @($inputFile1, $inputFile3) + Count = 2 + ExpectedResult = $inputText1 ExpectedSecondResult = $inputText3 } ) @@ -313,62 +354,78 @@ Describe "FormatHex" -tags "CI" { param ($Name, $PathCase, $Path, $ExpectedResult, $ExpectedSecondResult) - if ($PathCase) - { - $result = Format-Hex -Path $Path + if ($PathCase) { + $result = Format-Hex -Path $Path } - else # LiteralPath - { + else { + # LiteralPath $result = Format-Hex -LiteralPath $Path } $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $result[0].ToString() | Should -MatchExactly $ExpectedResult - if ($result.count -gt 1) - { + if ($result.count -gt 1) { $result[1].ToString() | Should -MatchExactly $ExpectedSecondResult } } + + It 'properly accepts -LiteralPath input from a FileInfo object' { + $FilePath = 'TestDrive:\FHX-LitPathTest.txt' + "Hello World!" | Set-Content -Path $FilePath + $FileObject = Get-Item -Path $FilePath + + $result = $FileObject | Format-Hex + if ($IsWindows) { + $Result.Bytes[-1] | Should -Be 0x0A + $Result.Bytes[-2] | Should -Be 0x0D + $Result.Bytes.Length | Should -Be 14 + } + else { + $Result.Bytes[-1] | Should -Be 0x0A + $Result.Bytes.Length | Should -Be 13 + } + } } Context "Encoding Parameter" { $testCases = @( @{ - Name = "Can process ASCII encoding 'fhx -InputObject 'hello' -Encoding ASCII'" - Encoding = "ASCII" - Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" + Name = "Can process ASCII encoding 'fhx -InputObject 'hello' -Encoding ASCII'" + Encoding = "ASCII" + Count = 1 + ExpectedResult = "0000000000000000 68 65 6C 6C 6F hello" } @{ - Name = "Can process BigEndianUnicode encoding 'fhx -InputObject 'hello' -Encoding BigEndianUnicode'" - Encoding = "BigEndianUnicode" - Count = 1 - ExpectedResult = "00000000000000000000 00 68 00 65 00 6C 00 6C 00 6F .h.e.l.l.o" + Name = "Can process BigEndianUnicode encoding 'fhx -InputObject 'hello' -Encoding BigEndianUnicode'" + Encoding = "BigEndianUnicode" + Count = 1 + ExpectedResult = "0000000000000000 00 68 00 65 00 6C 00 6C 00 6F h e l l o" } @{ - Name = "Can process Unicode encoding 'fhx -InputObject 'hello' -Encoding Unicode'" - Encoding = "Unicode" - Count = 1 - ExpectedResult = "00000000000000000000 68 00 65 00 6C 00 6C 00 6F 00 h.e.l.l.o." + Name = "Can process Unicode encoding 'fhx -InputObject 'hello' -Encoding Unicode'" + Encoding = "Unicode" + Count = 1 + ExpectedResult = "0000000000000000 68 00 65 00 6C 00 6C 00 6F 00 h e l l o " } @{ - Name = "Can process UTF7 encoding 'fhx -InputObject 'hello' -Encoding UTF7'" - Encoding = "UTF7" - Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" + Name = "Can process UTF7 encoding 'fhx -InputObject 'hello' -Encoding UTF7'" + Encoding = "UTF7" + Count = 1 + ExpectedResult = "0000000000000000 68 65 6C 6C 6F hello" } - @{ - Name = "Can process UTF8 encoding 'fhx -InputObject 'hello' -Encoding UTF8'" - Encoding = "UTF8" - Count = 1 - ExpectedResult = "00000000000000000000 68 65 6C 6C 6F hello" + @{ + Name = "Can process UTF8 encoding 'fhx -InputObject 'hello' -Encoding UTF8'" + Encoding = "UTF8" + Count = 1 + ExpectedResult = "0000000000000000 68 65 6C 6C 6F hello" } - @{ - Name = "Can process UTF32 encoding 'fhx -InputObject 'hello' -Encoding UTF32'" - Encoding = "UTF32" - Count = 1 - ExpectedResult = "00000000000000000000 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 h...e...l...l...$($newline)00000000000000000010 6F 00 00 00 o..." + @{ + Name = "Can process UTF32 encoding 'fhx -InputObject 'hello' -Encoding UTF32'" + Encoding = "UTF32" + Count = 2 + ExpectedResult = "0000000000000000 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 h e l l " + ExpectedSecondResult = "0000000000000010 6F 00 00 00 o " } ) @@ -390,16 +447,16 @@ Describe "FormatHex" -tags "CI" { $testCases = @( @{ - Name = "Does not support non-FileSystem Provider paths 'fhx -Path 'Cert:\CurrentUser\My\`$thumbprint' -ErrorAction Stop'" - PathParameterErrorCase = $true - Path = "Cert:\CurrentUser\My\$thumbprint" + Name = "Does not support non-FileSystem Provider paths 'fhx -Path 'Cert:\CurrentUser\My\`$thumbprint' -ErrorAction Stop'" + PathParameterErrorCase = $true + Path = "Cert:\CurrentUser\My\$thumbprint" ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" } @{ - Name = "Type Not Supported 'fhx -InputObject @{'hash' = 'table'} -ErrorAction Stop'" - InputObjectErrorCase = $true - Path = $inputFile1 - InputObject = @{ "hash" = "table" } + Name = "Type Not Supported 'fhx -InputObject @{'hash' = 'table'} -ErrorAction Stop'" + InputObjectErrorCase = $true + Path = $inputFile1 + InputObject = @{ "hash" = "table" } ExpectedFullyQualifiedErrorId = "FormatHexTypeNotSupported,Microsoft.PowerShell.Commands.FormatHex" } ) @@ -409,12 +466,10 @@ Describe "FormatHex" -tags "CI" { param ($Name, $PathParameterErrorCase, $Path, $InputObject, $InputObjectErrorCase, $ExpectedFullyQualifiedErrorId) { - if ($PathParameterErrorCase) - { + if ($PathParameterErrorCase) { $result = Format-Hex -Path $Path -ErrorAction Stop } - if ($InputObjectErrorCase) - { + if ($InputObjectErrorCase) { $result = Format-Hex -InputObject $InputObject -ErrorAction Stop } } | Should -Throw -ErrorId $ExpectedFullyQualifiedErrorId @@ -425,20 +480,20 @@ Describe "FormatHex" -tags "CI" { $testCases = @( @{ - Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - PathCase = $true - InvalidPath = "$($inputFile1.DirectoryName)\fakefile8888845345345348709.txt" + Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + PathCase = $true + InvalidPath = "$($inputFile1.DirectoryName)\fakefile8888845345345348709.txt" ExpectedFullyQualifiedErrorId = "FileNotFound,Microsoft.PowerShell.Commands.FormatHex" } @{ - Name = "If given a non FileSystem path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - PathCase = $true - InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + Name = "If given a non FileSystem path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + PathCase = $true + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" } @{ - Name = "If given a non FileSystem path in array (with LiteralPath), continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" - InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + Name = "If given a non FileSystem path in array (with LiteralPath), continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ErrorVariable e -ErrorAction SilentlyContinue'" + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" } ) @@ -450,12 +505,11 @@ Describe "FormatHex" -tags "CI" { $output = $null $errorThrown = $null - if ($PathCase) - { + if ($PathCase) { $output = Format-Hex -Path $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue } - else # LiteralPath - { + else { + # LiteralPath $output = Format-Hex -LiteralPath $InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue } @@ -470,10 +524,10 @@ Describe "FormatHex" -tags "CI" { It "Path is default Parameter Set 'fhx `$inputFile1'" { - $result = Format-Hex $inputFile1 + $result = Format-Hex $inputFile1 $result | Should -Not -BeNullOrEmpty - ,$result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $actualResult = $result.ToString() $actualResult | Should -MatchExactly $inputText1 } @@ -483,7 +537,7 @@ Describe "FormatHex" -tags "CI" { $result = Get-ChildItem $inputFile1 | Format-Hex $result | Should -Not -BeNullOrEmpty - ,$result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + , $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' $actualResult = $result.ToString() $actualResult | Should -MatchExactly $inputText1 } @@ -493,8 +547,9 @@ Describe "FormatHex" -tags "CI" { $result = "a" * 30 | Format-Hex $result | Should -Not -BeNullOrEmpty - ,$result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $result.ToString() | Should -MatchExactly "00000000000000000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa$($newline)00000000000000000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " + $result | Should -BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $result[0].ToString() | Should -MatchExactly "0000000000000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa" + $result[1].ToString() | Should -MatchExactly "0000000000000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " } It "Validate that files do not have buffer underrun problems 'Format-Hex -Path `$InputFile4'" { @@ -503,9 +558,9 @@ Describe "FormatHex" -tags "CI" { $result | Should -Not -BeNullOrEmpty $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " + $result[0].ToString() | Should -MatchExactly "0000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "0000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "0000000000000020 65 6E 74 ent " } } @@ -516,23 +571,22 @@ Describe "FormatHex" -tags "CI" { $result | Should -Not -BeNullOrEmpty $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " + $result[0].ToString() | Should -MatchExactly "0000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "0000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "0000000000000020 65 6E 74 ent " } It "Count = 1" { $result = Format-Hex -Path $inputFile4 -Count 1 - $result.ToString() | Should -MatchExactly "00000000000000000000 4E N " + $result.ToString() | Should -MatchExactly "0000000000000000 4E N " } It "Offset = length" { - $result = Format-Hex -Path $InputFile4 -Offset $inputText4.Length $result | Should -BeNullOrEmpty $result = Format-Hex -InputObject $inputText4 -Offset $inputText4.Length - $result.Bytes | Should -HaveCount 0 + $result | Should -BeNullOrEmpty } It "Offset = 1" { @@ -541,14 +595,14 @@ Describe "FormatHex" -tags "CI" { $result | Should -Not -BeNullOrEmpty $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000001 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 72 ow is the winter" - $result[1].ToString() | Should -MatchExactly "00000000000000000011 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 65 of our disconte" - $result[2].ToString() | Should -MatchExactly "00000000000000000021 6E 74 nt " + $result[0].ToString() | Should -MatchExactly "0000000000000001 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 72 ow is the winter" + $result[1].ToString() | Should -MatchExactly "0000000000000011 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 65 of our disconte" + $result[2].ToString() | Should -MatchExactly "0000000000000021 6E 74 nt " } It "Count = 1 and Offset = 1" { $result = Format-Hex -Path $inputFile4 -Count 1 -Offset 1 - $result.ToString() | Should -MatchExactly "00000000000000000001 6F o " + $result.ToString() | Should -MatchExactly "0000000000000001 6F o " } It "Count should be > 0" { @@ -565,9 +619,9 @@ Describe "FormatHex" -tags "CI" { $result | Should -Not -BeNullOrEmpty $result.Count | Should -Be 3 - $result[0].ToString() | Should -MatchExactly "00000000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" - $result[1].ToString() | Should -MatchExactly "00000000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" - $result[2].ToString() | Should -MatchExactly "00000000000000000020 65 6E 74 ent " + $result[0].ToString() | Should -MatchExactly "0000000000000000 4E 6F 77 20 69 73 20 74 68 65 20 77 69 6E 74 65 Now is the winte" + $result[1].ToString() | Should -MatchExactly "0000000000000010 72 20 6F 66 20 6F 75 72 20 64 69 73 63 6F 6E 74 r of our discont" + $result[2].ToString() | Should -MatchExactly "0000000000000020 65 6E 74 ent " } } }