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
5 changes: 5 additions & 0 deletions src/Cli/dotnet/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.CommandLine;
using System.CommandLine.Parsing;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.DotNet.Cli.CommandFactory;
using Microsoft.DotNet.Cli.CommandFactory.CommandResolution;
using Microsoft.DotNet.Cli.Commands.Run;
Expand All @@ -29,6 +30,10 @@ public class Program
public static ITelemetry TelemetryClient;
public static int Main(string[] args)
{
// Register a handler for SIGTERM to allow graceful shutdown of the application on Unix.
// See https://github.com/dotnet/docs/issues/46226.
using var termSignalRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, _ => Environment.Exit(0));

using AutomaticEncodingRestorer _ = new();

// Setting output encoding is not available on those platforms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Runtime.Versioning;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

// <metadata update handler placeholder>

Expand Down
17 changes: 5 additions & 12 deletions test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,22 +92,15 @@ public async Task RunsWithIterationEnvVariable()

App.Start(testAsset, []);

await App.AssertStarted();
await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForFileChangeBeforeRestarting);

var source = Path.Combine(testAsset.Path, "Program.cs");
var contents = File.ReadAllText(source);
const string messagePrefix = "DOTNET_WATCH_ITERATION = ";
App.AssertOutputContains("DOTNET_WATCH_ITERATION = 1");
App.Process.ClearOutput();

var value = await App.AssertOutputLineStartsWith(messagePrefix);
Assert.Equal(1, int.Parse(value, CultureInfo.InvariantCulture));
UpdateSourceFile(Path.Combine(testAsset.Path, "Program.cs"));

await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForFileChangeBeforeRestarting);

UpdateSourceFile(source);
await App.AssertStarted();

value = await App.AssertOutputLineStartsWith(messagePrefix);
Assert.Equal(2, int.Parse(value, CultureInfo.InvariantCulture));
App.AssertOutputContains("DOTNET_WATCH_ITERATION = 2");
}

[Fact]
Expand Down
43 changes: 38 additions & 5 deletions test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ public async Task AutoRestartOnRudeEdit(bool nonInteractive)
await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges);

App.AssertOutputContains(MessageDescriptor.RestartNeededToApplyChanges);
App.AssertOutputContains($"⌚ [auto-restart] {programPath}(38,11): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application.");
App.AssertOutputContains($"⌚ [auto-restart] {programPath}(39,11): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application.");
App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Exited");
App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Launched");
App.Process.ClearOutput();
Expand Down Expand Up @@ -459,7 +459,7 @@ public async Task AutoRestartOnRudeEditAfterRestartPrompt()
await App.AssertOutputLineStartsWith(" ❔ Do you want to restart your app? Yes (y) / No (n) / Always (a) / Never (v)", failure: _ => false);

App.AssertOutputContains(MessageDescriptor.RestartNeededToApplyChanges);
App.AssertOutputContains($"❌ {programPath}(38,11): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application.");
App.AssertOutputContains($"❌ {programPath}(39,11): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application.");
App.Process.ClearOutput();

App.SendKey('a');
Expand All @@ -476,7 +476,7 @@ public async Task AutoRestartOnRudeEditAfterRestartPrompt()
await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges);

App.AssertOutputContains(MessageDescriptor.RestartNeededToApplyChanges);
App.AssertOutputContains($"⌚ [auto-restart] {programPath}(38,1): error ENC0033: Deleting method 'F()' requires restarting the application.");
App.AssertOutputContains($"⌚ [auto-restart] {programPath}(39,1): error ENC0033: Deleting method 'F()' requires restarting the application.");
App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Exited");
App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Launched");
}
Expand Down Expand Up @@ -514,7 +514,7 @@ public async Task AutoRestartOnNoEffectEdit(bool nonInteractive)
await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges);

App.AssertOutputContains(MessageDescriptor.RestartNeededToApplyChanges);
App.AssertOutputContains($"⌚ [auto-restart] {programPath}(16,19): warning ENC0118: Changing 'top-level code' might not have any effect until the application is restarted.");
App.AssertOutputContains($"⌚ [auto-restart] {programPath}(17,19): warning ENC0118: Changing 'top-level code' might not have any effect until the application is restarted.");
App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Exited");
App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Launched");
App.AssertOutputContains("<Updated>");
Expand Down Expand Up @@ -721,6 +721,8 @@ class AppUpdateHandler
[PlatformSpecificFact(TestPlatforms.Windows)]
public async Task GracefulTermination_Windows()
{
var tfm = ToolsetInfo.CurrentTargetFramework;

var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp")
.WithSource();

Expand All @@ -739,7 +741,7 @@ public async Task GracefulTermination_Windows()

await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges);

await App.WaitUntilOutputContains(new Regex(@"dotnet watch 🕵️ \[.*\] Windows Ctrl\+C handling enabled."));
await App.WaitUntilOutputContains($"dotnet watch 🕵️ [WatchHotReloadApp ({tfm})] Windows Ctrl+C handling enabled.");

await App.WaitUntilOutputContains("Started");

Expand All @@ -749,6 +751,37 @@ public async Task GracefulTermination_Windows()
await App.WaitUntilOutputContains("exited with exit code 0.");
}

[PlatformSpecificFact(TestPlatforms.AnyUnix)]
public async Task GracefulTermination_Unix()
{
var tfm = ToolsetInfo.CurrentTargetFramework;

var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp")
.WithSource();

var programPath = Path.Combine(testAsset.Path, "Program.cs");

UpdateSourceFile(programPath, src => src.Replace("// <metadata update handler placeholder>", """
using var termSignalRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, _ =>
{
Console.WriteLine("SIGTERM detected! Performing cleanup...");
});
"""));

App.Start(testAsset, [], testFlags: TestFlags.ReadKeyFromStdin);

await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges);

await App.WaitUntilOutputContains($"dotnet watch 🕵️ [WatchHotReloadApp ({tfm})] Posix signal handlers registered.");

await App.WaitUntilOutputContains("Started");

App.SendControlC();

await App.WaitForOutputLineContaining("SIGTERM detected! Performing cleanup...");
await App.WaitUntilOutputContains("exited with exit code 0.");
}

[PlatformSpecificTheory(TestPlatforms.Windows, Skip = "https://github.com/dotnet/sdk/issues/49928")] // https://github.com/dotnet/aspnetcore/issues/63759
[CombinatorialData]
public async Task BlazorWasm(bool projectSpecifiesCapabilities)
Expand Down
Loading