Skip to content

Commit 573598c

Browse files
authored
Browser tests (#50844)
1 parent a73f745 commit 573598c

File tree

26 files changed

+463
-171
lines changed

26 files changed

+463
-171
lines changed

Directory.Build.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
<ItemGroup Condition="$(MicrosoftAspNetCoreAppRefPackageVersion.StartsWith('$(_TargetFrameworkVersionWithoutV)'))">
7474
<KnownFrameworkReference Update="Microsoft.AspNetCore.App">
7575
<LatestRuntimeFrameworkVersion>$(MicrosoftAspNetCoreAppRefPackageVersion)</LatestRuntimeFrameworkVersion>
76-
<RuntimePackRuntimeIdentifiers>${SupportedRuntimeIdentifiers}</RuntimePackRuntimeIdentifiers>
76+
<RuntimePackRuntimeIdentifiers>$(SupportedRuntimeIdentifiers)</RuntimePackRuntimeIdentifiers>
7777
<TargetingPackVersion>$(MicrosoftAspNetCoreAppRefPackageVersion)</TargetingPackVersion>
7878
<DefaultRuntimeFrameworkVersion>$(MicrosoftAspNetCoreAppRefPackageVersion)</DefaultRuntimeFrameworkVersion>
7979
</KnownFrameworkReference>

sdk.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@
312312
<Project Path="test/dotnet-format.UnitTests/dotnet-format.UnitTests.csproj" />
313313
<Project Path="test/dotnet-MsiInstallation.Tests/dotnet-MsiInstallation.Tests.csproj" />
314314
<Project Path="test/dotnet-new.IntegrationTests/dotnet-new.IntegrationTests.csproj" />
315+
<Project Path="test/dotnet-watch-test-browser/dotnet-watch-test-browser.csproj" />
315316
<Project Path="test/dotnet-watch.Tests/dotnet-watch.Tests.csproj" />
316317
<Project Path="test/dotnet.Tests/dotnet.Tests.csproj" />
317318
<Project Path="test/EndToEnd.Tests/EndToEnd.Tests.csproj" />

src/BuiltInTools/HotReloadClient/HotReloadClients.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public async Task ApplyStaticAssetUpdatesAsync(IEnumerable<(string filePath, str
173173
#endif
174174
content = ImmutableCollectionsMarshal.AsImmutableArray(blob);
175175
}
176-
catch (Exception e)
176+
catch (Exception e) when (e is not OperationCanceledException)
177177
{
178178
ClientLogger.LogError("Failed to read file {FilePath}: {Message}", filePath, e.Message);
179179
continue;

src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ public static void Log(this ILogger logger, LogEvent logEvent, params object[] a
3030
public static readonly LogEvent UpdatingDiagnostics = Create(LogLevel.Debug, "Updating diagnostics.");
3131
public static readonly LogEvent SendingStaticAssetUpdateRequest = Create(LogLevel.Debug, "Sending static asset update request to connected browsers: '{0}'.");
3232
public static readonly LogEvent RefreshServerRunningAt = Create(LogLevel.Debug, "Refresh server running at {0}.");
33+
public static readonly LogEvent ConnectedToRefreshServer = Create(LogLevel.Debug, "Connected to refresh server.");
3334
}

src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ public async Task WaitForClientConnectionAsync(CancellationToken cancellationTok
163163
}, progressCancellationSource.Token);
164164

165165
// Work around lack of Task.WaitAsync(cancellationToken) on .NET Framework:
166-
cancellationToken.Register(() => _browserConnected.SetCanceled());
166+
cancellationToken.Register(() => _browserConnected.TrySetCanceled());
167167

168168
try
169169
{

src/BuiltInTools/HotReloadClient/Web/BrowserConnection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public BrowserConnection(WebSocket clientSocket, string? sharedSecret, ILoggerFa
3737
ServerLogger = loggerFactory.CreateLogger(ServerLogComponentName, displayName);
3838
AgentLogger = loggerFactory.CreateLogger(AgentLogComponentName, displayName);
3939

40-
ServerLogger.LogDebug("Connected to referesh server.");
40+
ServerLogger.Log(LogEvents.ConnectedToRefreshServer);
4141
}
4242

4343
public void Dispose()

src/BuiltInTools/dotnet-watch.slnf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
"src\\BuiltInTools\\HotReloadClient\\Microsoft.DotNet.HotReload.Client.shproj",
1919
"src\\BuiltInTools\\dotnet-watch\\dotnet-watch.csproj",
2020
"test\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests.csproj",
21+
"test\\Microsoft.DotNet.HotReload.Client.Tests\\Microsoft.DotNet.HotReload.Client.Tests.csproj",
2122
"test\\Microsoft.Extensions.DotNetDeltaApplier.Tests\\Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj",
2223
"test\\Microsoft.NET.TestFramework\\Microsoft.NET.TestFramework.csproj",
2324
"test\\Microsoft.WebTools.AspireService.Tests\\Microsoft.WebTools.AspireService.Tests.csproj",
24-
"test\\Microsoft.DotNet.HotReload.Client.Tests\\Microsoft.DotNet.HotReload.Client.Tests.csproj",
25+
"test\\dotnet-watch-test-browser\\dotnet-watch-test-browser.csproj",
2526
"test\\dotnet-watch.Tests\\dotnet-watch.Tests.csproj"
2627
]
2728
}

src/BuiltInTools/dotnet-watch/Browser/BrowserLauncher.cs

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
using System.Collections.Immutable;
55
using System.Diagnostics;
66
using System.Diagnostics.CodeAnalysis;
7+
using System.Runtime.CompilerServices;
78
using Microsoft.Build.Graph;
89
using Microsoft.DotNet.HotReload;
910
using Microsoft.Extensions.Logging;
1011

1112
namespace Microsoft.DotNet.Watch;
1213

13-
internal sealed class BrowserLauncher(ILogger logger, EnvironmentOptions environmentOptions)
14+
internal sealed class BrowserLauncher(ILogger logger, IProcessOutputReporter processOutputReporter, EnvironmentOptions environmentOptions)
1415
{
1516
// interlocked
1617
private ImmutableHashSet<ProjectInstanceId> _browserLaunchAttempted = [];
@@ -61,18 +62,13 @@ public static string GetLaunchUrl(string? profileLaunchUrl, string outputLaunchU
6162

6263
private void LaunchBrowser(string launchUrl, AbstractBrowserRefreshServer? server)
6364
{
64-
var fileName = launchUrl;
65+
var (fileName, arg, useShellExecute) = environmentOptions.BrowserPath is { } browserPath
66+
? (browserPath, launchUrl, false)
67+
: (launchUrl, null, true);
6568

66-
var args = string.Empty;
67-
if (environmentOptions.BrowserPath is { } browserPath)
68-
{
69-
args = fileName;
70-
fileName = browserPath;
71-
}
69+
logger.Log(MessageDescriptor.LaunchingBrowser, fileName, arg);
7270

73-
logger.LogDebug("Launching browser: {FileName} {Args}", fileName, args);
74-
75-
if (environmentOptions.TestFlags != TestFlags.None)
71+
if (environmentOptions.TestFlags != TestFlags.None && environmentOptions.BrowserPath == null)
7672
{
7773
if (environmentOptions.TestFlags.HasFlag(TestFlags.MockBrowser))
7874
{
@@ -83,29 +79,23 @@ private void LaunchBrowser(string launchUrl, AbstractBrowserRefreshServer? serve
8379
return;
8480
}
8581

86-
var info = new ProcessStartInfo
82+
// dotnet-watch, by default, relies on URL file association to launch browsers. On Windows and MacOS, this works fairly well
83+
// where URLs are associated with the default browser. On Linux, this is a bit murky.
84+
// From emperical observation, it's noted that failing to launch a browser results in either Process.Start returning a null-value
85+
// or for the process to have immediately exited.
86+
// We can use this to provide a helpful message.
87+
var processSpec = new ProcessSpec()
8788
{
88-
FileName = fileName,
89-
Arguments = args,
90-
UseShellExecute = true,
89+
Executable = fileName,
90+
Arguments = arg != null ? [arg] : [],
91+
UseShellExecute = useShellExecute,
92+
OnOutput = environmentOptions.TestFlags.HasFlag(TestFlags.RedirectBrowserOutput) ? processOutputReporter.ReportOutput : null,
9193
};
9294

93-
try
94-
{
95-
using var browserProcess = Process.Start(info);
96-
if (browserProcess is null or { HasExited: true })
97-
{
98-
// dotnet-watch, by default, relies on URL file association to launch browsers. On Windows and MacOS, this works fairly well
99-
// where URLs are associated with the default browser. On Linux, this is a bit murky.
100-
// From emperical observation, it's noted that failing to launch a browser results in either Process.Start returning a null-value
101-
// or for the process to have immediately exited.
102-
// We can use this to provide a helpful message.
103-
logger.LogInformation("Unable to launch the browser. Url '{Url}'.", launchUrl);
104-
}
105-
}
106-
catch (Exception e)
95+
using var browserProcess = ProcessRunner.TryStartProcess(processSpec, logger);
96+
if (browserProcess is null or { HasExited: true })
10797
{
108-
logger.LogDebug("Failed to launch a browser: {Message}", e.Message);
98+
logger.LogWarning("Unable to launch the browser. Url '{Url}'.", launchUrl);
10999
}
110100
}
111101

src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ internal static class ProjectGraphUtilities
4343
}
4444
catch (Exception e) when (e is not OperationCanceledException)
4545
{
46+
// ProejctGraph aggregates OperationCanceledException exception,
47+
// throw here to propagate the cancellation.
48+
cancellationToken.ThrowIfCancellationRequested();
49+
4650
logger.LogDebug("Failed to load project graph.");
4751

4852
if (e is AggregateException { InnerExceptions: var innerExceptions })

src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentOptions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ internal enum TestFlags
2222
/// This allows tests to trigger key based events.
2323
/// </summary>
2424
ReadKeyFromStdin = 1 << 3,
25+
26+
/// <summary>
27+
/// Redirects the output of the launched browser process to watch output.
28+
/// </summary>
29+
RedirectBrowserOutput = 1 << 4,
2530
}
2631

2732
internal sealed record EnvironmentOptions(

0 commit comments

Comments
 (0)