Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/System.Management.Automation/engine/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1564,6 +1564,7 @@ public static class InternalTestHooks
internal static bool ForceScriptBlockLogging;
internal static bool UseDebugAmsiImplementation;
internal static bool BypassAppLockerPolicyCaching;
internal static bool BypassOnlineHelpRetrieval;
// It's useful to test that we don't depend on the ScriptBlock and AST objects and can use a re-parsed version.
internal static bool IgnoreScriptBlockCache;

Expand Down
74 changes: 71 additions & 3 deletions src/System.Management.Automation/help/HelpCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
using System.Management.Automation.Internal;
using System.Management.Automation.Help;
using System.Runtime.InteropServices;
using System.IO;
#if !UNIX
using Microsoft.Win32;
#endif

namespace Microsoft.PowerShell.Commands
{
Expand Down Expand Up @@ -615,18 +619,39 @@ private void LaunchOnlineHelp(Uri uriToLaunch)
"https");
}

// we use this test hook is to avoid actually calling out to another process
if (InternalTestHooks.BypassOnlineHelpRetrieval)
{
this.WriteObject(string.Format(CultureInfo.InvariantCulture, HelpDisplayStrings.OnlineHelpUri, uriToLaunch.OriginalString));
return;
}

Exception exception = null;
bool wrapCaughtException = true;
try
{
this.WriteVerbose(string.Format(CultureInfo.InvariantCulture, HelpDisplayStrings.OnlineHelpUri, uriToLaunch.OriginalString));
System.Diagnostics.Process browserProcess = new System.Diagnostics.Process();

#if UNIX
browserProcess.StartInfo.FileName = Platform.IsLinux ? "xdg-open" : /* OS X */ "open";
browserProcess.StartInfo.Arguments = uriToLaunch.OriginalString;
browserProcess.Start();
#elif CORECLR
throw new PlatformNotSupportedException();
// On FullCLR, ProcessStartInfo.UseShellExecute is true by default. This means that the shell will be used when starting the process.
// On CoreCLR, UseShellExecute is not supported. To work around this, we check if there is a default browser in the system.
// If there is, we lunch it to open the HelpURI. If there isn't, we error out.
string webBrowserPath = GetDefaultWebBrowser();
if (webBrowserPath == null)
{
wrapCaughtException = false;
exception = PSTraceSource.NewInvalidOperationException(HelpErrors.CannotLaunchURI, uriToLaunch.OriginalString);
}
else
{
browserProcess.StartInfo = new ProcessStartInfo(webBrowserPath);
browserProcess.StartInfo.Arguments = "\"" + uriToLaunch.OriginalString + "\"";
browserProcess.Start();
}
#else
browserProcess.StartInfo.FileName = uriToLaunch.OriginalString;
browserProcess.Start();
Expand All @@ -643,10 +668,53 @@ private void LaunchOnlineHelp(Uri uriToLaunch)

if (null != exception)
{
throw PSTraceSource.NewInvalidOperationException(exception, HelpErrors.CannotLaunchURI, uriToLaunch.OriginalString);
if (wrapCaughtException)
throw PSTraceSource.NewInvalidOperationException(exception, HelpErrors.CannotLaunchURI, uriToLaunch.OriginalString);
else
throw exception;
}
}

#if !UNIX
/// <summary>
/// Gets the path to the default browser by querying the Windows registry.
/// </summary>
/// <returns></returns>
private string GetDefaultWebBrowser()
{
// Check if there is a default browser in the system.
const string httpRegkey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice";
object progId = Registry.GetValue(httpRegkey, "ProgId", null);
if (progId != null)
{
// Query the registry to find the web browser path.
using (RegistryKey browserRegKey = Registry.ClassesRoot.OpenSubKey(progId + "\\shell\\open\\command", false))
{
string browserPath = browserRegKey?.GetValue(null)?.ToString().Replace(/* remove the quotes */ "\"", "");
if (!string.IsNullOrEmpty(browserPath))
{
const string exeExtension = ".exe";
if (!browserPath.EndsWith(exeExtension, StringComparison.OrdinalIgnoreCase))
{
// Remove any extra chars in the path after ".exe".
int extIndex = browserPath.LastIndexOf(exeExtension, StringComparison.OrdinalIgnoreCase);
browserPath = extIndex > 0 ? browserPath.Substring(0, extIndex + exeExtension.Length) : string.Empty;
}

// Make sure the path to the default browser exists.
if (File.Exists(browserPath))
{
return browserPath;
}
}
}
}

// By default, return null.
return null;
}
#endif

#endregion

private void HelpSystem_OnProgress(object sender, HelpProgressInfo arg)
Expand Down
94 changes: 94 additions & 0 deletions test/powershell/engine/HelpSystem.OnlineHelp.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
Describe 'Online help tests for PowerShell Core Cmdlets' -Tags "CI" {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Francisco Gamino (@Francisco-Gamino) is almost ready on the fix of #951. This test will be updated in the PR for that issue.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue #2465 is opened to track the update to the test files here.


# The csv files (V2Cmdlets.csv and V3Cmdlets.csv) contain a list of cmdlets and expected HelpURIs.
# The HelpURI is part of the cmdlet metadata, and when the user runs 'get-help <cmdletName> -online'
# the browser navigates to the address in the HelpURI. However, if a help file is present, the HelpURI
# on the file take precedence over the one in the cmdlet metadata. Therefore, the help content
# in the box needs to be deleted before running the tests, because otherwise, the HelpURI
# (when calling get-help -online) might not matched the one in the csv file.

# Currently update-help does not work on Linux (see https://github.com/PowerShell/PowerShell/issues/951),
# so if the help content is deleted, we do not have a way to install it back, and any subsequent
# 'get-help' tests which depend on the help content will fail. Until this update-help issue is fixed,
# we need to skip these tests on Linux and OSX.

$skipTest = ([System.Management.Automation.Platform]::IsLinux -or [System.Management.Automation.Platform]::IsOSX)

BeforeAll {

# There is a bug in Linux in which a 'Boolean value defined inside Describe block cannot be evaluated properly
# in if statement inside BeforeAll block on Linux' Becuase of this, I need to define the bool value again inside the block.
# See https://github.com/PowerShell/PowerShell/issues/2445
#
$skipTest = ([System.Management.Automation.Platform]::IsLinux -or [System.Management.Automation.Platform]::IsOSX)

if (-not $skipTest)
{
# Enable the test hook
[system.management.automation.internal.internaltesthooks]::SetTestHook('BypassOnlineHelpRetrieval', $true)

# Remove the help content
Write-Verbose "Deleting help content for get-help -online tests" -Verbose
$path = Join-Path $PSHOME (Get-UICulture).Name
Get-ChildItem "$path\*dll-help.xml" -Recurse -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue
}
}

AfterAll {
if (-not $skipTest)
{
# Disable the test hook
[system.management.automation.internal.internaltesthooks]::SetTestHook('BypassOnlineHelpRetrieval', $false)
}
}

foreach ($filePath in @("$PSScriptRoot\testdata\HelpURI\V2Cmdlets.csv", "$PSScriptRoot\testdata\HelpURI\V3Cmdlets.csv"))
{
$cmdletList = Import-Csv $filePath -ea Stop

foreach ($cmdlet in $cmdletList)
{
# If the cmdlet is not preset in CoreCLR, skip it.
if (Get-Command $cmdlet.TopicTitle -ea SilentlyContinue)
{
# TopicTitle - is the cmdlet name in the csv file
# HelpURI - is the expected help URI in the csv file

It "Validate 'get-help $($cmdlet.TopicTitle) -Online'" -skip:$skipTest {
$actualURI = Get-Help $cmdlet.TopicTitle -Online
$actualURI = $actualURI.Replace("Help URI: ","")
$actualURI | Should Be $cmdlet.HelpURI
}
}
}
}
}

Describe 'Get-Help -Online opens the default web browser and navigates to the cmdlet help content' -Tags "Feature" {

$skipTest = [System.Management.Automation.Platform]::IsIoT -or
[System.Management.Automation.Platform]::IsNanoServer

It "Get-Help get-process -online" -skip:$skipTest {

{ Get-Help get-process -online } | Should Not Throw
}
}

Describe 'Get-Help -Online is not supported on Nano Server and IoT' -Tags "CI" {

$skipTest = -not ([System.Management.Automation.Platform]::IsIoT -or [System.Management.Automation.Platform]::IsNanoServer)

It "Get-help -online <cmdletName> throws InvalidOperation." -skip:$skipTest {

try
{
Get-Help Get-Help -Online
throw "Execution should not have succeeded"
}
catch
{
$_.FullyQualifiedErrorId | Should Be "InvalidOperation,Microsoft.PowerShell.Commands.GetHelpCommand"
}
}
}
Loading