diff --git a/.gitignore b/.gitignore index c8259cf33..9cdda77f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bin/ obj/ +debug/ project.lock.json *-tests.xml diff --git a/.vscode/launch.json b/.vscode/launch.json index 59b831f29..afd0d5d0b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,8 +7,8 @@ "request": "launch", "justMyCode": false, "stopAtEntry": true, - "program": "${workspaceRoot}/bin/powershell", - "args": [ ], + "program": "${workspaceRoot}/debug/powershell", + "args": [ "-NonInteractive" ], "preLaunchTask": "build", "cwd": "${workspaceRoot}" }, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9864ad2b4..3c853f8aa 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,7 +8,7 @@ "tasks": [ { "taskName": "build", - "args": [ "Import-Module ${workspaceRoot}/PowerShellGitHubDev.psm1; Start-PSBuild" ], + "args": [ "Import-Module ${workspaceRoot}/PowerShellGitHubDev.psm1; Start-PSBuild -Output debug" ], "isBuildCommand": true, "problemMatcher": "$msCompile" } diff --git a/PowerShellGitHubDev.psm1 b/PowerShellGitHubDev.psm1 index 3dde4e746..3a5bee497 100644 --- a/PowerShellGitHubDev.psm1 +++ b/PowerShellGitHubDev.psm1 @@ -25,6 +25,7 @@ function Start-PSBuild { param( [switch]$NoPath, [switch]$Restore, + [string]$Output, [Parameter(ParameterSetName='CoreCLR')] [switch]$Publish, @@ -105,7 +106,7 @@ function Start-PSBuild { } # set output options - $OptionsArguments = @{Publish=$Publish; FullCLR=$FullCLR; Runtime=$Runtime} + $OptionsArguments = @{Publish=$Publish; Output=$Output; FullCLR=$FullCLR; Runtime=$Runtime} $script:Options = New-PSOptions @OptionsArguments # setup arguments @@ -115,6 +116,9 @@ function Start-PSBuild { } else { $Arguments += "build" } + if ($Output) { + $Arguments += "--output", (Join-Path $PSScriptRoot $Output) + } $Arguments += "--configuration", $Options.Configuration $Arguments += "--framework", $Options.Framework $Arguments += "--runtime", $Options.Runtime @@ -212,6 +216,7 @@ function New-PSOptions { [string]$Runtime, [switch]$Publish, + [string]$Output, [switch]$FullCLR ) @@ -261,21 +266,25 @@ function New-PSOptions { "powershell.exe" } - # Build the Output path in script scope - $Output = [IO.Path]::Combine($Top, "bin", $Configuration, $Framework) + # Build the Output path + if ($Output) { + $Output = Join-Path $PSScriptRoot $Output + } else { + $Output = [IO.Path]::Combine($Top, "bin", $Configuration, $Framework) - # FullCLR only builds a library, so there is no runtime component - if (-not $FullCLR) { - $Output = [IO.Path]::Combine($Output, $Runtime) + # FullCLR only builds a library, so there is no runtime component + if (-not $FullCLR) { + $Output = [IO.Path]::Combine($Output, $Runtime) + } + + # Publish injects the publish directory + if ($Publish) { + $Output = [IO.Path]::Combine($Output, "publish") + } + + $Output = [IO.Path]::Combine($Output, $Executable) } - # Publish injects the publish directory - if ($Publish) { - $Output = [IO.Path]::Combine($Output, "publish") - } - - $Output = [IO.Path]::Combine($Output, $Executable) - return @{ Top = $Top; Configuration = $Configuration; Framework = $Framework; @@ -388,7 +397,7 @@ function Start-PSBootstrap { } elseif ($IsWindows -And -Not $IsCore) { Remove-Item -ErrorAction SilentlyContinue -Recurse -Force ~\AppData\Local\Microsoft\dotnet Invoke-WebRequest -Uri https://raw.githubusercontent.com/dotnet/cli/rel/1.0.0/scripts/obtain/install.ps1 -OutFile install.ps1 - ./install.ps1 + ./install.ps1 -Version 1.0.0-rc2-002655 } else { Write-Warning "Start-PSBootstrap cannot be run in Core PowerShell on Windows (need Invoke-WebRequest!)" diff --git a/docs/FAQ.md b/docs/FAQ.md index 14dd8eac4..e07381a57 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -43,6 +43,20 @@ more information. [error]: https://blogs.msdn.microsoft.com/kebab/2013/06/09/an-introduction-to-error-handling-in-powershell/ +I just pulled master, why did my build fail? +============================================ + +There are two likely reasons: + +If the `src/windows-build` submodule was changed, you need to manually run `git +submodule update` to check your sources out to the correct commits. Pulling does +not automatically do this. When a submodule is out-of-date, you will likely be +missing required build files. + +If package dependencies were changed in any `project.json`, you need to manually +run `dotnet restore` to update your local dependency graphs. `Start-PSBuild +-Restore` can automatically do this. + Why did `Start-PSBuild` tell me to update `dotnet`? =================================================== diff --git a/docs/debugging/README.md b/docs/debugging/README.md index 2994048d1..319c423d6 100644 --- a/docs/debugging/README.md +++ b/docs/debugging/README.md @@ -2,7 +2,7 @@ Debugging ========= VS Code -------- +======= [Experimental .NET Core Debugging in VS Code][core-debug] enables cross-platform debugging with the [Visual Studio Code][vscode] editor. @@ -38,19 +38,32 @@ process named `powershell`, and will attach to it. If you need more fine grained control, replace `processName` with `processId` and provide a PID. (Please be careful not to commit such a change). +Important Note +-------------- + +Debugging is very sensitive to the versions of our .NET Core packages, VS Code, +and the C# extension. + +- Use the VS Code [Insiders][] build +- Use the latest pre-release of the [C# extension][] +- Refer to the pre-release [documentation][] + [core-debug]: https://blogs.msdn.microsoft.com/visualstudioalm/2016/03/10/experimental-net-core-debugging-in-vs-code/ [vscode]: https://code.visualstudio.com/ [OmniSharp]: https://github.com/OmniSharp/omnisharp-vscode [vscclrdebugger]: http://aka.ms/vscclrdebugger +[insiders]: https://code.visualstudio.com/insiders +[C# extension]: https://github.com/OmniSharp/omnisharp-vscode/releases +[documentation]: https://github.com/OmniSharp/omnisharp-vscode/pull/157 corehost --------- +======== The native executable prouduced by .NET CLI will produce trace output if launched with `COREHOST_TRACE=1 ./powershell`. CoreCLR PAL ------------ +=========== The native code in the CLR has debug channels to selectively output information to the console. These are controlled by the diff --git a/src/Microsoft.Management.Infrastructure/project.json b/src/Microsoft.Management.Infrastructure/project.json index c30907ee6..e6ff6d089 100644 --- a/src/Microsoft.Management.Infrastructure/project.json +++ b/src/Microsoft.Management.Infrastructure/project.json @@ -8,7 +8,7 @@ }, "dependencies": { - "Microsoft.Management.Infrastructure.Native": "1.0.0-rc3" + "Microsoft.Management.Infrastructure.Native": "1.0.0-rc4" }, "frameworks": { diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs index d2ba941ee..fb58250d3 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs @@ -3562,7 +3562,22 @@ namespace Microsoft.PowerShell.Commands } bool shouldRecurse = Recurse; - if (!Recurse && hasChildren) + bool treatAsFile = false; + try + { + System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(providerPath); + if (!Platform.IsWindows && di != null && (di.Attributes & System.IO.FileAttributes.ReparsePoint) != 0) + { + shouldRecurse = false; + treatAsFile = true; + } + } + catch (System.IO.FileNotFoundException) + { + // not a directory + } + + if (!treatAsFile && !Recurse && hasChildren) { // Get the localized prompt string diff --git a/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs b/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs index d66748dfa..dfd9c13fb 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsExtensions.cs @@ -1070,17 +1070,6 @@ namespace System.Management.Automation } } - internal static string WinGetMachineName() - { - // In future release of operating systems, you might be able to rename a machine without - // rebooting. Therefore, don't cache this machine name. - StringBuilder buf = new StringBuilder(MaxMachineNameLength); - int len = MaxMachineNameLength; - if (Win32Native.GetComputerName(buf, ref len) == 0) - throw new InvalidOperationException(CoreClrStubResources.CannotGetComputerName); - return buf.ToString(); - } - /// /// MachineName /// @@ -1088,14 +1077,7 @@ namespace System.Management.Automation { get { - if (Platform.IsWindows) - { - return WinGetMachineName(); - } - else - { - return Platform.NonWindowsGetMachineName(); - } + return System.Environment.MachineName; } } @@ -1276,9 +1258,6 @@ namespace System.Management.Automation // Win32 return type is BOOLEAN (which is 1 byte and not BOOL which is 4bytes) internal static extern byte GetUserNameEx(int format, [Out] StringBuilder domainName, ref uint domainNameLen); - [DllImport("api-ms-win-downlevel-kernel32-l2-1-0.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal extern static int GetComputerName([Out]StringBuilder nameBuffer, ref int bufferSize); - [DllImport("api-ms-win-core-localization-l1-2-1.dll", CharSet = CharSet.Unicode)] internal static extern int FormatMessage(int dwFlags, IntPtr lpSource, int dwMessageId, int dwLanguageId, [Out]StringBuilder lpBuffer, diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 50c651425..34623fc1b 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -351,18 +351,6 @@ namespace System.Management.Automation } } - internal static string NonWindowsGetMachineName() - { - if (!IsWindows) - { - return LinuxPlatform.HostName; - } - else - { - throw new PlatformNotSupportedException(); - } - } - // Hostname in this context seems to be the FQDN internal static string NonWindowsGetHostName() { @@ -470,22 +458,6 @@ namespace System.Management.Automation internal static class LinuxPlatform { - public static string HostName - { - get - { - StringBuilder value = new StringBuilder(1024); - uint valueLen = (uint)value.Capacity; - string computerName = Native.GetComputerName(); - if (string.IsNullOrEmpty(computerName)) - { - int lastError = Marshal.GetLastWin32Error(); - throw new InvalidOperationException("LinuxPlatform.HostName error: " + lastError); - } - return computerName; - } - } - private static string _userName; public static string UserName { @@ -696,10 +668,6 @@ namespace System.Management.Automation [return: MarshalAs(UnmanagedType.LPStr)] internal static extern string GetUserName(); - [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] - [return: MarshalAs(UnmanagedType.LPStr)] - internal static extern string GetComputerName(); - [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] internal static extern int GetLinkCount([MarshalAs(UnmanagedType.LPStr)]string filePath, out int linkCount); diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 79e92e995..6dc033623 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -2858,7 +2858,7 @@ namespace Microsoft.PowerShell.Commands } //if this is a reparse point and force is not specified then warn user but dont remove the directory. - if (((directory.Attributes & FileAttributes.ReparsePoint) != 0) && !Force) + if (Platform.IsWindows && ((directory.Attributes & FileAttributes.ReparsePoint) != 0) && !Force) { String error = StringUtil.Format(FileSystemProviderStrings.DirectoryReparsePoint, directory.FullName); Exception e = new IOException(error); @@ -8452,10 +8452,24 @@ namespace Microsoft.PowerShell.Commands IntPtr dangerousHandle = handle.DangerousGetHandle(); int bytesReturned; + // Do a FSCTL_GET_REPARSE_POINT first because the ReparseTag could be + // IO_REPARSE_TAG_MOUNT_POINT or IO_REPARSE_TAG_SYMLINK. + // Using the wrong one results in mismatched-tag error. + REPARSE_GUID_DATA_BUFFER junctionData = new REPARSE_GUID_DATA_BUFFER(); + ClrFacade.StructureToPtr(junctionData, outBuffer, false); + + result = DeviceIoControl(dangerousHandle, FSCTL_GET_REPARSE_POINT, IntPtr.Zero, 0, + outBuffer, inOutBufferSize, out bytesReturned, IntPtr.Zero); + if (!result) + { + int lastError = Marshal.GetLastWin32Error(); + throw new Win32Exception(lastError); + } + + junctionData = ClrFacade.PtrToStructure(outBuffer); junctionData.ReparseDataLength = 0; junctionData.DataBuffer = new char[MAX_REPARSE_SIZE]; - junctionData.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; ClrFacade.StructureToPtr(junctionData, inBuffer, false); diff --git a/src/windows-build b/src/windows-build index 674541f74..7144b55bd 160000 --- a/src/windows-build +++ b/src/windows-build @@ -1 +1 @@ -Subproject commit 674541f7444de5be5f8c63e3a2ef2d98739c1943 +Subproject commit 7144b55bdafb71dc91af4f5fb13b6e14904b789f diff --git a/test/csharp/test_CorePsPlatform.cs b/test/csharp/test_CorePsPlatform.cs index a99c3b0f9..1284b2583 100644 --- a/test/csharp/test_CorePsPlatform.cs +++ b/test/csharp/test_CorePsPlatform.cs @@ -99,7 +99,7 @@ namespace PSTests // The process should return an exit code of 0 on success Assert.Equal(0, process.ExitCode); // It should be the same as what our platform code returns - Assert.Equal(hostname, Platform.NonWindowsGetMachineName()); + Assert.Equal(hostname, System.Management.Automation.Environment.MachineName); } } diff --git a/test/powershell/ConvertFrom-StringData.Tests.ps1 b/test/powershell/ConvertFrom-StringData.Tests.ps1 index f1ec128e6..fe1f6232a 100644 --- a/test/powershell/ConvertFrom-StringData.Tests.ps1 +++ b/test/powershell/ConvertFrom-StringData.Tests.ps1 @@ -1,3 +1,23 @@ +Describe "ConvertFrom-StringData DRT Unit Tests" -Tags DRT { + It "Should able to throw error when convert invalid line" { + $str =@" +#comments here +abc +#comments here +def=content of def +"@ + try + { + ConvertFrom-StringData $str + Throw "we expect 'InvalidOperation' exception" + } + catch + { + $_.FullyQualifiedErrorId | Should be "InvalidOperation,Microsoft.PowerShell.Commands.ConvertFromStringDataCommand" + } + } +} + Describe "ConvertFrom-StringData" { $sampleData = @' foo = 0 diff --git a/test/powershell/Format-Custom.Tests.ps1 b/test/powershell/Format-Custom.Tests.ps1 index 3486a1428..df7d1144c 100644 --- a/test/powershell/Format-Custom.Tests.ps1 +++ b/test/powershell/Format-Custom.Tests.ps1 @@ -23,3 +23,22 @@ Describe "Format-Custom" { } } + + +Describe "Format-Custom DRT basic functionality" -Tags DRT{ + It "Format-Custom with subobject should work"{ + $expectResult1 = "this is the name" + $expectResult2 = "this is the name of the sub object" + $testObject = @{} + $testObject.name = $expectResult1 + $testObject.subObjectValue = @{} + $testObject.subObjectValue.name = $expectResult2 + $testObject.subObjectValue.array = (0..63) + $testObject.subObjectValue.stringarray = @("one","two") + $result = $testObject | Format-Custom | Out-String + $result | Should Match $expectResult1 + $result | Should Match $expectResult2 + $result | Should Match "one" + $result | Should Match "two" + } +} \ No newline at end of file diff --git a/test/powershell/Format-List.Tests.ps1 b/test/powershell/Format-List.Tests.ps1 index 7e8e67f57..782456551 100644 --- a/test/powershell/Format-List.Tests.ps1 +++ b/test/powershell/Format-List.Tests.ps1 @@ -32,27 +32,27 @@ Describe "Format-List" { It "Should be able to call a property of the piped input" { # Tested on two input commands to verify functionality. - { Get-Command | Format-List -Property Name } | Should Not BeNullOrEmpty + { Get-Command | Select-Object -First 5 | Format-List -Property Name } | Should Not BeNullOrEmpty { Get-Date | Format-List -Property DisplayName } | Should Not BeNullOrEmpty } It "Should be able to display a list of props when separated by a comma" { - (Get-Command | Format-List -Property Name,Source | Out-String) -Split "${nl}" | + (Get-Command | Select-Object -First 5 | Format-List -Property Name,Source | Out-String) -Split "${nl}" | Where-Object { $_.trim() -ne "" } | ForEach-Object { $_ | Should Match "(Name)|(Source)" } } It "Should show the requested prop in every element" { # Testing each element of format-list, using a for-each loop since the Format-List is so opaque - (Get-Command | Format-List -Property Source | Out-String) -Split "${nl}" | + (Get-Command | Select-Object -First 5 | Format-List -Property Source | Out-String) -Split "${nl}" | Where-Object { $_.trim() -ne "" } | ForEach-Object { $_ | Should Match "Source :" } } It "Should not show anything other than the requested props" { - $output = Get-Command | Format-List -Property Name | Out-String + $output = Get-Command | Select-Object -First 5 | Format-List -Property Name | Out-String $output | Should Not Match "CommandType :" $output | Should Not Match "Source :" @@ -66,3 +66,62 @@ Describe "Format-List" { } } + +Describe "Format-List DRT basic functionality" -Tags DRT{ + It "Format-List with array should work"{ + $al = (0..255) + $info = @{} + $info.array = $al + $result = $info | Format-List | Out-String + $result | Should Match "Name : array\s+Value : {0, 1, 2, 3...}" + } + + It "Format-List with No Objects for End-To-End should work"{ + $p = @{} + $result = $p | Format-List -Force -Property "foo","bar" | Out-String + $result.Trim() | Should BeNullOrEmpty + } + + It "Format-List with Null Objects for End-To-End should work"{ + $p = $null + $result = $p | Format-List -Force -Property "foo","bar" | Out-String + $result.Trim() | Should BeNullOrEmpty + } + + It "Format-List with single line string for End-To-End should work"{ + $p = "single line string" + $result = $p | Format-List -Force -Property "foo","bar" | Out-String + $result.Trim() | Should BeNullOrEmpty + } + + It "Format-List with multiple line string for End-To-End should work"{ + $p = "Line1\nLine2" + $result = $p | Format-List -Force -Property "foo","bar" | Out-String + $result.Trim() | Should BeNullOrEmpty + } + + It "Format-List with string sequence for End-To-End should work"{ + $p = "Line1","Line2" + $result = $p | Format-List -Force -Property "foo","bar" | Out-String + $result.Trim() | Should BeNullOrEmpty + } + + It "Format-List with complex object for End-To-End should work"{ + Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thr,Fri,Sat}" + $eto = New-Object MyDayOfWeek + $info = @{} + $info.intArray = 1,2,3,4 + $info.arrayList = "string1","string2" + $info.enumerable = [MyDayOfWeek]$eto + $info.enumerableTestObject = $eto + $result = $info|Format-List|Out-String + $result | Should Match "Name : enumerableTestObject" + $result | Should Match "Value : Sun" + $result | Should Match "Name : arrayList" + $result | Should Match "Value : {string1, string2}" + $result | Should Match "Name : enumerable" + $result | Should Match "Value : Sun" + $result | Should Match "Name : intArray" + $result | Should Match "Value : {1, 2, 3, 4}" + } +} diff --git a/test/powershell/Format-Wide.Tests.ps1 b/test/powershell/Format-Wide.Tests.ps1 index e4b35780b..c4e9a5862 100644 --- a/test/powershell/Format-Wide.Tests.ps1 +++ b/test/powershell/Format-Wide.Tests.ps1 @@ -8,26 +8,90 @@ Describe "Format-Wide" { } It "Should be able to specify the columns in output using the column switch" { - { ls | Format-Wide -Column 3 } | Should Not Throw + { Get-ChildItem | Format-Wide -Column 3 } | Should Not Throw } It "Should be able to use the autosize switch" { - { ls | Format-Wide -Autosize } | Should Not Throw + { Get-ChildItem | Format-Wide -Autosize } | Should Not Throw } It "Should be able to take inputobject instead of pipe" { - { Format-Wide -InputObject $(ls) } | Should Not Throw + { Format-Wide -InputObject $(Get-ChildItem) } | Should Not Throw } It "Should be able to use the property switch" { - { Format-Wide -InputObject $(ls) -Property Mode } | Should Not Throw + { Format-Wide -InputObject $(Get-ChildItem) -Property Mode } | Should Not Throw } It "Should throw an error when property switch and view switch are used together" { - { Format-Wide -InputObject $(ls) -Property CreationTime -View aoeu } | Should Throw "Found invalid data" + try + { + Format-Wide -InputObject $(Get-ChildItem) -Property CreationTime -View aoeu + } + catch + { + $_.FullyQualifiedErrorId | Should be "FormatCannotSpecifyViewAndProperty,Microsoft.PowerShell.Commands.FormatWideCommand" + } } It "Should throw and suggest proper input when view is used with invalid input without the property switch" { - { Format-Wide -InputObject $(gps) -View aoeu } | Should Throw + { Format-Wide -InputObject $(Get-Process) -View aoeu } | Should Throw } } + +Describe "Format-Wide DRT basic functionality" -Tags DRT{ + It "Format-Wide with array should work"{ + $al = (0..255) + $info = @{} + $info.array = $al + $result = $info | Format-Wide | Out-String + $result | Should Match "array" + } + + It "Format-Wide with No Objects for End-To-End should work"{ + $p = @{} + $result = $p | Format-Wide | Out-String + $result | Should BeNullOrEmpty + } + + It "Format-Wide with Null Objects for End-To-End should work"{ + $p = $null + $result = $p | Format-Wide | Out-String + $result | Should BeNullOrEmpty + } + + It "Format-Wide with single line string for End-To-End should work"{ + $p = "single line string" + $result = $p | Format-Wide | Out-String + $result | Should Match $p + } + + It "Format-Wide with multiple line string for End-To-End should work"{ + $p = "Line1\nLine2" + $result = $p | Format-Wide | Out-String + $result | Should Match "Line1" + $result | Should Match "Line2" + } + + It "Format-Wide with string sequence for End-To-End should work"{ + $p = "Line1","Line2" + $result = $p |Format-Wide | Out-String + $result | Should Match "Line1" + $result | Should Match "Line2" + } + + It "Format-Wide with complex object for End-To-End should work"{ + Add-Type -TypeDefinition "public enum MyDayOfWeek{Sun,Mon,Tue,Wed,Thr,Fri,Sat}" + $eto = New-Object MyDayOfWeek + $info = @{} + $info.intArray = 1,2,3,4 + $info.arrayList = "string1","string2" + $info.enumerable = [MyDayOfWeek]$eto + $info.enumerableTestObject = $eto + $result = $info|Format-Wide|Out-String + $result | Should Match "intArray" + $result | Should Match "arrayList" + $result | Should Match "enumerable" + $result | Should Match "enumerableTestObject" + } +} \ No newline at end of file diff --git a/test/powershell/Get-Random.Tests.ps1 b/test/powershell/Get-Random.Tests.ps1 index 0d769846e..58cca7cb9 100644 --- a/test/powershell/Get-Random.Tests.ps1 +++ b/test/powershell/Get-Random.Tests.ps1 @@ -1,3 +1,94 @@ +Describe "Get-Random DRT Unit Tests" -Tags DRT{ + $testData = @( + @{ Name = 'no params'; Maximum = $null; Minimum = $null; GreaterThan = -1; LessThan = ([int32]::MaxValue); Type = 'System.Int32' } + @{ Name = 'only positive maximum number'; Maximum = 100; Minimum = $null; GreaterThan = -1; LessThan = 100; Type = 'System.Int32' } + @{ Name = 'maximum set to 0, Minimum to a negative number'; Maximum = 0; Minimum = -100; GreaterThan = -101; LessThan = 0; Type = 'System.Int32' } + @{ Name = 'positive maximum, negative Minimum'; Maximum = 100; Minimum = -100; GreaterThan = -101; LessThan = 100; Type = 'System.Int32' } + @{ Name = 'both negative'; Maximum = -100; Minimum = -200; GreaterThan = -201; LessThan = -100; Type = 'System.Int32' } + @{ Name = 'both negative with parentheses'; Maximum = (-100); Minimum = (-200); GreaterThan = -201; LessThan = -100; Type = 'System.Int32' } + @{ Name = 'maximum enclosed in quote'; Maximum = '8'; Minimum = 5; GreaterThan = 4; LessThan = 8; Type = 'System.Int32' } + @{ Name = 'minimum enclosed in quote'; Maximum = 8; Minimum = '5'; GreaterThan = 4; LessThan = 8; Type = 'System.Int32' } + @{ Name = 'maximum with plus sign'; Maximum = +100; Minimum = 0; GreaterThan = -1; LessThan = 100; Type = 'System.Int32' } + @{ Name = 'maximum with plus sign and quote'; Maximum = '+100'; Minimum = 0; GreaterThan = -1; LessThan = 100; Type = 'System.Int32' } + @{ Name = 'both with quote'; Maximum = '+100'; Minimum = '-100'; GreaterThan = -101; LessThan = 100; Type = 'System.Int32' } + @{ Name = 'maximum set to kb'; Maximum = '1kb'; Minimum = 0; GreaterThan = -1; LessThan = 1024; Type = 'System.Int32' } + @{ Name = 'maximum is Int64.MaxValue'; Maximum = ([int64]::MaxValue); Minimum = $null; GreaterThan = ([int64]-1); LessThan = ([int64]::MaxValue); Type = 'System.Int64' } + @{ Name = 'maximum is a 64-bit integer'; Maximum = ([int64]100); Minimum = $null; GreaterThan = ([int64]-1); LessThan = ([int64]100); Type = 'System.Int64' } + @{ Name = 'maximum set to a large integer greater than int32.MaxValue'; Maximum = 100000000000; Minimum = $null; GreaterThan = ([int64]-1); LessThan = ([int64]100000000000); Type = 'System.Int64' } + @{ Name = 'maximum set to 0, Minumum set to a negative 64-bit integer'; Maximum = ([int64]0); Minimum = ([int64]-100); GreaterThan = ([int64]-101); LessThan = ([int64]0); Type = 'System.Int64' } + @{ Name = 'maximum set to positive 64-bit number, min set to negative 64-bit number'; Maximum = ([int64]100); Minimum = ([int64]-100); GreaterThan = ([int64]-101); LessThan = ([int64]100); Type = 'System.Int64' } + @{ Name = 'both are negative 64-bit number'; Maximum = ([int64]-100); Minimum = ([int64]-200); GreaterThan = ([int64]-201); LessThan = ([int64]-100); Type = 'System.Int64' } + @{ Name = 'both are negative 64-bit number with parentheses'; Maximum = ([int64](-100)); Minimum = ([int64](-200)); GreaterThan = ([int64]-201); LessThan = ([int64]-100); Type = 'System.Int64' } + @{ Name = 'max is 32-bit, min is 64-bit integer'; Maximum = '8'; Minimum = ([int64]5); GreaterThan = ([int64]4); LessThan = ([int64]8); Type = 'System.Int64' } + @{ Name = 'max is 64-bit, min is 32-bit integer'; Maximum = ([int64]8); Minimum = '5'; GreaterThan = ([int64]4); LessThan = ([int64]8); Type = 'System.Int64' } + @{ Name = 'max set to a 32-bit integer, min set to [int64]0'; Maximum = +100; Minimum = ([int64]0); GreaterThan = ([int64]-1); LessThan = ([int64]100); Type = 'System.Int64' } + @{ Name = 'max set to a 32-bit integer with quote'; Maximum = '+100'; Minimum = ([int64]0); GreaterThan = ([int64]-1); LessThan = ([int64]100); Type = 'System.Int64' } + @{ Name = 'max is [int64]0, min is a 32-bit integer'; Maximum = ([int64]0); Minimum = '-100'; GreaterThan = ([int64]-101); LessThan = ([int64]0); Type = 'System.Int64' } + @{ Name = 'min set to 1MB, max set to a 64-bit integer greater than min'; Maximum = ([int64]1048585); Minimum = '1mb'; GreaterThan = ([int64]1048575); LessThan = ([int64]1048585); Type = 'System.Int64' } + @{ Name = 'max set to 1tb, min set to 10 mb'; Maximum = '1tb'; Minimum = '10mb'; GreaterThan = ([int64]10485759); LessThan = ([int64]1099511627776); Type = 'System.Int64' } + @{ Name = 'max is int64.MaxValue, min is Int64.MinValue'; Maximum = ([int64]::MaxValue); Minimum = ([int64]::MinValue); GreaterThan = ([int64]::MinValue); LessThan = ([int64]::MaxValue); Type = 'System.Int64' } + @{ Name = 'both are int64.MaxValue plus a 32-bit integer'; Maximum = ([int64](([int]::MaxValue)+15)); Minimum = ([int64](([int]::MaxValue)+10)); GreaterThan = ([int64](([int]::MaxValue)+9)); LessThan = ([int64](([int]::MaxValue)+15)); Type = 'System.Int64' } + @{ Name = 'both are greater than int32.MaxValue without specified type, and max with quote'; Maximum = '100099000001'; Minimum = 100000000001; GreaterThan = ([int64]10000000000); LessThan = ([int64]100099000001); Type = 'System.Int64' } + @{ Name = 'both are greater than int32.MaxValue without specified type, and min with quote'; Maximum = 100000002230; Minimum = '100000002222'; GreaterThan = ([int64]100000002221); LessThan = ([int64]100000002230); Type = 'System.Int64' } + @{ Name = 'max is greater than int32.MaxValue without specified type'; Maximum = 90000000000; Minimum = 4; GreaterThan = ([int64]3); LessThan = ([int64]90000000000); Type = 'System.Int64' } + @{ Name = 'max is a double-precision number'; Maximum = 100.0; Minimum = $null; GreaterThan = -1.0; LessThan = 100.0; Type = 'System.Double' } + @{ Name = 'both are double-precision numbers, min is negative.'; Maximum = 0.0; Minimum = -100.0; GreaterThan = -101.0; LessThan = 0.0; Type = 'System.Double' } + @{ Name = 'both are double-precision number, max is positive, min is negative.'; Maximum = 100.0; Minimum = -100.0; GreaterThan = -101.0; LessThan = 100.0; Type = 'System.Double' } + @{ Name = 'max is a double-precision number, min is int32'; Maximum = 8.0; Minimum = 5; GreaterThan = 4.0; LessThan = 8.0; Type = 'System.Double' } + @{ Name = 'min is a double-precision number, max is int32'; Maximum = 8; Minimum = 5.0; GreaterThan = 4.0; LessThan = 8.0; Type = 'System.Double' } + @{ Name = 'max set to a special double number'; Maximum = 20.; Minimum = 0.0; GreaterThan = -1.0; LessThan = 20.0; Type = 'System.Double' } + @{ Name = 'max is double with quote'; Maximum = '20.'; Minimum = 0.0; GreaterThan = -1.0; LessThan = 20.0; Type = 'System.Double' } + @{ Name = 'max is double with plus sign'; Maximum = +100.0; Minimum = 0; GreaterThan = -1.0; LessThan = 100.0; Type = 'System.Double' } + @{ Name = 'max is doulbe with plus sign and enclosed in quote'; Maximum = '+100.0'; Minimum = 0; GreaterThan = -1.0; LessThan = 100.0; Type = 'System.Double' } + @{ Name = 'both set to the special numbers as 1.0e+xx '; Maximum = $null; Minimum = 1.0e+100; GreaterThan = 1.0e+99; LessThan = ([double]::MaxValue); Type = 'System.Double' } + @{ Name = 'max is Double.MaxValue, min is Double.MinValue'; Maximum = ([double]::MaxValue); Minimum = ([double]::MinValue); GreaterThan = ([double]::MinValue); LessThan = ([double]::MaxValue); Type = 'System.Double' } + + ) + + $testDataForError = @( + @{ Name = 'Min is greater than max and all are positive 32-bit integer'; Maximum = 10; Minimum = 20} + @{ Name = 'Min and Max are same and all are positive 32-bit integer'; Maximum = 20; Minimum = 20} + @{ Name = 'Min is greater than max and all are negative 32-bit integer'; Maximum = -20; Minimum = -10} + @{ Name = 'Min and Max are same and all are negative 32-bit integer'; Maximum = -20; Minimum = -20} + @{ Name = 'Min is greater than max and all are positive double-precision number'; Maximum = 10.0; Minimum = 20.0} + @{ Name = 'Min and Max are same and all are positive double-precision number'; Maximum = 20.0; Minimum = 20.0} + @{ Name = 'Min is greater than max and all are negative double-precision number'; Maximum = -20.0; Minimum = -10.0} + @{ Name = 'Min and Max are same and all are negative double-precision number'; Maximum = -20.0; Minimum = -20.0} + @{ Name = 'Max is a negative number, min is the default number '; Maximum = -10; Minimum = $null} + + ) + + # minimum is always set to the actual low end of the range, details refer to closed issue #887. + It "get a correct random number for ''" -TestCases $testData { + param($maximum, $minimum, $greaterThan, $lessThan, $type) + + $result = Get-Random -Maximum $maximum -Minimum $minimum + $result | Should BeGreaterThan $greaterThan + $result | Should BeLessThan $lessThan + $result.GetType().FullName | Should Be $type + + } + + It "Should be able to throw error when ''" -TestCases $testDataForError { + param($maximum, $minimum) + try + { + Get-Random -Minimum $minimum -Maximum $maximum + throw "OK" + } + catch + { + $_.FullyQualifiedErrorId | Should Be "MinGreaterThanOrEqualMax,Microsoft.PowerShell.Commands.GetRandomCommand" + } + } + + It "Tests for setting the seed" { + $result1 = (get-random -SetSeed 123), (get-random) + $result2 = (get-random -SetSeed 123), (get-random) + $result1 | Should Be $result2 + } +} + Describe "Get-Random" { It "Should return a random number greater than -1 " { Get-Random | Should BeGreaterThan -1 diff --git a/test/powershell/Get-Unique.Tests.ps1 b/test/powershell/Get-Unique.Tests.ps1 index 8eade4134..2b5688e90 100644 --- a/test/powershell/Get-Unique.Tests.ps1 +++ b/test/powershell/Get-Unique.Tests.ps1 @@ -1,3 +1,22 @@ +Describe "Get-Unique DRT Unit Tests" -Tags DRT{ + It "Command get-unique works with AsString switch" { + $inputArray = "aa","aa","Aa","ba","BA","BA" + $results = $inputArray | Get-Unique -AsString + + $results.Length | Should Be 4 + + $results[0] | Should Be "aa" + $results[1] | Should Be "Aa" + $results[2] | Should Be "ba" + $results[3] | Should Be "BA" + + $results[0].GetType().FullName | Should be System.String + $results[1].GetType().FullName | Should be System.String + $results[2].GetType().FullName | Should be System.String + $results[3].GetType().FullName | Should be System.String + } +} + Describe "Get-Unique" { $sortedList1 = 1,2,2,3,3,4,5 It "Should be able to use the Get-Unique cmdlet without error with inputObject switch" { @@ -59,12 +78,12 @@ Describe "Get-Unique" { $collection = "a", "b", "b", "d" $expectedOutput = "a", "b", "d" - $actual = Get-Unique -InputObject $collection + $actual = $collection | Get-Unique - $actual.Length | Should Be $collection.Length + $actual.Length | Should Be $expectedOutput.Length - $actual[0] | Should Be $collection[0] - $actual[1] | Should Be $collection[1] - $actual[2] | Should Be $collection[2] + $actual[0] | Should Be $expectedOutput[0] + $actual[1] | Should Be $expectedOutput[1] + $actual[2] | Should Be $expectedOutput[2] } }