Skip to content

Commit 18ab0ce

Browse files
authored
Wait for project changes when unchanged project doesn't build (#45167)
1 parent 3906dc5 commit 18ab0ce

File tree

4 files changed

+89
-5
lines changed

4 files changed

+89
-5
lines changed

src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
122122
if (!await BuildProjectAsync(rootProjectOptions.ProjectPath, rootProjectOptions.BuildArguments, iterationCancellationToken))
123123
{
124124
// error has been reported:
125-
waitForFileChangeBeforeRestarting = false;
126-
return;
125+
continue;
127126
}
128127

129128
rootRunningProject = await projectLauncher.TryLaunchProcessAsync(

test/TestAssets/TestProjects/WatchNoDepsApp/WatchNoDepsApp.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@
2121
<Warning Text="The value of property is '$(TestProperty)'" />
2222
</Target>
2323

24+
<ItemGroup>
25+
<!-- add item -->
26+
</ItemGroup>
27+
2428
</Project>

test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,75 @@ public static void Print()
6565
await App.AssertOutputLineStartsWith("Changed!");
6666
}
6767

68+
/// <summary>
69+
/// Unchanged project doesn't build. Wait for source change and rebuild.
70+
/// </summary>
71+
[Fact]
72+
public async Task BaselineCompilationError()
73+
{
74+
var testAsset = TestAssets.CopyTestAsset("WatchNoDepsApp")
75+
.WithSource();
76+
77+
var programPath = Path.Combine(testAsset.Path, "Program.cs");
78+
File.WriteAllText(programPath,
79+
"""
80+
Console.Write
81+
""");
82+
83+
App.Start(testAsset, []);
84+
85+
await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForFileChangeBeforeRestarting, failure: _ => false);
86+
87+
UpdateSourceFile(programPath, """
88+
System.Console.WriteLine("<Updated>");
89+
""");
90+
91+
await App.AssertOutputLineStartsWith("<Updated>");
92+
}
93+
94+
/// <summary>
95+
/// We currently do not support applying project changes.
96+
/// The workaround is to restart via Ctrl+R.
97+
/// </summary>
98+
[Fact]
99+
public async Task ProjectChangeAndRestart()
100+
{
101+
var testAsset = TestAssets.CopyTestAsset("WatchNoDepsApp")
102+
.WithSource();
103+
104+
var programPath = Path.Combine(testAsset.Path, "Program.cs");
105+
var projectPath = Path.Combine(testAsset.Path, "WatchNoDepsApp.csproj");
106+
107+
App.Start(testAsset, ["--no-exit"], testFlags: TestFlags.ReadKeyFromStdin);
108+
109+
await App.AssertOutputLineStartsWith(MessageDescriptor.WaitingForChanges);
110+
111+
// missing System.Linq import:
112+
UpdateSourceFile(programPath, content => content.Replace("""
113+
Console.WriteLine("Started");
114+
""",
115+
"""
116+
Console.WriteLine($">>> {typeof(Enumerable)}");
117+
"""));
118+
119+
await App.AssertOutputLineStartsWith("dotnet watch ⌚ Unable to apply hot reload due to compilation errors.", failure: _ => false);
120+
121+
UpdateSourceFile(projectPath, content => content.Replace("""
122+
<!-- add item -->
123+
""",
124+
"""
125+
<Using Include="System.Linq" />
126+
"""));
127+
128+
// project change not applied:
129+
await App.AssertOutputLineStartsWith("dotnet watch ⌚ Unable to apply hot reload due to compilation errors.", failure: _ => false);
130+
131+
// Ctlr+R rebuilds and restarts:
132+
App.SendControlR();
133+
134+
await App.AssertOutputLineStartsWith(">>> System.Linq.Enumerable", failure: _ => false);
135+
}
136+
68137
[Fact]
69138
public async Task ChangeFileInFSharpProject()
70139
{

test/dotnet-watch.Tests/Utilities/TestReporter.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public bool IsVerbose
2424

2525
public void ReportProcessOutput(OutputLine line)
2626
{
27-
output.WriteLine(line.Content);
27+
WriteTestOutput(line.Content);
2828
ProcessOutput.Add(line.Content);
2929

3030
OnProcessOutput?.Invoke(line);
@@ -34,7 +34,7 @@ public void ReportProcessOutput(ProjectGraphNode project, OutputLine line)
3434
{
3535
var content = $"[{project.GetDisplayName()}]: {line.Content}";
3636

37-
output.WriteLine(content);
37+
WriteTestOutput(content);
3838
ProcessOutput.Add(content);
3939

4040
OnProjectProcessOutput?.Invoke(project.ProjectInstance.FullPath, line);
@@ -67,7 +67,7 @@ public void Report(MessageDescriptor descriptor, string prefix, object?[] args)
6767
{
6868
if (descriptor.TryGetMessage(prefix, args, out var message))
6969
{
70-
output.WriteLine($"{ToString(descriptor.Severity)} {descriptor.Emoji} {message}");
70+
WriteTestOutput($"{ToString(descriptor.Severity)} {descriptor.Emoji} {message}");
7171
}
7272

7373
if (descriptor.Id.HasValue && _actions.TryGetValue(descriptor.Id.Value, out var action))
@@ -76,6 +76,18 @@ public void Report(MessageDescriptor descriptor, string prefix, object?[] args)
7676
}
7777
}
7878

79+
private void WriteTestOutput(string message)
80+
{
81+
try
82+
{
83+
output.WriteLine(message);
84+
}
85+
catch (InvalidOperationException)
86+
{
87+
// May happen when a test is aborted and no longer running.
88+
}
89+
}
90+
7991
private static string ToString(MessageSeverity severity)
8092
=> severity switch
8193
{

0 commit comments

Comments
 (0)