Skip to content
Open
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
3 changes: 3 additions & 0 deletions src/Dotnet.Watch/HotReloadClient/Logging/LogEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#nullable enable

using System;
using System.Runtime.CompilerServices;
using Microsoft.Extensions.Logging;

Expand Down Expand Up @@ -76,5 +77,7 @@ public static void Log<TArg1, TArg2, TArg3>(this ILogger logger, LogEvent<(TArg1
public static readonly LogEvent<string> RefreshServerRunningAt = Create<string>(LogLevel.Debug, "Refresh server running at {0}.");
public static readonly LogEvent<None> ConnectedToRefreshServer = Create(LogLevel.Debug, "Connected to refresh server.");
public static readonly LogEvent<string> ManifestFileNotFound = Create<string>(LogLevel.Debug, "Manifest file '{0}' not found.");
public static readonly LogEvent<string> ProjectSpecifiesCapabilities = Create<string>(LogLevel.Debug, "Project specifies capabilities: {0}.");
public static readonly LogEvent<(Version, string)> UsingCapabilitiesBasedOnTargetFrameworkVersion = Create<(Version, string)>(LogLevel.Debug, "Using capabilities based on project target framework version: '{0}': {1}.");
}

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ internal static class LoggingUtilities
public static ILogger CreateLogger(this ILoggerFactory factory, string componentName, string displayName)
=> factory.CreateLogger($"{componentName}|{displayName}");

public static (string comonentName, string? displayName) ParseCategoryName(string categoryName)
public static (string componentName, string? displayName) ParseCategoryName(string categoryName)
=> categoryName.IndexOf('|') is int index && index > 0
? (categoryName[..index], categoryName[(index + 1)..])
: (categoryName, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ private static ImmutableArray<string> GetUpdateCapabilities(ILogger logger, Immu
var capabilitiesStr = string.Join(", ", capabilities);
if (projectHotReloadCapabilities.IsEmpty)
{
logger.LogDebug("Project specifies capabilities: {Capabilities}.", capabilitiesStr);
// Note that this is not possible with SDK 10+ since the WASM SDK always defines the capabilities in the project,
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The condition was inverted.

// but the code is shared with VS and CDK which might not use the latest SDK.
logger.Log(LogEvents.UsingCapabilitiesBasedOnTargetFrameworkVersion, projectTargetFrameworkVersion, capabilitiesStr);
}
else
{
logger.LogDebug("Using capabilities based on project target framework version: '{Version}': {Capabilities}.", projectTargetFrameworkVersion, capabilitiesStr);
logger.Log(LogEvents.ProjectSpecifiesCapabilities, capabilitiesStr);
}

return capabilities;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public bool IsServerSupported(ProjectGraphNode projectNode, ILogger logger)
return false;
}

logger.Log(MessageDescriptor.ConfiguredToUseBrowserRefresh);
logger.Log(MessageDescriptor.UsingBrowserRefreshMiddleware);
return true;
}
}
6 changes: 4 additions & 2 deletions src/Dotnet.Watch/Watch/UI/IReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ public static MessageDescriptor GetDescriptor(EventId id)
public static readonly MessageDescriptor<(int, string)> UpdateBatchFailedWithError = Create(LogEvents.UpdateBatchFailedWithError, Emoji.HotReload);
public static readonly MessageDescriptor<(int, string)> UpdateBatchExceptionStackTrace = Create(LogEvents.UpdateBatchExceptionStackTrace, Emoji.HotReload);
public static readonly MessageDescriptor<string> Capabilities = Create(LogEvents.Capabilities, Emoji.HotReload);
public static readonly MessageDescriptor<string> ProjectSpecifiesCapabilities = Create(LogEvents.ProjectSpecifiesCapabilities, Emoji.HotReload);
public static readonly MessageDescriptor<(Version, string)> UsingCapabilitiesBasedOnTargetFrameworkVersion = Create(LogEvents.UsingCapabilitiesBasedOnTargetFrameworkVersion, Emoji.HotReload);
public static readonly MessageDescriptor<None> WaitingForFileChangeBeforeRestarting = Create("Waiting for a file to change before restarting ...", Emoji.Wait, LogLevel.Warning);
public static readonly MessageDescriptor<None> WatchingWithHotReload = Create("Watching with Hot Reload.", Emoji.Watch, LogLevel.Debug);
public static readonly MessageDescriptor<None> RestartInProgress = Create("Restart in progress.", Emoji.Restart, LogLevel.Information);
Expand All @@ -220,8 +222,8 @@ public static MessageDescriptor GetDescriptor(EventId id)
public static readonly MessageDescriptor<string> ApplyUpdate_Verbose = Create<string>("{0}", Emoji.Default, LogLevel.Debug);
public static readonly MessageDescriptor<string> ApplyUpdate_AutoVerbose = Create<string>("{0}", Emoji.Default, LogLevel.Debug);
public static readonly MessageDescriptor<string> ApplyUpdate_ChangingEntryPoint = Create<string>("{0} Press \"Ctrl + R\" to restart.", Emoji.Warning, LogLevel.Warning);
public static readonly MessageDescriptor<None> ConfiguredToLaunchBrowser = Create("dotnet-watch is configured to launch a browser on ASP.NET Core application startup.", Emoji.Watch, LogLevel.Debug);
public static readonly MessageDescriptor<None> ConfiguredToUseBrowserRefresh = Create("Using browser-refresh middleware", Emoji.Default, LogLevel.Debug);
public static readonly MessageDescriptor<None> ConfiguredToLaunchBrowser = Create("Configured to launch a browser on ASP.NET Core application startup.", Emoji.Watch, LogLevel.Debug);
public static readonly MessageDescriptor<None> UsingBrowserRefreshMiddleware = Create("Using browser-refresh middleware", Emoji.Default, LogLevel.Debug);
public static readonly MessageDescriptor<string> BrowserRefreshSuppressedViaEnvironmentVariable_ManualRefreshRequired = Create<string>("Browser refresh is suppressed via environment variable '{0}'. To reload static assets after an update refresh browser manually.", Emoji.Watch, LogLevel.Debug);
public static readonly MessageDescriptor<string> BrowserRefreshSuppressedViaEnvironmentVariable_ApplicationWillBeRestarted = Create<string>("Browser refresh is suppressed via environment variable '{0}'. Application will be restarted when updated.", Emoji.Watch, LogLevel.Warning);
public static readonly MessageDescriptor<None> BrowserRefreshNotSupportedByProjectTargetFramework_ManualRefreshRequired = Create("Browser refresh is not supported by the project target framework. To reload static assets after an update refresh browser manually. For more information see 'https://aka.ms/dotnet/watch/unsupported-tfm'.", Emoji.Watch, LogLevel.Warning);
Expand Down
19 changes: 14 additions & 5 deletions test/Microsoft.NET.TestFramework/TestAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,15 @@ internal void FindProjectFiles()
/// <summary>
/// Copies all of the source code from the TestAsset's original location to the previously-configured destination directory.
/// </summary>
/// <returns></returns>
public TestAsset WithSource()
/// <param name="targetFramework">
/// Target framework to use for $(CurrentTargetFramework) property.
/// </param>
/// <param name="packageVersionPropertySubstitutions">
/// Properties used in PackageReference version attributes in the test projects that should be replaced with the given values.
/// </param>
public TestAsset WithSource(
string? targetFramework = null,
IEnumerable<(string versionPropertyName, string version)>? packageVersionPropertySubstitutions = null)
{
_projectFiles = new List<string>();

Expand Down Expand Up @@ -93,10 +100,12 @@ public TestAsset WithSource()
File.Copy(srcFile, destFile, true);
}

targetFramework ??= ToolsetInfo.CurrentTargetFramework;

var substitutions = new[]
{
(propertyName: "TargetFramework", variableName: "CurrentTargetFramework", value: ToolsetInfo.CurrentTargetFramework),
(propertyName: "CurrentTargetFramework", variableName: "CurrentTargetFramework", value: ToolsetInfo.CurrentTargetFramework),
(propertyName: "TargetFramework", variableName: "CurrentTargetFramework", value: targetFramework),
(propertyName: "CurrentTargetFramework", variableName: "CurrentTargetFramework", value: targetFramework),
(propertyName: "RuntimeIdentifier", variableName: "LatestWinRuntimeIdentifier", value: ToolsetInfo.LatestWinRuntimeIdentifier),
(propertyName: "RuntimeIdentifier", variableName: "LatestLinuxRuntimeIdentifier", value: ToolsetInfo.LatestLinuxRuntimeIdentifier),
(propertyName: "RuntimeIdentifier", variableName: "LatestMacRuntimeIdentifier", value: ToolsetInfo.LatestMacRuntimeIdentifier),
Expand All @@ -108,7 +117,7 @@ public TestAsset WithSource()
UpdateProjProperty(propertyName, variableName, value);
}

foreach (var (propertyName, version) in ToolsetInfo.GetPackageVersionProperties())
foreach (var (propertyName, version) in packageVersionPropertySubstitutions ?? ToolsetInfo.GetPackageVersionProperties())
{
ReplacePackageVersionVariable(propertyName, version);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="$(MicrosoftAspNetCoreAppRefPackageVersion)" PrivateAssets="all" />
</ItemGroup>

<!-- Based on .NET 8 breaking change: Add SelfContained if RuntimeIdentifier is defined to match old behavior.. -->
<PropertyGroup Condition="'$(RuntimeIdentifier)' != '' and '$(SelfContained)' == ''">
<SelfContained>true</SelfContained>
</PropertyGroup>

<!-- Enabling compression increases build time. We want to avoid this for tests so we
disable it here. This doesn't affect any test assertions. -->
<PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion test/dotnet-watch.Tests/Browser/BrowserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public async Task BrowserDiagnostics()

App.Start(testAsset, ["--urls", url], relativeProjectDirectory: "RazorApp", testFlags: TestFlags.ReadKeyFromStdin);

await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToUseBrowserRefresh);
await App.WaitUntilOutputContains(MessageDescriptor.UsingBrowserRefreshMiddleware);
await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToLaunchBrowser);
await App.WaitUntilOutputContains(MessageDescriptor.WaitingForChanges);

Expand Down
49 changes: 22 additions & 27 deletions test/dotnet-watch.Tests/HotReload/RazorHotReloadTests.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

namespace Microsoft.DotNet.Watch.UnitTests;

public class RazorHotReloadTests(ITestOutputHelper logger) : DotNetWatchTestBase(logger)
{
[PlatformSpecificTheory(TestPlatforms.Windows | TestPlatforms.OSX)] // https://github.com/dotnet/sdk/issues/53114
[CombinatorialData]
public async Task BlazorWasm(bool projectSpecifiesCapabilities)
[InlineData("net6.0", "6.0.36")]
[InlineData(ToolsetInfo.CurrentTargetFramework, null)]
public async Task BlazorWasm(string tfm, string? wasmVersion)
{
var tfm = ToolsetInfo.CurrentTargetFramework;

var testAsset = TestAssets.CopyTestAsset("WatchBlazorWasm", identifier: projectSpecifiesCapabilities.ToString())
.WithSource();

if (projectSpecifiesCapabilities)
{
testAsset = testAsset.WithProjectChanges(proj =>
{
proj.Root.Descendants()
.First(e => e.Name.LocalName == "PropertyGroup")
.Add(XElement.Parse("""
<WebAssemblyHotReloadCapabilities>Baseline;AddMethodToExistingType</WebAssemblyHotReloadCapabilities>
"""));
});
}
var testAsset = TestAssets.CopyTestAsset("WatchBlazorWasm", identifier: $"{tfm}_{wasmVersion}").WithSource(
targetFramework: tfm,
packageVersionPropertySubstitutions: wasmVersion != null ? [("MicrosoftAspNetCoreAppRefPackageVersion", wasmVersion)] : null);

var port = TestOptions.GetTestPort();
App.Start(testAsset, ["--urls", "http://localhost:" + port], testFlags: TestFlags.MockBrowser);

await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToUseBrowserRefresh);
await App.WaitUntilOutputContains(MessageDescriptor.UsingBrowserRefreshMiddleware);
await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToLaunchBrowser);

// AddExplicitInterfaceImplementation is an implicit capability that's always added for .NET targets:
string[] expectedCapabilities = tfm switch
{
"net6.0" => ["Baseline", "AddMethodToExistingType", "AddStaticFieldToExistingType", "NewTypeDefinition", "ChangeCustomAttributes", "AddExplicitInterfaceImplementation"],
_ => ["Baseline", "AddMethodToExistingType", "AddStaticFieldToExistingType", "NewTypeDefinition", "ChangeCustomAttributes", "AddInstanceFieldToExistingType",
"GenericAddMethodToExistingType", "GenericUpdateMethod", "UpdateParameters", "GenericAddFieldToExistingType", "AddExplicitInterfaceImplementation" ]
};

await App.WaitUntilOutputContains(MessageDescriptor.ProjectSpecifiesCapabilities.GetMessage(string.Join(", ", expectedCapabilities)));

// env variable passed when launching the server:
await App.WaitUntilOutputContains($"HOTRELOAD_DELTA_CLIENT_LOG_MESSAGES=dotnet watch 🕵️ [blazorwasm ({tfm})]");

Expand Down Expand Up @@ -61,8 +57,7 @@ public async Task BlazorWasm(bool projectSpecifiesCapabilities)

UpdateSourceFile(Path.Combine(testAsset.Path, "Pages", "Index.razor"), newSource);

// WebAssemblyHotReloadCapabilities set by project is overwritten in WASM SDK targets:
await App.WaitUntilOutputContains("dotnet watch 🔥 Hot reload capabilities: AddExplicitInterfaceImplementation AddInstanceFieldToExistingType AddMethodToExistingType AddStaticFieldToExistingType Baseline ChangeCustomAttributes GenericAddFieldToExistingType GenericAddMethodToExistingType GenericUpdateMethod NewTypeDefinition UpdateParameters.");
await App.WaitUntilOutputContains($"dotnet watch 🔥 Hot reload capabilities: {string.Join(" ", expectedCapabilities.Order())}.");
await App.WaitUntilOutputContains(MessageDescriptor.ManagedCodeChangesApplied);
}

Expand All @@ -74,7 +69,7 @@ public async Task BlazorWasm_MSBuildWarning()
.WithSource()
.WithProjectChanges(proj =>
{
proj.Root.Descendants()
proj.Root!.Descendants()
.Single(e => e.Name.LocalName == "ItemGroup")
.Add(XElement.Parse("""
<AdditionalFiles Include="Pages\Index.razor" />
Expand All @@ -99,7 +94,7 @@ public async Task BlazorWasm_Restart()

await App.WaitUntilOutputContains(MessageDescriptor.WaitingForChanges);

await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToUseBrowserRefresh);
await App.WaitUntilOutputContains(MessageDescriptor.UsingBrowserRefreshMiddleware);
await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToLaunchBrowser);
await App.WaitUntilOutputContains(MessageDescriptor.PressCtrlRToRestart);

Expand All @@ -120,7 +115,7 @@ public async Task BlazorWasmHosted()
var port = TestOptions.GetTestPort();
App.Start(testAsset, ["--urls", "http://localhost:" + port], "blazorhosted", testFlags: TestFlags.MockBrowser);

await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToUseBrowserRefresh);
await App.WaitUntilOutputContains(MessageDescriptor.UsingBrowserRefreshMiddleware);
await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToLaunchBrowser);
await App.WaitUntilOutputContains(MessageDescriptor.ApplicationKind_BlazorHosted);
}
Expand All @@ -136,7 +131,7 @@ public async Task Razor_Component_ScopedCssAndStaticAssets()

await App.WaitUntilOutputContains(MessageDescriptor.WaitingForChanges);

await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToUseBrowserRefresh);
await App.WaitUntilOutputContains(MessageDescriptor.UsingBrowserRefreshMiddleware);
await App.WaitUntilOutputContains(MessageDescriptor.ConfiguredToLaunchBrowser);
await App.WaitUntilOutputContains(MessageDescriptor.LaunchingBrowser.GetMessage($"http://localhost:{port}"));
App.Process.ClearOutput();
Expand Down
Loading