diff --git a/src/dotnet-watch/Internal/ProcessRunner.cs b/src/dotnet-watch/Internal/ProcessRunner.cs index c866b6db14..a5f7cac8ef 100644 --- a/src/dotnet-watch/Internal/ProcessRunner.cs +++ b/src/dotnet-watch/Internal/ProcessRunner.cs @@ -105,9 +105,23 @@ namespace Microsoft.DotNet.Watcher.Internal { _process = process; _process.Exited += OnExited; + Task = _tcs.Task.ContinueWith(_ => + { + // We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously + // this code used Process.Exited, which could result in us missing some output due to the ordering of + // events. + // + // See the remarks here: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit#System_Diagnostics_Process_WaitForExit_System_Int32_ + if (!process.WaitForExit(Int32.MaxValue)) + { + throw new TimeoutException(); + } + + process.WaitForExit(); + }); } - public Task Task => _tcs.Task; + public Task Task { get; } public void TryKill() { diff --git a/test/dotnet-watch.FunctionalTests/AwaitableProcess.cs b/test/dotnet-watch.FunctionalTests/AwaitableProcess.cs index 25a0189315..91b53133eb 100644 --- a/test/dotnet-watch.FunctionalTests/AwaitableProcess.cs +++ b/test/dotnet-watch.FunctionalTests/AwaitableProcess.cs @@ -116,6 +116,8 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests private void OnExit(object sender, EventArgs args) { + // Wait to ensure the process has exited and all output consumed + _process.WaitForExit(); _source.Complete(); } diff --git a/test/dotnet-watch.FunctionalTests/NoDepsAppTests.cs b/test/dotnet-watch.FunctionalTests/NoDepsAppTests.cs index 2cbf841ae0..ea9ffbec03 100644 --- a/test/dotnet-watch.FunctionalTests/NoDepsAppTests.cs +++ b/test/dotnet-watch.FunctionalTests/NoDepsAppTests.cs @@ -41,7 +41,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests Assert.Throws(() => Process.GetProcessById(pid)); } - [Fact(Skip="https://github.com/aspnet/DotNetTools/issues/407")] + [Fact] public async Task RestartProcessThatTerminatesAfterFileChange() { await _app.StartWatcherAsync(); diff --git a/test/dotnet-watch.FunctionalTests/Scenario/WatchableApp.cs b/test/dotnet-watch.FunctionalTests/Scenario/WatchableApp.cs index 7725812bd6..16bfbacc8d 100644 --- a/test/dotnet-watch.FunctionalTests/Scenario/WatchableApp.cs +++ b/test/dotnet-watch.FunctionalTests/Scenario/WatchableApp.cs @@ -18,6 +18,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests private const string StartedMessage = "Started"; private const string ExitingMessage = "Exiting"; + private const string WatchExitedMessage = "watch : Exited"; private readonly ITestOutputHelper _logger; private string _appName; @@ -42,8 +43,11 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests public Task HasRestarted() => Process.GetOutputLineAsync(StartedMessage, DefaultMessageTimeOut); - public Task HasExited() - => Process.GetOutputLineAsync(ExitingMessage, DefaultMessageTimeOut); + public async Task HasExited() + { + await Process.GetOutputLineAsync(ExitingMessage, DefaultMessageTimeOut); + await Process.GetOutputLineAsync(WatchExitedMessage, DefaultMessageTimeOut); + } public bool UsePollingWatcher { get; set; }