From 6b96500f66aebe9ece43c9030dd359920e3da1f9 Mon Sep 17 00:00:00 2001 From: "Maria Romero (InConsulting)" Date: Tue, 28 Feb 2017 16:13:04 -0800 Subject: [PATCH 1/2] Implement Format-Hex in C# --- .../FormatAndOutput/format-hex/Format-Hex.cs | 329 ++++++++++++++++++ .../commands/utility/UtilityCommon.cs | 10 - .../resources/UtilityCommonStrings.resx | 6 +- .../Microsoft.PowerShell.Utility.psm1 | 322 ----------------- .../Microsoft.PowerShell.Utility.psd1 | 4 +- .../Microsoft.PowerShell.Utility.psd1 | 4 +- .../Microsoft.PowerShell.Utility.psd1 | 4 +- 7 files changed, 338 insertions(+), 341 deletions(-) create mode 100644 src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs 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 new file mode 100644 index 00000000000..7c88279a534 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-hex/Format-Hex.cs @@ -0,0 +1,329 @@ +using System; +using System.IO; +using System.Text; +using System.Security; +using System.Management.Automation; +using System.Collections.Generic; +using System.Management.Automation.Internal; +// Once Serialization is available on CoreCLR: using System.Runtime.Serialization.Formatters.Binary; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Displays the hexidecimal equivalent of the input data. + /// + [Cmdlet(VerbsCommon.Format, "Hex", SupportsShouldProcess = true, HelpUri ="https://go.microsoft.com/fwlink/?LinkId=526919")] + [Alias ("fhx")] + [OutputType(typeof(Microsoft.PowerShell.Commands.ByteCollection))] + public sealed class FormatHex : PSCmdlet + { + private const int BUFFERSIZE = 16; + + #region Parameters + + /// + /// Path of file(s) to process + /// + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Path")] + [ValidateNotNullOrEmpty()] + public string[] Path { get; set; } + + /// + /// Literal path of file to process + /// + [Parameter(Mandatory = true, ParameterSetName = "LiteralPath")] + [ValidateNotNullOrEmpty()] + [Alias("PSPath")] + public string[] LiteralPath { get; set; } + + /// + /// Ojbect to process + /// + [Parameter(Mandatory = true, ParameterSetName = "ByInputObject", ValueFromPipeline = true)] + public PSObject InputObject { get; set; } + + /// + /// Type of character encoding for InputObject + /// + [Parameter(ParameterSetName = "ByInputObject")] + [ValidateSetAttribute(new string[] { + EncodingConversion.Unicode, + EncodingConversion.BigEndianUnicode, + EncodingConversion.Utf8, + EncodingConversion.Utf7, + EncodingConversion.Utf32, + EncodingConversion.Ascii})] + public string Encoding { get; set; } = "Ascii"; + + /// + /// This parameter is no-op + /// + [Parameter(ParameterSetName = "ByInputObject")] + public SwitchParameter Raw { get; set; } + + #endregion + + #region Overrides + + /// + /// Implements the ProcessRecord method for the FormatHex command. + /// + protected override void ProcessRecord() + { + if (String.Equals(this.ParameterSetName, "ByInputObject", StringComparison.OrdinalIgnoreCase)) + { + ProcessObjectContent(InputObject); + } + else + { + List pathsToProcess = String.Equals(this.ParameterSetName, "LiteralPath", StringComparison.OrdinalIgnoreCase) ? + ResolvePaths(LiteralPath, true) : ResolvePaths(Path, false); + + ProcessPath(pathsToProcess); + } + } + + #endregion + + #region Paths + + /// + /// Validate each path provided and if valid, add to array of paths to process. + /// If path is a literal path it is added to the array to process; we cannot validate them until we + /// try to process file contents. + /// + /// + /// + /// + private List ResolvePaths(string[] path, bool literalPath) + { + List pathsToProcess = new List(); + ProviderInfo provider = null; + PSDriveInfo drive = null; + + foreach (string currentPath in path) + { + List newPaths = new List(); + + if (literalPath) + { + newPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(currentPath, out provider, out drive)); + } + else + { + try + { + newPaths.AddRange(Context.SessionState.Path.GetResolvedProviderPathFromPSPath(currentPath, out provider)); + } + catch (ItemNotFoundException e) + { + if (!WildcardPattern.ContainsWildcardCharacters(currentPath)) + { + ErrorRecord errorRecord = new ErrorRecord(e, "FileNotFound", ErrorCategory.ObjectNotFound, path); + WriteError(errorRecord); + continue; + } + } + } + + if (!provider.Name.Equals("FileSystem", StringComparison.OrdinalIgnoreCase)) + { + // Write a non-terminating error message indicating that path specified is not supported. + string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexOnlySupportsFileSystemPaths, currentPath); + ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(errorMessage), + "FormatHexOnlySupportsFileSystemPaths", + ErrorCategory.InvalidArgument, + currentPath); + WriteError(errorRecord); + continue; + } + + pathsToProcess.AddRange(newPaths); + } + + return pathsToProcess; + } + + /// + /// Pass each valid path on to process its contents. + /// + /// + private void ProcessPath(List pathsToProcess) + { + foreach (string path in pathsToProcess) + { + ProcessFileContent(path); + } + } + + /// + /// Creates a binary reader that reads the file content into a buffer (byte[]) 16 bytes at a time, and + /// passes a copy of that array on to the ConvertToHexidecimal method to output. + /// + /// + private void ProcessFileContent(string path) + { + byte[] buffer = new byte[BUFFERSIZE]; + + try + { + using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read))) + { + UInt32 offset = 0; + Int32 bytesRead = 0; + + while ((bytesRead = reader.Read(buffer, 0, BUFFERSIZE)) > 0) + { + if (bytesRead == BUFFERSIZE) + { + // We are reusing the same buffer so if we save the output to a variable, the variable + // will just contain multiple references to the same buffer memory space (containing only the + // last bytes of the file read). Copying the buffer allows us to pass the values on without + // overwriting previous values. + byte[] copyOfBuffer = new byte[16]; + Array.Copy(buffer, 0, copyOfBuffer, 0, bytesRead); + ConvertToHexidecimal(copyOfBuffer, path, offset); + } + else + { + // Handle the case of a partial (and probably last) buffer. Copies the bytes read into a new, + // shorter array so we do not have the extra bytes from the previous pass through at the end. + byte[] remainingBytes = new byte[bytesRead]; + Array.Copy(buffer, 0, remainingBytes, 0, bytesRead); + ConvertToHexidecimal(remainingBytes, path, offset); + } + // Update offset value. + offset += (UInt32)bytesRead; + } + } + } + catch (IOException ioException) + { + // IOException takes care of FileNotFoundException, DirectoryNotFoundException, and PathTooLongException + WriteError(new ErrorRecord(ioException, "FormatHexIOError", ErrorCategory.WriteError, path)); + } + catch (ArgumentException argException) + { + WriteError(new ErrorRecord(argException, "FormatHexArgumentError", ErrorCategory.WriteError, path)); + } + catch (NotSupportedException notSupportedException) + { + WriteError(new ErrorRecord(notSupportedException, "FormatHexPathRefersToANonFileDevice", ErrorCategory.InvalidArgument, path)); + } + catch (SecurityException securityException) + { + WriteError(new ErrorRecord(securityException, "FormatHexUnauthorizedAccessError", ErrorCategory.PermissionDenied, path)); + } + } + + #endregion + + #region InputObjects + + /// + /// Creates a byte array from the object passed to the cmdlet (based on type) and passes + /// that array on to the ConvertToHexidecimal method to output. + /// + /// + private void ProcessObjectContent(PSObject inputObject) + { + Object obj = inputObject.BaseObject; + byte[] inputBytes = null; + if (obj is System.IO.FileSystemInfo) + { + string[] path = { ((FileSystemInfo)obj).FullName }; + List pathsToProcess = ResolvePaths(path, true); + ProcessPath(pathsToProcess); + } + + else if (obj is string) + { + string inputString = obj.ToString(); + Encoding resolvedEncoding = EncodingConversion.Convert(this, Encoding); + inputBytes = resolvedEncoding.GetBytes(inputString); + } + + else if (obj is byte) + { + inputBytes = new byte[] { (byte)obj }; + } + + else if (obj is byte[]) + { + inputBytes = ((byte[])obj); + } + + else if (obj is Int32) + { + inputBytes = BitConverter.GetBytes((Int32)obj); + } + + else if (obj is Int32[]) + { + List inputStreamArray = new List(); + Int32[] inputInts = (Int32[])obj; + foreach (Int32 value in inputInts) + { + byte[] tempBytes = BitConverter.GetBytes(value); + inputStreamArray.AddRange(tempBytes); + } + inputBytes = inputStreamArray.ToArray(); + } + + else if (obj is Int64) + { + inputBytes = BitConverter.GetBytes((Int64)obj); + } + + else if (obj is Int64[]) + { + List inputStreamArray = new List(); + Int64[] inputInts = (Int64[])obj; + foreach (Int64 value in inputInts) + { + byte[] tempBytes = BitConverter.GetBytes(value); + inputStreamArray.AddRange(tempBytes); + } + inputBytes = inputStreamArray.ToArray(); + } + + // If the object type is not supported, throw an error. Once Serialization is + // available on CoreCLR, other types will be supported. + else + { + string errorMessage = StringUtil.Format(UtilityCommonStrings.FormatHexTypeNotSupported, obj.GetType()); + ErrorRecord errorRecord = new ErrorRecord(new ArgumentException(errorMessage), + "FormatHexTypeNotSupported", + ErrorCategory.InvalidArgument, + obj.GetType()); + WriteError(errorRecord); + } + + if (inputBytes != null) + { + ConvertToHexidecimal(inputBytes, null, 0); + } + } + + #endregion + + #region Output + + /// + /// Outputs the hexadecimial representaion of the of the input data. + /// + /// + /// + /// + private void ConvertToHexidecimal(byte[] inputBytes, string path, UInt32 offset) + { + if (inputBytes != null) + { + ByteCollection byteCollectionObject = new ByteCollection(offset, inputBytes, path); + WriteObject(byteCollectionObject); + } + } + + #endregion + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 3d855c425e1..63cb01ea9a3 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -124,16 +124,6 @@ public static class UtilityResources /// public static string FileReadError { get { return UtilityCommonStrings.FileReadError; } } - /// - /// Error message to indicate that Format-Hex cmdlet does not directly support the type provided as input. - /// - public static string FormatHexTypeNotSupported { get { return UtilityCommonStrings.FormatHexTypeNotSupported; } } - - /// - /// Error message to indicate that Format-Hex cmdlet does not directly support the type provided as input. - /// - public static string FormatHexResolvePathError { get { return UtilityCommonStrings.FormatHexResolvePathError; } } - /// /// The resource string used to indicate 'PATH:' in the formating header. /// diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityCommonStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityCommonStrings.resx index dd157fb7169..24bcc7b970d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityCommonStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityCommonStrings.resx @@ -145,10 +145,10 @@ The file '{0}' cannot be read: {1} - Cannot convert input of type {0} to hexadecimal. To view the hexadecimal formatting of its string representation, pipe it to the Out-String cmdlet before piping it to Format-Hex. + Cannot convert input of type '{0}' to hexadecimal. To view the hexadecimal formatting of its string representation, pipe it to the Out-String cmdlet before piping it to Format-Hex. - - Cannot display the context of {0} as hex. The path resolves to multiple files. + + The given path '{0}' is not supported. This command only supports the FileSystem Provider paths. Path: diff --git a/src/Modules/Shared/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1 b/src/Modules/Shared/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1 index 1f5acbe8632..322c671fcb0 100644 --- a/src/Modules/Shared/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1 +++ b/src/Modules/Shared/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1 @@ -1,325 +1,3 @@ -<############################################################################################ -# Format-Hex cmdlet helps in displaying the Hexadecimal equivalent of the input data. -############################################################################################> -function Format-Hex -{ - [CmdletBinding( - DefaultParameterSetName="Path", - HelpUri="https://go.microsoft.com/fwlink/?LinkId=526919")] - [Alias("fhx")] - [OutputType("Microsoft.PowerShell.Commands.ByteCollection")] - param - ( - [Parameter (Mandatory=$true, Position=0, ParameterSetName="Path")] - [ValidateNotNullOrEmpty()] - [string[]] $Path, - - [Parameter (Mandatory=$true, ParameterSetName="LiteralPath")] - [ValidateNotNullOrEmpty()] - [Alias("PSPath")] - [string[]] $LiteralPath, - - [Parameter(Mandatory=$true, ParameterSetName="ByInputObject", ValueFromPipeline=$true)] - [Object] $InputObject, - - [Parameter (ParameterSetName="ByInputObject")] - [ValidateSet("Ascii", "UTF32", "UTF7", "UTF8", "BigEndianUnicode", "Unicode")] - [string] $Encoding = "Ascii", - - [Parameter(ParameterSetName="ByInputObject")] - [switch]$Raw - ) - - begin - { - $bufferSize = 16 - $inputStreamArray = [System.Collections.ArrayList]::New() - <############################################################################################ - # The ConvertToHexadecimalHelper is a helper method used to fetch unicode bytes from the - # input data and display the hexadecimal representation of the of the input data in bytes. - ############################################################################################> - function ConvertToHexadecimalHelper - { - param - ( - [Byte[]] $inputBytes, - [string] $path, - [Uint32] $offset - ) - - # This section is used to display the hexadecimal - # representation of the of the input data in bytes. - if($inputBytes -ne $null) - { - $byteCollectionObject = [Microsoft.PowerShell.Commands.ByteCollection]::new($offset, $inputBytes, $path) - Write-Output -InputObject $byteCollectionObject - } - } - - <############################################################################################ - # The ProcessFileContent is a helper method used to fetch file contents in blocks and - # process it to support displaying hexadecimal formating of the fetched content. - ############################################################################################> - function ProcessFileContent - { - param - ( - [string] $filePath, - [boolean] $isLiteralPath - ) - - if($isLiteralPath) - { - $resolvedPaths = Resolve-Path -LiteralPath $filePath - } - else - { - $resolvedPaths = Resolve-Path -Path $filePath - } - - # If Path resolution has failed then a corresponding non-terminating error is - # written to the pipeline. We continue processing any remaining files. - if($resolvedPaths -eq $null) - { - return - } - - if($resolvedPaths.Count -gt 1) - { - # write a non-terminating error message indicating that path specified is resolving to multiple file system paths. - $errorMessage = [Microsoft.PowerShell.Commands.UtilityResources]::FormatHexResolvePathError -f $filePath - Write-Error -Message $errorMessage -Category ([System.Management.Automation.ErrorCategory]::InvalidData) -ErrorId "FormatHexResolvePathError" - } - - $targetFilePath = $resolvedPaths.ProviderPath - - - if($targetFilePath -ne $null) - { - $buffer = [byte[]]::new($bufferSize) - - try - { - try - { - $currentFileStream = [System.IO.File]::Open($targetFilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) - } - catch - { - # Failed to access the file. Write a non terminating error to the pipeline - # and move on with the remaining files. - $exception = $_.Exception - if($null -ne $_.Exception -and - $null -ne $_.Exception.InnerException) - { - $exception = $_.Exception.InnerException - } - - $errorRecord = [System.Management.Automation.ErrorRecord]::new($exception,"FormatHexFileAccessError", ([System.Management.Automation.ErrorCategory]::ReadError), $targetFilePath) - $PSCmdlet.WriteError($errorRecord) - } - - if($null -ne $currentFileStream) - { - $srcStream = [System.IO.BinaryReader]::new($currentFileStream) - $displayHeader = $true - $offset = 0 - $blockCounter = 0 - while($numberOfBytesRead = $srcStream.Read($buffer, 0, $bufferSize)) - { - # send only the bytes that have been read - # if we send the whole buffer, we'll have extraneous bytes - # at the end of an incomplete group of 16 bytes - if ( $numberOfBytesRead -eq $bufferSize ) - { - # under some circumstances if we don't copy the buffer - # and the results are stored to a variable, the results are not - # correct and one object replicated in all the output objects - ConvertToHexadecimalHelper ($buffer.Clone()) $targetFilePath $offset - } - else - { - # handle the case of the partial (and probably last) buffer - $bytesReadBuffer = [byte[]]::New($numberOfBytesRead) - [Array]::Copy($buffer,0, $bytesReadBuffer,0,$numberOfBytesRead) - ConvertToHexadecimalHelper $bytesReadBuffer $targetFilePath $offset - } - $displayHeader = $false - $blockCounter++; - - # Updating the offset value. - $offset = $blockCounter*0x10 - } - } - } - finally - { - If($null -ne $currentFileStream) - { - $currentFileStream.Dispose() - } - If($null -ne $srcStream) - { - $srcStream.Dispose() - } - } - } - } - } - - process - { - switch($PSCmdlet.ParameterSetName) - { - "Path" - { - ProcessFileContent $Path $false - } - "LiteralPath" - { - ProcessFileContent $LiteralPath $true - } - "ByInputObject" - { - # If it's an actual byte array, then we directly use it for hexadecimal formatting. - if($InputObject -is [Byte[]]) - { - ConvertToHexadecimalHelper $InputObject $null - return - } - # if it's a single byte, we'll assume streaming - elseif($InputObject -is [byte]) - { - $null = $inputStreamArray.Add($InputObject) - } - # If the input data is of string type then directly get bytes out of it. - elseif($InputObject -is [string]) - { - # The ValidateSet attribute on the Encoding parameter makes sure that only - # valid values (supported on all platforms where Format-Hex is available) - # are allowed through user input. - $inputBytes = [Text.Encoding]::$Encoding.GetBytes($InputObject) - ConvertToHexadecimalHelper $inputBytes $null - return - } - elseif($InputObject -is [System.IO.FileSystemInfo]) - { - # If file path is provided as an input, use the file contents to show the hexadecimal format. - $filePath = ([System.IO.FileSystemInfo]$InputObject).FullName - ProcessFileContent $filePath $false - return - } - elseif($InputObject -is [int64]) - { - $inputBytes = [BitConverter]::GetBytes($InputObject) - $null = $inputStreamArray.AddRange($inputBytes) - } - elseif($InputObject -is [int64[]]) - { - foreach($i64 in $InputObject) - { - $inputBytes = [BitConverter]::GetBytes($i64) - $null = $inputStreamArray.AddRange($inputBytes) - } - } - elseif($InputObject -is [int]) - { - # If we get what appears as ints, it may not be what the user really wants. - # for example, if the user types a small set of numbers just to get their - # character representations, as follows: - # - # 170..180 | format-hex - # Path: - # 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F - #00000000 AA AB AC AD AE AF B0 B1 B2 B3 B4 - # - # any integer padding is likely to be more confusing than this - # fairly compact representation. - # - # However, some might like to see the results with the raw data, - # -Raw exists to provide that behavior: - # PS# 170..180 | format-hex -Raw - # - # Path: - # - # 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F - # - # 00000000 AA 00 00 00 AB 00 00 00 AC 00 00 00 AD 00 00 00 ............ - # 00000010 AE 00 00 00 AF 00 00 00 B0 00 00 00 B1 00 00 00 ............ - # 00000020 B2 00 00 00 B3 00 00 00 B4 00 00 00 ......... - # - # this provides a representation of the piped numbers which includes all - # of the bytes which are in an int32 - if ( $Raw ) - { - $inputBytes = [BitConverter]::GetBytes($InputObject) - $null = $inputStreamArray.AddRange($inputBytes) - } - else - { - # first determine whether we can represent this as a byte - $possibleByte = $InputObject -as [byte] - # first determine whether we can represent this as a int16 - $possibleInt16 = $InputObject -as [int16] - if ( $possibleByte -ne $null ) - { - $null = $inputStreamArray.Add($possibleByte) - } - elseif ( $possibleint16 -ne $null ) - { - $inputBytes = [BitConverter]::GetBytes($possibleInt16) - $null = $inputStreamArray.AddRange($inputBytes) - } - else - { - # now int - $inputBytes = [BitConverter]::GetBytes($InputObject) - $null = $inputStreamArray.AddRange($inputBytes) - } - } - } - else - { - # Otherwise, write a non-terminating error message indicating that input object type is not supported. - $errorMessage = [Microsoft.PowerShell.Commands.UtilityResources]::FormatHexTypeNotSupported -f $InputObject.GetType() - Write-Error -Message $errorMessage -Category ([System.Management.Automation.ErrorCategory]::ParserError) -ErrorId "FormatHexFailureTypeNotSupported" - } - # Handle streaming case here - # during this process we may not have enough characters to create a ByteCollection - # if we do, create as many ByteCollections as necessary, each being 16 bytes in length - if ( $inputStreamArray.Count -ge $bufferSize ) - { - $rowCount = [math]::Floor($inputStreamArray.Count/$bufferSize) - $arrayLength = $bufferSize * $rowCount - for($i = 0; $i -lt $rowCount; $i++) - { - $rowOffset = $i * $bufferSize - ConvertToHexadecimalHelper -inputBytes $inputStreamArray.GetRange($rowOffset, $bufferSize) -path ' ' -offset $offset - $offset += $bufferSize - } - # We use RemoveRange because of the pathological case of having - # streamed combination of bytes, int16, int32, int64 which are greater - # than 16 bytes. Consider the case: - # $i = [int16]::MaxValue + 3 - # $i64=[int64]::MaxValue -5 - # .{ $i;$i;$i;$i64 } | format-hex - # which create an arraylist 20 bytes - # we need to remove only the bytes from the array which we emitted - $inputStreamArray.RemoveRange(0,$arrayLength) - } - } - } - } - end - { - # now manage any left over bytes in the $inputStreamArray - if ( $PSCmdlet.ParameterSetName -eq "ByInputObject" ) - { - ConvertToHexadecimalHelper $inputStreamArray $null -path ' ' -offset $offset - } - } - -} ## Converts a SDDL string into an object-based representation of a security ## descriptor diff --git a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 03ab08635a1..a5f20d41b1c 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -22,8 +22,8 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", "Get-PSBreakpoint", "Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-FileHash", "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", - "Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "New-TemporaryFile", "Get-Verb" -FunctionsToExport= "Format-Hex", "Import-PowerShellDataFile" + "Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "New-TemporaryFile", "Get-Verb", "Format-Hex" +FunctionsToExport= "Import-PowerShellDataFile" AliasesToExport= "fhx" NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1" HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390787' diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index d42ab9be6ee..f601e2d50f8 100644 --- a/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -22,8 +22,8 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", "Get-PSBreakpoint", "Remove-PSBreakpoint", "New-TemporaryFile", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-FileHash", "Unblock-File", "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", - "Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "Get-Verb" -FunctionsToExport= "Format-Hex", "ConvertFrom-SddlString" + "Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "Get-Verb", "Format-Hex" +FunctionsToExport= "ConvertFrom-SddlString" AliasesToExport= "fhx" NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1" HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390787' diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 62eafabfdc8..25008e2703a 100644 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -24,8 +24,8 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", "Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack", "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Show-Command", "Unblock-File", "Get-FileHash", "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", "Get-RunspaceDebug", "Wait-Debugger", - "ConvertFrom-String", "Convert-String" , "Get-Uptime", "New-TemporaryFile", "Get-Verb" -FunctionsToExport= "Format-Hex", "ConvertFrom-SddlString" + "ConvertFrom-String", "Convert-String" , "Get-Uptime", "New-TemporaryFile", "Get-Verb", "Format-Hex" +FunctionsToExport= "ConvertFrom-SddlString" AliasesToExport= "CFS", "fhx" NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1" HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390787' From 738264027f249952a0d209396bb44d0d6ccdecb1 Mon Sep 17 00:00:00 2001 From: "Maria Romero (InConsulting)" Date: Mon, 13 Mar 2017 09:50:39 -0700 Subject: [PATCH 2/2] Update Format-Hex test cases and add new tests for added functionality --- .../FormatHex.Tests.ps1 | 584 +++++++++++++----- 1 file changed, 427 insertions(+), 157 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 index 961bfaeaf93..d9f3669c6a5 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/FormatHex.Tests.ps1 @@ -5,219 +5,489 @@ <# Purpose: - Verify that Format-Hex display the Hexa decimal value for the input data. + Verify Format-Hex displays the Hexadecimal value for the input data. Action: - Run Format-Fex. + Run Format-Hex. Expected Result: - Hexa decimal equivalent of the input data is displayed. + Hexadecimal equivalent of the input data is displayed. #> -Describe "FormatHex" -tags "CI" { - BeforeAll { - Setup -d FormatHexDataDir - $inputText1 = 'Hello World' - $inputText2 = 'This is a bit more text' - $inputFile1 = setup -f "FormatHexDataDir/SourceFile-1.txt" -content $inputText1 -pass - $inputFile2 = setup -f "FormatHexDataDir/SourceFile-2.txt" -content $inputText2 -pass - } +function RunInputObjectTestCase($testCase) +{ + It "$($testCase.Name)" { - # This test is to validate to pipeline support in Format-Hex cmdlet. - It "ValidatePipelineSupport" { + $result = Format-Hex -InputObject $testCase.InputObject - # InputObject Parameter set should get invoked and - # the input data should be treated as string. - $result = $inputText1 | Format-Hex $result | Should Not Be $null - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - ($actualResult -match $inputText1) | Should Be $true + $result.count | Should Be $testCase.Count + $result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $result.ToString() | Should Match $testCase.ExpectedResult } +} - # This test is to validate to pipeline support in Format-Hex cmdlet. - It "ValidateByteArrayInputSupport" { +function RunObjectFromPipelineTestCase($testCase) +{ + It "$($testCase.Name)" { - # InputObject Parameter set should get invoked and - # the input data should be treated as byte[]. - $inputBytes = [System.Text.Encoding]::ASCII.GetBytes($inputText1) + $result = $testCase.InputObject | Format-Hex - $result = Format-Hex -InputObject $inputBytes $result | Should Not Be $null - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - ($actualResult -match $inputText1) | Should Be $true + $result.count | Should Be $testCase.Count + $result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $result[0].ToString() | Should Match $testCase.ExpectedResult + + if ($result.count > 1) + { + $result[1].ToString() | Should Match $testCase.ExpectedSecondResult + } } +} - # This test is to validate to input given through Path parameter set in Format-Hex cmdlet. - It "ValidatePathParameterSet" { +function RunPathAndLiteralPathParameterTestCase($testCase) +{ + It "$($testCase.Name)" { + + if ($testCase.PathCase) + { + $result = Format-Hex -Path $testCase.Path + } + else # LiteralPath + { + $result = Format-Hex -LiteralPath $testCase.Path + } - $result = Format-Hex -Path $inputFile1 $result | Should Not Be $null - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - ($actualResult -match $inputText1) | Should Be $true + $result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $result[0].ToString() | Should Match $testCase.ExpectedResult + + if ($result.count > 1) + { + $result[1].ToString() | Should Match $testCase.ExpectedSecondResult + } } +} - # This test is to validate to Path parameter set is considered as default in Format-Hex cmdlet. - It "ValidatePathAsDefaultParameterSet" { +function RunEncodingTestCase($testCase) +{ + It "$($testCase.Name)" { - $result = Format-Hex $inputFile1 - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - ($actualResult -match $inputText1) | Should Be $true + $result = Format-Hex -InputObject 'hello' -Encoding $testCase.Encoding + + $result | Should Not Be $null + $result.count | Should Be $testCase.Count + $result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $result[0].ToString() | Should Match $testCase.ExpectedResult } +} - # This test is to validate to input given through LiteralPath parameter set in Format-Hex cmdlet. - It "ValidateLiteralPathParameterSet" { +function RunContinuesToProcessCase($testCase) +{ + $skipTest = ([System.Management.Automation.Platform]::IsLinux -or [System.Management.Automation.Platform]::IsOSX) - $result = Format-Hex -LiteralPath $inputFile1 - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - ($actualResult -match $inputText1) | Should Be $true - } + It "$($testCase.Name)" -Skip:$($skipTest -or (-not $certProviderAvailable)) { - # This test is to validate to input given through pipeline. The input being piped from results of Get-hildItem - It "ValidateFileInfoPipelineInput" { + $output = $null + $errorThrown = $null - $result = Get-ChildItem $inputFile1 | Format-Hex - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - ($actualResult -match $inputText1) | Should Be $true - } + if ($testCase.PathCase) + { + $output = Format-Hex -Path $testCase.InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue + } + else # LiteralPath + { + $output = Format-Hex -LiteralPath $testCase.InvalidPath, $inputFile1 -ErrorVariable errorThrown -ErrorAction SilentlyContinue + } - # This test is to validate Encoding formats functionality of Format-Hex cmdlet. - It "ValidateEncodingFormats" { + $errorThrown | Should Not Be $null + $errorThrown.FullyQualifiedErrorId | Should Match $testCase.ExpectedFullyQualifiedErrorId - $result = Format-Hex -InputObject $inputText1 -Encoding ASCII - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - ($actualResult -match $inputText1) | Should Be $true + $output | Should Not Be $null + $output[0].ToString() | Should Match $inputText1 } +} - # This test is to validate that integers can be piped to the format-hex - It "ValidateIntegerInput" { +function RunExpectedErrorTestCase($testCase) +{ + $skipTest = ([System.Management.Automation.Platform]::IsLinux -or [System.Management.Automation.Platform]::IsOSX) - $result = 1,2,3,4 | Format-Hex - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - # whitespace sensitive - $actualResult | should be "00000000 01 02 03 04 .... " - } + It "$($testCase.Name)" -Skip:$($skipTest -or (-not $certProviderAvailable)) { - # This test is to validate that integers can be piped to the format-hex - # and properly represented as characters in the string - It "ValidateIntegerInputThatPresentAsCharacters" { + try + { + if ($testCase.PathParameterErrorCase) + { + $result = Format-Hex -Path $testCase.Path -ErrorAction Stop + } + if ($testCase.InputObjectErrorCase) + { + $result = Format-Hex -InputObject $testCase.InputObject -ErrorAction Stop + } + } + catch + { + $thrownError = $_ + } - $result = 65..68 | Format-Hex - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - # whitespace sensitive - $actualResult | should be "00000000 41 42 43 44 ABCD " + $thrownError | Should Not Be $null + $thrownError.FullyQualifiedErrorId | Should Match $testCase.ExpectedFullyQualifiedErrorId } +} + +Describe "FormatHex" -tags "CI" { + + BeforeAll { - # This test is to validate that integers can be piped to the format-hex - It "ValidateIntegerRawInput" { + Setup -d FormatHexDataDir + $inputText1 = 'Hello World' + $inputText2 = 'This is a bit more text' + $inputText3 = 'Literal path' + $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 - $result = 1,2,3,4 | Format-Hex -Raw - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - # whitespace sensitive - $actualResult | should be "00000000 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................" + $certificateProvider = Get-ChildItem Cert:\CurrentUser\My\ -ErrorAction SilentlyContinue + $thumbprint = $null + $certProviderAvailable = $false + + if ($certificateProvider.Count -gt 0) + { + $thumbprint = $certificateProvider[0].Thumbprint + $certProviderAvailable = $true + } } - # handle int64 - It "ValidateInteger64" { + Context "InputObject Paramater" { + $testCases = @( + @{ + 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 + ExpectedResult = "00000000 01 02 03 04 05 ....." + } + @{ + 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 + 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 + 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 = "00000000 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 = "00000000 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 = "00000000 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 = "00000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + } + ) + + foreach ($testCase in $testCases) + { + RunInputObjectTestCase $testCase + } + } - $result = [int64]::MaxValue | Format-Hex - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - # whitespace sensitive - $actualResult | Should Match "00000000 FF FF FF FF FF FF FF 7F ." + Context "InputObject From Pipeline" { + $testCases = @( + @{ + Name = "Can process byte type '[byte]5 | fhx'" + InputObject = [byte]5 + Count = 1 + ExpectedResult = "00000000 05" + } + @{ + Name = "Can process byte[] type '[byte[]](1,2) | fhx'" + InputObject = [byte[]](1,2) + Count = 2 + ExpectedResult = "00000000 01 ." + ExpectedSecondResult = "00000000 02 ." + } + @{ + Name = "Can process int type '7 | fhx'" + InputObject = 7 + Count = 1 + ExpectedResult = "00000000 07 00 00 00 ...." + } + @{ + Name = "Can process int[] type '[int[]](5,6) | fhx'" + InputObject = [int[]](5,6) + Count = 2 + ExpectedResult = "00000000 05 00 00 00 ...." + ExpectedSecondResult = "00000000 06 00 00 00 ...." + } + @{ + Name = "Can process int32 type '[int32]2032 | fhx'" + InputObject = [int32]2032 + Count = 1 + ExpectedResult = "00000000 F0 07 00 00 ð..." + } + @{ + Name = "Can process int32[] type '[int32[]](2032, 2033) | fhx'" + InputObject = [int32[]](2032, 2033) + Count = 2 + ExpectedResult = "00000000 F0 07 00 00 ð..." + ExpectedSecondResult = "00000000 F1 07 00 00 ñ..." + } + @{ + Name = "Can process Int64 type '[Int64]9223372036854775807 | fhx'" + InputObject = [Int64]9223372036854775807 + Count = 1 + ExpectedResult = "00000000 FF FF FF FF FF FF FF 7F ......." + } + @{ + Name = "Can process Int64[] type '[Int64[]](9223372036852,9223372036853) | fhx'" + InputObject = [Int64[]](9223372036852,9223372036853) + Count = 2 + ExpectedResult = "00000000 F4 5A D0 7B 63 08 00 00 ôZÐ{c..." + ExpectedSecondResult = "00000000 F5 5A D0 7B 63 08 00 00 õZÐ{c..." + } + @{ + Name = "Can process string type 'hello world | fhx'" + InputObject = "hello world" + Count = 1 + ExpectedResult = "00000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 hello world" + } + ) + + foreach ($testCase in $testCases) + { + RunObjectFromPipelineTestCase $testCase + } } - # handle bytes, int16, int32, and int64 - It "Validate combined and reduced number formatting" { - $b = 65 # fits in a byte - $i16 = 32767 # fits in an int16 - $i32 = 2147483647 # an int32 - $i64 = 9223372036854775807 # an int64 - $result = $b,$i16,$i32,$i64 | format-Hex - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() - $actualResult | should Match "00000000 41 FF 7F FF FF FF 7F FF FF FF FF FF FF FF 7F A" + Context "Path Paramater" { + + $testDirectory = $inputFile1.DirectoryName + + $testCases = @( + @{ + 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 + 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 + ExpectedSecondResult = $inputText2 + } + ) + + foreach ($testCase in $testCases) + { + RunPathAndLiteralPathParameterTestCase $testCase + } } - # handle bytes, int16, int32, and int64 - It "Validate combined and with raw number formatting" { - $b = 65 # fits in a byte - $i16 = 32767 # fits in an int16 - $i32 = 2147483647 # an int32 - $i64 = 9223372036854775807 # an int64 - # this will cause 2 lines to be emitted - $result = $b,$i16,$i32,$i64 | format-Hex -Raw - $result | Should Not BeNullOrEmpty - $result[0] | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $result[1] | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $result0 = $result[0].ToString() - $result0 | should match "00000000 41 00 00 00 FF 7F 00 00 FF FF FF 7F FF FF FF FF A" - $result1 = $result[1].ToString() - $result1 | should match "00000010 FF FF FF 7F .." + Context "LiteralPath Paramater" { + $testCases = @( + @{ + 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 + ExpectedSecondResult = $inputText3 + } + ) + + foreach ($testCase in $testCases) + { + RunPathAndLiteralPathParameterTestCase $testCase + } } - # This test is to validate that streamed text does not have buffer underrun problems - It "ValidateEachBufferHasCorrectContentForStreamingText" { - $result = "a" * 30 | Format-Hex - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - $actualResult = $result.ToString() -split "`r`n" - $actualResult.Count | should be 2 - $actualResult[0].ToString() | Should be "00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa" - $actualResult[1].ToString() | Should be "00000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaa " + Context "Encoding Parameter" { + $testCases = @( + @{ + Name = "Can process ASCII encoding 'fhx -InputObject 'hello' -Encoding ASCII'" + Encoding = "ASCII" + Count = 1 + ExpectedResult = "00000000 68 65 6C 6C 6F hello" + } + @{ + Name = "Can process BigEndianUnicode encoding 'fhx -InputObject 'hello' -Encoding BigEndianUnicode'" + Encoding = "BigEndianUnicode" + Count = 1 + ExpectedResult = "00000000 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 = "00000000 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 = "00000000 68 65 6C 6C 6F hello" + } + @{ + Name = "Can process UTF8 encoding 'fhx -InputObject 'hello' -Encoding UTF8'" + Encoding = "UTF8" + Count = 1 + ExpectedResult = "00000000 68 65 6C 6C 6F hello" + } + @{ + Name = "Can process UTF32 encoding 'fhx -InputObject 'hello' -Encoding UTF32'" + Encoding = "UTF32" + Count = 1 + ExpectedResult = "00000000 68 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 h...e...l...l...`r`n00000010 6F 00 00 00 o..." + } + ) + + foreach ($testCase in $testCases) + { + RunEncodingTestCase $testCase } +} - # This test is to validate that files do not have buffer underrun problems - It "ValidateEachBufferHasCorrectContentForFiles" { - $result = Format-Hex -path $InputFile2 - $result | Should Not BeNullOrEmpty - $result.Count | should be 2 - $result[0].ToString() | Should be "00000000 54 68 69 73 20 69 73 20 61 20 62 69 74 20 6D 6F This is a bit mo" - if ( $IsCoreCLR ) { - $result[1].ToString() | Should be "00000010 72 65 20 74 65 78 74 re text " + Context "Validate Error Scenarios" { + + $testDirectory = $inputFile1.DirectoryName + + $testCases = @( + @{ + 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" } + ExpectedFullyQualifiedErrorId = "FormatHexTypeNotSupported,Microsoft.PowerShell.Commands.FormatHex" + } + ) + + foreach ($testCase in $testCases) + { + RunExpectedErrorTestCase $testCase } } - # This test ensures that if we stream bytes from a file, the output is correct - It "ValidateStreamOfBytesFromFileHasProperOutput" { - $result = Get-Content $InputFile1 -Encoding Byte | Format-Hex - $result | Should Not BeNullOrEmpty - ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' - if ( $IsCoreCLR ) { - $result.ToString() | Should be "00000000 48 65 6C 6C 6F 20 57 6F 72 6C 64 Hello World " + Context "Continues to Process Valid Paths" { + + $testCases = @( + @{ + Name = "If given invalid path in array, continues to process valid paths 'fhx -Path `$invalidPath, `$inputFile1 -ev 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 -ev 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 -ev e -ErrorAction SilentlyContinue'" + InvalidPath = "Cert:\CurrentUser\My\$thumbprint" + ExpectedFullyQualifiedErrorId = "FormatHexOnlySupportsFileSystemPaths,Microsoft.PowerShell.Commands.FormatHex" + } + ) + + foreach ($testCase in $testCases) + { + RunContinuesToProcessCase $testCase } } - # This test is to validate the alias for Format-Hex cmdlet. - It "ValidateCmdletAlias" { + Context "Cmdlet Functionality" { + + It "Path is default Parameter Set 'fhx `$inputFile1'" { + + $result = Format-Hex $inputFile1 - try - { - $result = Get-Command fhx -ErrorAction Stop $result | Should Not BeNullOrEmpty - $result.CommandType.ToString() | Should Be "Alias" + ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $actualResult = $result.ToString() + $actualResult | Should Match $inputText1 } - catch - { - $_ | Should BeNullOrEmpty + + It "Validate file input from Pipeline 'Get-ChildItem `$inputFile1 | Format-Hex'" { + + $result = Get-ChildItem $inputFile1 | Format-Hex + + $result | Should Not BeNullOrEmpty + ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $actualResult = $result.ToString() + $actualResult | Should Match $inputText1 + } + + It "Validate that streamed text does not have buffer underrun problems ''a' * 30 | Format-Hex'" { + + $result = "a" * 30 | Format-Hex + + $result | Should Not BeNullOrEmpty + ,$result | Should BeOfType 'Microsoft.PowerShell.Commands.ByteCollection' + $result.ToString() | Should be "00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa`r`n00000010 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 `$InputFile2'" { + + $result = Format-Hex -path $InputFile2 + + $result | Should Not BeNullOrEmpty + $result.Count | should be 2 + $result[0].ToString() | Should be "00000000 54 68 69 73 20 69 73 20 61 20 62 69 74 20 6D 6F This is a bit mo" + $result[1].ToString() | Should be "00000010 72 65 20 74 65 78 74 re text " } } }