From 65513b8815427dd09c4aa95b4e48ccd2b18da1db Mon Sep 17 00:00:00 2001 From: "Steve Lee [MSFT]" Date: Fri, 14 Jul 2017 18:55:06 -0700 Subject: [PATCH 1/9] Support Invoke-Item -Path On CoreFx, UseShellExecute for Process start is false by default to be cross platform compatible. In the case of a folder, Process.Start() returns Access Denied as it's not an executable. On Windows we can use the ShellExecute path to have explorer open the folder. --- .../namespaces/FileSystemProvider.cs | 3 ++- .../Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 123936bead8..ce0db825f70 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1350,9 +1350,10 @@ protected override void InvokeDefaultAction(string path) invokeProcess.Start(); } #elif CORECLR - catch (Win32Exception ex) when (ex.NativeErrorCode == 193) + catch (Win32Exception ex) when (ex.NativeErrorCode == 193 || ex.NativeErrorCode == 5) { // Error code 193 -- BAD_EXE_FORMAT (not a valid Win32 application). + // Error code 5 -- ACCESS_DENIED (a folder is not an app) // If it's headless SKUs, rethrow. if (Platform.IsNanoServer || Platform.IsIoT) { throw; } // If it's full Windows, then try ShellExecute. diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 index 52a4198685a..1d5a86f9486 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 @@ -59,6 +59,11 @@ Describe "Invoke-Item basic tests" -Tags "CI" { } Get-Content $redirectFile -Raw | Should Match "usage: ping" } + + It "Should invoke a folder without error" { + # can't validate that it actually opened, but no error should be returned + Invoke-Item -Path $PSHOME + } } Describe "Invoke-Item tests on Windows" -Tags "CI","RequireAdminOnWindows" { From 706a7b2dfd800bdd2baec5c9a974de9610595d77 Mon Sep 17 00:00:00 2001 From: "Steve Lee [MSFT]" Date: Sat, 15 Jul 2017 07:20:24 -0700 Subject: [PATCH 2/9] added validation that shell window opened on Windows --- .../Invoke-Item.Tests.ps1 | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 index 1d5a86f9486..7422e8803ad 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 @@ -61,8 +61,28 @@ Describe "Invoke-Item basic tests" -Tags "CI" { } It "Should invoke a folder without error" { - # can't validate that it actually opened, but no error should be returned - Invoke-Item -Path $PSHOME + if ($IsWindows) + { + $shell = New-Object -ComObject "Shell.Application" + $windows = $shell.Windows() + + $before = $windows.Count + Invoke-Item -Path $PSHOME + # give it time to open + Start-Sleep -Milliseconds 500 + $after = $windows.Count + + $before + 1 | Should Be $after + $item = $windows.Item($after - 1) + $item.LocationURL | Should Match ($PSHOME -replace '\\', '/') + ## close the windows explorer + $item.Quit() + } + else + { + # can't validate that it actually opened, but no error should be returned + Invoke-Item -Path $PSHOME + } } } From 1a3134c4f199adb02fb3f0a6b264e82e4beaa642 Mon Sep 17 00:00:00 2001 From: "Steve Lee [MSFT]" Date: Sat, 15 Jul 2017 09:44:10 -0700 Subject: [PATCH 3/9] special case directories to work on Unix --- .../namespaces/FileSystemProvider.cs | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index ce0db825f70..37ce39ff875 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1323,12 +1323,38 @@ protected override void InvokeDefaultAction(string path) string resource = StringUtil.Format(FileSystemProviderStrings.InvokeItemResourceFileTemplate, path); +#if UNIX + void StartProcessWithOpen(string filename) + { + System.Diagnostics.Process process = new System.Diagnostics.Process(); + // The file is possibly not an executable, so we try invoking the default program that handles this file. + const string quoteFormat = "\"{0}\""; + process.StartInfo.FileName = Platform.IsLinux ? "xdg-open" : /* OS X */ "open"; + if (NativeCommandParameterBinder.NeedQuotes(filename)) + { + path = string.Format(CultureInfo.InvariantCulture, quoteFormat, filename); + } + process.StartInfo.Arguments = filename; + // xdg-open on Ubuntu outputs debug info to stderr, suppress it + process.StartInfo.RedirectStandardError = true; + process.Start(); + } +#endif + if (ShouldProcess(resource, action)) { System.Diagnostics.Process invokeProcess = new System.Diagnostics.Process(); try { + // special case directories on UNIX so that we can open them in the window-manager + // corefx doesn't return error if you try to start a directory +#if UNIX + if (Directory.Exists(path)) + { + StartProcessWithOpen(path); + } +#endif // Try Process.Start first. // - In FullCLR, this is all we need to do. // - In CoreCLR, this works for executables on Win/Unix platforms @@ -1339,15 +1365,7 @@ protected override void InvokeDefaultAction(string path) catch (Win32Exception ex) when (ex.NativeErrorCode == 13) { // Error code 13 -- Permission denied. - // The file is possibly not an executable, so we try invoking the default program that handles this file. - const string quoteFormat = "\"{0}\""; - invokeProcess.StartInfo.FileName = Platform.IsLinux ? "xdg-open" : /* OS X */ "open"; - if (NativeCommandParameterBinder.NeedQuotes(path)) - { - path = string.Format(CultureInfo.InvariantCulture, quoteFormat, path); - } - invokeProcess.StartInfo.Arguments = path; - invokeProcess.Start(); + StartProcessWithOpen(path); } #elif CORECLR catch (Win32Exception ex) when (ex.NativeErrorCode == 193 || ex.NativeErrorCode == 5) From 08f6a439474edf916fe8eb595c0aa401332cc06f Mon Sep 17 00:00:00 2001 From: "Steve Lee [MSFT]" Date: Sat, 15 Jul 2017 14:20:04 -0700 Subject: [PATCH 4/9] reorganized code to make it more readable --- .../namespaces/FileSystemProvider.cs | 60 ++++++++----------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 37ce39ff875..422202ceeb3 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1312,6 +1312,12 @@ private FileSystemInfo GetFileSystemItem(string path, ref bool isContainer, bool /// protected override void InvokeDefaultAction(string path) { +#if UNIX + const int PERMISSION_DENIED_NOT_EXECUTABLE = 13; +#else + const int PERMISSION_DENIED_NOT_EXECUTABLE = 193; +#endif + if (String.IsNullOrEmpty(path)) { throw PSTraceSource.NewArgumentException("path"); @@ -1323,10 +1329,10 @@ protected override void InvokeDefaultAction(string path) string resource = StringUtil.Format(FileSystemProviderStrings.InvokeItemResourceFileTemplate, path); -#if UNIX - void StartProcessWithOpen(string filename) + void StartProcessUsingHandler(string filename) { System.Diagnostics.Process process = new System.Diagnostics.Process(); +#if UNIX // The file is possibly not an executable, so we try invoking the default program that handles this file. const string quoteFormat = "\"{0}\""; process.StartInfo.FileName = Platform.IsLinux ? "xdg-open" : /* OS X */ "open"; @@ -1338,54 +1344,38 @@ void StartProcessWithOpen(string filename) // xdg-open on Ubuntu outputs debug info to stderr, suppress it process.StartInfo.RedirectStandardError = true; process.Start(); - } +#else + if (Platform.IsNanoServer || Platform.IsIoT) { throw new NotSupportedException(); } + process.StartInfo.FileName = filename; + ShellExecuteHelper.Start(process.StartInfo); #endif + } if (ShouldProcess(resource, action)) { - System.Diagnostics.Process invokeProcess = new System.Diagnostics.Process(); try { // special case directories on UNIX so that we can open them in the window-manager // corefx doesn't return error if you try to start a directory -#if UNIX if (Directory.Exists(path)) { - StartProcessWithOpen(path); + StartProcessUsingHandler(path); + } + else + { + System.Diagnostics.Process invokeProcess = new System.Diagnostics.Process(); + // Try Process.Start first. + // - In FullCLR, this is all we need to do + // - In CoreCLR, this works for executables on Win/Unix platforms + invokeProcess.StartInfo.FileName = path; + invokeProcess.Start(); } -#endif - // Try Process.Start first. - // - In FullCLR, this is all we need to do. - // - In CoreCLR, this works for executables on Win/Unix platforms - invokeProcess.StartInfo.FileName = path; - invokeProcess.Start(); - } -#if UNIX - catch (Win32Exception ex) when (ex.NativeErrorCode == 13) - { - // Error code 13 -- Permission denied. - StartProcessWithOpen(path); - } -#elif CORECLR - catch (Win32Exception ex) when (ex.NativeErrorCode == 193 || ex.NativeErrorCode == 5) - { - // Error code 193 -- BAD_EXE_FORMAT (not a valid Win32 application). - // Error code 5 -- ACCESS_DENIED (a folder is not an app) - // If it's headless SKUs, rethrow. - if (Platform.IsNanoServer || Platform.IsIoT) { throw; } - // If it's full Windows, then try ShellExecute. - ShellExecuteHelper.Start(invokeProcess.StartInfo); } -#else - finally + catch (Win32Exception ex) when (ex.NativeErrorCode == PERMISSION_DENIED_NOT_EXECUTABLE) { - // Nothing to do in FullCLR. - // This empty 'finally' block is just to match the 'try' block above so that the code can be organized - // in a clean way without too many if/def's. - // Empty finally block will be ignored in release build, so there is no performance concern. + StartProcessUsingHandler(path); } -#endif } } // InvokeDefaultAction From 1aa5e770e42b22b01385071db28cf6277808ea67 Mon Sep 17 00:00:00 2001 From: "Steve Lee [MSFT]" Date: Sat, 15 Jul 2017 14:39:33 -0700 Subject: [PATCH 5/9] changed fixed sleep to polling to make test more reliable --- .../Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 index 7422e8803ad..44c5d8d630c 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 @@ -68,8 +68,11 @@ Describe "Invoke-Item basic tests" -Tags "CI" { $before = $windows.Count Invoke-Item -Path $PSHOME - # give it time to open - Start-Sleep -Milliseconds 500 + $startTime = Get-Date + while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and ($windows.Count -eq $before)) + { + Start-Sleep -Milliseconds 100 + } $after = $windows.Count $before + 1 | Should Be $after From c04d1be940d0156e3927799b158a86b11ff867fa Mon Sep 17 00:00:00 2001 From: "Steve Lee [MSFT]" Date: Sat, 15 Jul 2017 16:05:00 -0700 Subject: [PATCH 6/9] added validation for Linux --- .../Invoke-Item.Tests.ps1 | 87 ++++++++++++++----- 1 file changed, 66 insertions(+), 21 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 index 44c5d8d630c..e47c110f2ab 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 @@ -60,31 +60,76 @@ Describe "Invoke-Item basic tests" -Tags "CI" { Get-Content $redirectFile -Raw | Should Match "usage: ping" } - It "Should invoke a folder without error" { - if ($IsWindows) - { - $shell = New-Object -ComObject "Shell.Application" - $windows = $shell.Windows() - - $before = $windows.Count - Invoke-Item -Path $PSHOME - $startTime = Get-Date - while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and ($windows.Count -eq $before)) + Context "Invoke a folder" { + BeforeAll { + if ($IsLinux) { - Start-Sleep -Milliseconds 100 + $mimeDefault = xdg-mime query default inode/directory + Remove-Item $HOME/InvokeItemTest.Success -Force -ErrorAction SilentlyContinue + Set-Content -Path $HOME/.local/share/applications/InvokeItemTest.desktop -Force -Value @" +[Desktop Entry] +Version=1.0 +Name=InvokeItemTest +Comment=Validate Invoke-Item for directory +Exec=/bin/sh -c 'echo %u > ~/InvokeItemTest.Success' +Icon=utilities-terminal +Terminal=true +Type=Application +Categories=Application; +"@ + xdg-mime default InvokeItemTest.desktop inode/directory } - $after = $windows.Count + } - $before + 1 | Should Be $after - $item = $windows.Item($after - 1) - $item.LocationURL | Should Match ($PSHOME -replace '\\', '/') - ## close the windows explorer - $item.Quit() + AfterAll { + if ($IsLinux) + { + xdg-mime default $mimeDefault inode/directory + Remove-Item $HOME/.local/share/applications/InvokeItemTest.desktop -Force -ErrorAction SilentlyContinue + Remove-Item $HOME/InvokeItemTest.Success -Force -ErrorAction SilentlyContinue + } } - else - { - # can't validate that it actually opened, but no error should be returned - Invoke-Item -Path $PSHOME + + It "Should invoke a folder without error" { + if ($IsWindows) + { + $shell = New-Object -ComObject "Shell.Application" + $windows = $shell.Windows() + + $before = $windows.Count + Invoke-Item -Path $PSHOME + $startTime = Get-Date + # may take time for explorer to open window + while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and ($windows.Count -eq $before)) + { + Start-Sleep -Milliseconds 100 + } + $after = $windows.Count + + $before + 1 | Should Be $after + $item = $windows.Item($after - 1) + $item.LocationURL | Should Match ($PSHOME -replace '\\', '/') + ## close the windows explorer + $item.Quit() + } + elseif ($IsLinux) + { + # validate on Unix by reassociating default app for directories + Invoke-Item -Path $PSHOME + $startTime = Get-Date + # may take time for handler to start + while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and (-not (Test-Path "$HOME/InvokeItemTest.Success"))) + { + Start-Sleep -Milliseconds 100 + } + + Get-Content $HOME/InvokeItemTest.Success | Should Be $PSHOME + } + else + { + # can't validate on MacOS, so just make sure no error + Invoke-Item -Path $PSHOME + } } } } From 186f8ffc29f1d476a74aee1813e93c91700b3024 Mon Sep 17 00:00:00 2001 From: "Steve Lee [MSFT]" Date: Mon, 17 Jul 2017 21:04:23 -0700 Subject: [PATCH 7/9] reorganized code based on feedback --- .../namespaces/FileSystemProvider.cs | 73 +++++++++---------- .../Invoke-Item.Tests.ps1 | 27 +++++-- 2 files changed, 55 insertions(+), 45 deletions(-) diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 422202ceeb3..4d1efe401b8 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1313,9 +1313,11 @@ private FileSystemInfo GetFileSystemItem(string path, ref bool isContainer, bool protected override void InvokeDefaultAction(string path) { #if UNIX - const int PERMISSION_DENIED_NOT_EXECUTABLE = 13; + // Error code 13 -- Permission denied + const int NOT_EXECUTABLE = 13; #else - const int PERMISSION_DENIED_NOT_EXECUTABLE = 193; + // Error code 193 -- BAD_EXE_FORMAT (not a valid Win32 application) + const int NOT_EXECUTABLE = 193; #endif if (String.IsNullOrEmpty(path)) @@ -1329,52 +1331,47 @@ protected override void InvokeDefaultAction(string path) string resource = StringUtil.Format(FileSystemProviderStrings.InvokeItemResourceFileTemplate, path); - void StartProcessUsingHandler(string filename) - { - System.Diagnostics.Process process = new System.Diagnostics.Process(); -#if UNIX - // The file is possibly not an executable, so we try invoking the default program that handles this file. - const string quoteFormat = "\"{0}\""; - process.StartInfo.FileName = Platform.IsLinux ? "xdg-open" : /* OS X */ "open"; - if (NativeCommandParameterBinder.NeedQuotes(filename)) - { - path = string.Format(CultureInfo.InvariantCulture, quoteFormat, filename); - } - process.StartInfo.Arguments = filename; - // xdg-open on Ubuntu outputs debug info to stderr, suppress it - process.StartInfo.RedirectStandardError = true; - process.Start(); -#else - if (Platform.IsNanoServer || Platform.IsIoT) { throw new NotSupportedException(); } - process.StartInfo.FileName = filename; - ShellExecuteHelper.Start(process.StartInfo); -#endif - } - if (ShouldProcess(resource, action)) { + var invokeProcess = new System.Diagnostics.Process(); + invokeProcess.StartInfo.FileName = path; + bool invokeDefaultProgram = false; - try + if (Directory.Exists(path) && !Platform.IsNanoServer && !Platform.IsIoT) + { + // Path points to a directory and it's not NanoServer or IoT, so we can opne the file explorer + invokeDefaultProgram = true; + } + else { - // special case directories on UNIX so that we can open them in the window-manager - // corefx doesn't return error if you try to start a directory - if (Directory.Exists(path)) + try { - StartProcessUsingHandler(path); + // Try Process.Start first. This works for executables on Win/Unix platforms + invokeProcess.Start(); } - else + catch (Win32Exception ex) when (ex.NativeErrorCode == NOT_EXECUTABLE) { - System.Diagnostics.Process invokeProcess = new System.Diagnostics.Process(); - // Try Process.Start first. - // - In FullCLR, this is all we need to do - // - In CoreCLR, this works for executables on Win/Unix platforms - invokeProcess.StartInfo.FileName = path; - invokeProcess.Start(); + // The file is possibly not an executable. If it's headless SKUs, rethrow. + if (Platform.IsNanoServer || Platform.IsIoT) { throw; } + // Otherwise, try invoking the default program that handles this file. + invokeDefaultProgram = true; } } - catch (Win32Exception ex) when (ex.NativeErrorCode == PERMISSION_DENIED_NOT_EXECUTABLE) + + if (invokeDefaultProgram) { - StartProcessUsingHandler(path); +#if UNIX + const string quoteFormat = "\"{0}\""; + invokeProcess.StartInfo.FileName = Platform.IsLinux ? "xdg-open" : /* OS X */ "open"; + if (NativeCommandParameterBinder.NeedQuotes(path)) + { + path = string.Format(CultureInfo.InvariantCulture, quoteFormat, path); + } + invokeProcess.StartInfo.Arguments = path; + invokeProcess.Start(); +#else + ShellExecuteHelper.Start(invokeProcess.StartInfo); +#endif } } } // InvokeDefaultAction diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 index e47c110f2ab..07857025541 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 @@ -64,9 +64,17 @@ Describe "Invoke-Item basic tests" -Tags "CI" { BeforeAll { if ($IsLinux) { - $mimeDefault = xdg-mime query default inode/directory - Remove-Item $HOME/InvokeItemTest.Success -Force -ErrorAction SilentlyContinue - Set-Content -Path $HOME/.local/share/applications/InvokeItemTest.desktop -Force -Value @" + $appFolder = "$HOME/.local/share/applications" + $NoDesktop = $false + if ($null -ne (gcm xdg-mime -ErrorAction SilentlyContinue)) + { + $mimeDefault = xdg-mime query default inode/directory + Remove-Item $HOME/InvokeItemTest.Success -Force -ErrorAction SilentlyContinue + if (!(Test-Path $appFolder)) + { + New-Item -Path $appFolder -Force + } + Set-Content -Path "$appFolder/InvokeItemTest.desktop" -Verbose -Force -Value @" [Desktop Entry] Version=1.0 Name=InvokeItemTest @@ -77,20 +85,25 @@ Terminal=true Type=Application Categories=Application; "@ - xdg-mime default InvokeItemTest.desktop inode/directory + xdg-mime default InvokeItemTest.desktop inode/directory + } + else + { + $NoDesktop = $true + } } } AfterAll { - if ($IsLinux) + if ($IsLinux -and !$NoDesktop) { xdg-mime default $mimeDefault inode/directory - Remove-Item $HOME/.local/share/applications/InvokeItemTest.desktop -Force -ErrorAction SilentlyContinue + Remove-Item $appFolder/InvokeItemTest.desktop -Force -ErrorAction SilentlyContinue Remove-Item $HOME/InvokeItemTest.Success -Force -ErrorAction SilentlyContinue } } - It "Should invoke a folder without error" { + It "Should invoke a folder without error" -Skip:($NoDesktop) { if ($IsWindows) { $shell = New-Object -ComObject "Shell.Application" From 46442da432b5ee03eb623d42716f161b14ed0f67 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 25 Jul 2017 16:01:16 -0700 Subject: [PATCH 8/9] added MacOS test --- .../Invoke-Item.Tests.ps1 | 58 +++++++++++-------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 index 07857025541..198b0428ee3 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 @@ -28,21 +28,23 @@ Describe "Invoke-Item basic tests" -Tags "CI" { ## Run this test only on OSX because redirecting stderr of 'xdg-open' results in weird behavior in our Linux CI, ## causing this test to fail or the build to hang. - It "Should invoke text file '' without error" -Skip:(!$IsOSX) -TestCases $textFileTestCases { + It "Should invoke text file '' without error on Mac" -Skip:(!$IsOSX) -TestCases $textFileTestCases { param($TestFile) - ## Redirect stderr to a file. So if 'open' failed to open the text file, an error - ## message from 'open' would be written to the redirection file. - $proc = Start-Process -FilePath $powershell -ArgumentList "-noprofile -c Invoke-Item '$TestFile'" ` - -RedirectStandardError $redirectErr ` - -PassThru - $proc.WaitForExit(3000) > $null - if (!$proc.HasExited) { - try { $proc.Kill() } catch { } + $expectedTitle = Split-Path $TestFile -Leaf + $beforeCount = [int]('tell application "TextEdit" to count of windows' | osascript) + Invoke-Item -Path $TestFile + $startTime = Get-Date + $title = [String]::Empty + while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and ($title -ne $expectedTitle)) + { + Start-Sleep -Milliseconds 100 + $title = 'tell application "TextEdit" to get name of front window' | osascript } - ## If the text file was successfully opened, the redirection file should be empty since no error - ## message was written to it. - Get-Content $redirectErr -Raw | Should BeNullOrEmpty + $afterCount = [int]('tell application "TextEdit" to count of windows' | osascript) + $afterCount | Should Be ($beforeCount + 1) + $title | Should Be $expectedTitle + "tell application ""TextEdit"" to close window ""$expectedTitle""" | osascript } } @@ -62,19 +64,15 @@ Describe "Invoke-Item basic tests" -Tags "CI" { Context "Invoke a folder" { BeforeAll { + $supportedEnvironment = $true if ($IsLinux) { $appFolder = "$HOME/.local/share/applications" - $NoDesktop = $false - if ($null -ne (gcm xdg-mime -ErrorAction SilentlyContinue)) + if (Test-Path $appFolder) { $mimeDefault = xdg-mime query default inode/directory Remove-Item $HOME/InvokeItemTest.Success -Force -ErrorAction SilentlyContinue - if (!(Test-Path $appFolder)) - { - New-Item -Path $appFolder -Force - } - Set-Content -Path "$appFolder/InvokeItemTest.desktop" -Verbose -Force -Value @" + Set-Content -Path "$appFolder/InvokeItemTest.desktop" -Force -Value @" [Desktop Entry] Version=1.0 Name=InvokeItemTest @@ -89,13 +87,13 @@ Categories=Application; } else { - $NoDesktop = $true + $supportedEnvironment = $false } } } AfterAll { - if ($IsLinux -and !$NoDesktop) + if ($IsLinux -and $supportedEnvironment) { xdg-mime default $mimeDefault inode/directory Remove-Item $appFolder/InvokeItemTest.desktop -Force -ErrorAction SilentlyContinue @@ -103,7 +101,7 @@ Categories=Application; } } - It "Should invoke a folder without error" -Skip:($NoDesktop) { + It "Should invoke a folder without error" -Skip:(!$supportedEnvironment) { if ($IsWindows) { $shell = New-Object -ComObject "Shell.Application" @@ -135,13 +133,25 @@ Categories=Application; { Start-Sleep -Milliseconds 100 } - Get-Content $HOME/InvokeItemTest.Success | Should Be $PSHOME } else { - # can't validate on MacOS, so just make sure no error + # validate on MacOS by using AppleScript + $beforeCount = [int]('tell application "Finder" to count of windows' | osascript) Invoke-Item -Path $PSHOME + $startTime = Get-Date + $expectedTitle = Split-Path $PSHOME -Leaf + $title = [String]::Empty + while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and ($title -ne $expectedTitle)) + { + Start-Sleep -Milliseconds 100 + $title = 'tell application "Finder" to get name of front window' | osascript + } + $afterCount = [int]('tell application "Finder" to count of windows' | osascript) + $afterCount | Should Be ($beforeCount + 1) + $title | Should Be $expectedTitle + 'tell application "Finder" to close front window' | osascript } } } From 4a4cd71e0cac55cf1938f32b83e07e8c94af32fe Mon Sep 17 00:00:00 2001 From: SteveL-MSFT Date: Thu, 27 Jul 2017 13:47:12 -0700 Subject: [PATCH 9/9] increased timeout for waiting for window to open --- .../Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 index 198b0428ee3..8405dc53034 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Invoke-Item.Tests.ps1 @@ -36,7 +36,7 @@ Describe "Invoke-Item basic tests" -Tags "CI" { Invoke-Item -Path $TestFile $startTime = Get-Date $title = [String]::Empty - while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and ($title -ne $expectedTitle)) + while (((Get-Date) - $startTime).TotalSeconds -lt 10 -and ($title -ne $expectedTitle)) { Start-Sleep -Milliseconds 100 $title = 'tell application "TextEdit" to get name of front window' | osascript @@ -111,7 +111,7 @@ Categories=Application; Invoke-Item -Path $PSHOME $startTime = Get-Date # may take time for explorer to open window - while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and ($windows.Count -eq $before)) + while (((Get-Date) - $startTime).TotalSeconds -lt 10 -and ($windows.Count -eq $before)) { Start-Sleep -Milliseconds 100 } @@ -129,7 +129,7 @@ Categories=Application; Invoke-Item -Path $PSHOME $startTime = Get-Date # may take time for handler to start - while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and (-not (Test-Path "$HOME/InvokeItemTest.Success"))) + while (((Get-Date) - $startTime).TotalSeconds -lt 10 -and (-not (Test-Path "$HOME/InvokeItemTest.Success"))) { Start-Sleep -Milliseconds 100 } @@ -143,7 +143,7 @@ Categories=Application; $startTime = Get-Date $expectedTitle = Split-Path $PSHOME -Leaf $title = [String]::Empty - while (((Get-Date) - $startTime).TotalSeconds -lt 5 -and ($title -ne $expectedTitle)) + while (((Get-Date) - $startTime).TotalSeconds -lt 10 -and ($title -ne $expectedTitle)) { Start-Sleep -Milliseconds 100 $title = 'tell application "Finder" to get name of front window' | osascript