From cfa34cbb91d1252f7e9a75dc2d78fa3e9ac2baa3 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 30 Dec 2021 10:16:54 -0800 Subject: [PATCH 1/2] Assume basline capabilities (#23185) In a Blazor app, the apply-update capabilities are available after the app is up and running in the browser. Occasionally it takes long for the app to start up and our baseline task times out. Currently we return an empty list of capabilities which causes the compiler to produce no deltas. This PR bumps up the timeout ever so slightly and returns baseline capabilities instead. Fixes https://github.com/dotnet/aspnetcore/issues/36723 --- .../HotReload/BlazorWebAssemblyDeltaApplier.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs index 54aac04ef06f..c65ab9789b2e 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs @@ -10,11 +10,9 @@ using System.Linq; using System.Net.WebSockets; using System.Text; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExternalAccess.Watch.Api; -using Microsoft.Extensions.HotReload; using Microsoft.Extensions.Tools.Internal; namespace Microsoft.DotNet.Watcher.Tools @@ -22,6 +20,7 @@ namespace Microsoft.DotNet.Watcher.Tools internal class BlazorWebAssemblyDeltaApplier : IDeltaApplier { private static Task>? _cachedCapabilties; + private static readonly ImmutableArray _baselineCapabilities = ImmutableArray.Create("Baseline"); private readonly IReporter _reporter; private int _sequenceId; @@ -48,7 +47,7 @@ async Task> GetApplyUpdateCapabilitiesCoreAsync() { if (context.BrowserRefreshServer is null) { - return ImmutableArray.Empty; + return _baselineCapabilities; } await context.BrowserRefreshServer.WaitForClientConnectionAsync(cancellationToken); @@ -58,14 +57,14 @@ async Task> GetApplyUpdateCapabilitiesCoreAsync() var buffer = ArrayPool.Shared.Rent(32 * 1024); try { - // We'll query the browser and ask it send capabilities. If the browser does not respond in 10s, we'll assume something is amiss and return - // no capabilities. This should give you baseline hot reload capabilties. + // We'll query the browser and ask it send capabilities. If the browser does not respond in a short duration, we'll assume something is amiss and return + // baseline capabilities. var response = await context.BrowserRefreshServer.ReceiveAsync(buffer, cancellationToken) .AsTask() - .WaitAsync(TimeSpan.FromSeconds(10), cancellationToken); + .WaitAsync(TimeSpan.FromSeconds(15), cancellationToken); if (!response.HasValue || !response.Value.EndOfMessage || response.Value.MessageType != WebSocketMessageType.Text) { - return ImmutableArray.Empty; + return _baselineCapabilities; } var values = Encoding.UTF8.GetString(buffer.AsSpan(0, response.Value.Count)); @@ -83,7 +82,7 @@ async Task> GetApplyUpdateCapabilitiesCoreAsync() ArrayPool.Shared.Return(buffer); } - return ImmutableArray.Empty; + return _baselineCapabilities; } } From 6752136719614a88e514d6254d3df3fa62c76bbe Mon Sep 17 00:00:00 2001 From: Pranav K Date: Fri, 31 Dec 2021 09:56:49 -0800 Subject: [PATCH 2/2] Use project path instead of working directory to resolve launchSettings.json (#23184) * Use project path instead of working directory to resolve launchSettings.json Fixes https://github.com/dotnet/aspnetcore/issues/35393 * Update DotNetWatcherTests.cs --- .../WatchAppWithLaunchSettings/Program.cs | 2 + .../Properties/launchSettings.json | 10 +++++ .../WatchAppWithLaunchSettings.csproj | 10 +++++ src/BuiltInTools/dotnet-watch/Program.cs | 2 +- .../dotnet-watch.Tests/DotNetWatcherTests.cs | 39 +++++++++++++++++++ .../Utilities/WatchableApp.cs | 4 +- 6 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/Assets/TestProjects/WatchAppWithLaunchSettings/Program.cs create mode 100644 src/Assets/TestProjects/WatchAppWithLaunchSettings/Properties/launchSettings.json create mode 100644 src/Assets/TestProjects/WatchAppWithLaunchSettings/WatchAppWithLaunchSettings.csproj diff --git a/src/Assets/TestProjects/WatchAppWithLaunchSettings/Program.cs b/src/Assets/TestProjects/WatchAppWithLaunchSettings/Program.cs new file mode 100644 index 000000000000..143f5183dfec --- /dev/null +++ b/src/Assets/TestProjects/WatchAppWithLaunchSettings/Program.cs @@ -0,0 +1,2 @@ +Console.WriteLine("Started"); +Console.WriteLine($"Environment: {Environment.GetEnvironmentVariable("EnvironmentFromProfile")}"); diff --git a/src/Assets/TestProjects/WatchAppWithLaunchSettings/Properties/launchSettings.json b/src/Assets/TestProjects/WatchAppWithLaunchSettings/Properties/launchSettings.json new file mode 100644 index 000000000000..f8b1abd49b2b --- /dev/null +++ b/src/Assets/TestProjects/WatchAppWithLaunchSettings/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "app": { + "commandName": "Project", + "environmentVariables": { + "EnvironmentFromProfile": "Development" + } + } + } + } diff --git a/src/Assets/TestProjects/WatchAppWithLaunchSettings/WatchAppWithLaunchSettings.csproj b/src/Assets/TestProjects/WatchAppWithLaunchSettings/WatchAppWithLaunchSettings.csproj new file mode 100644 index 000000000000..74abf5c97664 --- /dev/null +++ b/src/Assets/TestProjects/WatchAppWithLaunchSettings/WatchAppWithLaunchSettings.csproj @@ -0,0 +1,10 @@ + + + + Exe + net6.0 + enable + enable + + + diff --git a/src/BuiltInTools/dotnet-watch/Program.cs b/src/BuiltInTools/dotnet-watch/Program.cs index a60b3de5d031..246f07e76291 100644 --- a/src/BuiltInTools/dotnet-watch/Program.cs +++ b/src/BuiltInTools/dotnet-watch/Program.cs @@ -278,7 +278,7 @@ private async Task MainInternalAsync(IReporter reporter, CommandLineOptions _reporter.Output("Polling file watcher is enabled"); } - var defaultProfile = LaunchSettingsProfile.ReadDefaultProfile(_workingDirectory, reporter) ?? new(); + var defaultProfile = LaunchSettingsProfile.ReadDefaultProfile(processInfo.WorkingDirectory, reporter) ?? new(); var context = new DotNetWatchContext { diff --git a/src/Tests/dotnet-watch.Tests/DotNetWatcherTests.cs b/src/Tests/dotnet-watch.Tests/DotNetWatcherTests.cs index fc71b74b3f3b..e1eb948f67e8 100644 --- a/src/Tests/dotnet-watch.Tests/DotNetWatcherTests.cs +++ b/src/Tests/dotnet-watch.Tests/DotNetWatcherTests.cs @@ -129,5 +129,44 @@ public async Task RunsWithRestoreIfCsprojChanges() message = await app.Process.GetOutputLineStartsWithAsync(messagePrefix, TimeSpan.FromMinutes(2)); Assert.Equal(messagePrefix + " --no-restore -- wait", message.Trim()); } + + [CoreMSBuildOnlyFact] + public async Task Run_WithHotReloadEnabled_ReadsLaunchSettings() + { + var testAsset = _testAssetsManager.CopyTestAsset("WatchAppWithLaunchSettings") + .WithSource() + .Path; + + using var app = new WatchableApp(testAsset, _logger); + + app.DotnetWatchArgs.Add("--verbose"); + + await app.StartWatcherAsync(); + + await app.Process.GetOutputLineAsync("Environment: Development", TimeSpan.FromSeconds(10)); + } + + [CoreMSBuildOnlyFact] + public async Task Run_WithHotReloadEnabled_ReadsLaunchSettings_WhenUsingProjectOption() + { + var testAsset = _testAssetsManager.CopyTestAsset("WatchAppWithLaunchSettings") + .WithSource() + .Path; + + var directoryInfo = new DirectoryInfo(testAsset); + using var app = new WatchableApp(testAsset, _logger) + { + // Configure the working directory to be one level above the test app directory. + WorkingDirectory = Path.GetFullPath(directoryInfo.Parent.FullName), + }; + + app.DotnetWatchArgs.Add("--verbose"); + app.DotnetWatchArgs.Add("--project"); + app.DotnetWatchArgs.Add(Path.Combine(directoryInfo.Name, "WatchAppWithLaunchSettings.csproj")); + + await app.StartWatcherAsync(); + + await app.Process.GetOutputLineAsync("Environment: Development", TimeSpan.FromSeconds(10)); + } } } diff --git a/src/Tests/dotnet-watch.Tests/Utilities/WatchableApp.cs b/src/Tests/dotnet-watch.Tests/Utilities/WatchableApp.cs index d8a036559988..415f9b802219 100644 --- a/src/Tests/dotnet-watch.Tests/Utilities/WatchableApp.cs +++ b/src/Tests/dotnet-watch.Tests/Utilities/WatchableApp.cs @@ -38,6 +38,8 @@ public WatchableApp(string sourceDirectory, ITestOutputHelper logger) public string SourceDirectory { get; } + public string WorkingDirectory { get; set; } + public Task HasRestarted() => HasRestarted(DefaultMessageTimeOut); @@ -76,7 +78,7 @@ public void Start(IEnumerable arguments, [CallerMemberName] string name var commandSpec = new DotnetCommand(_logger, args.ToArray()) { - WorkingDirectory = SourceDirectory, + WorkingDirectory = WorkingDirectory ?? SourceDirectory, }; commandSpec.WithEnvironmentVariable("DOTNET_USE_POLLING_FILE_WATCHER", "true"); commandSpec.WithEnvironmentVariable("__DOTNET_WATCH_RUNNING_AS_TEST", "true");