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
34 changes: 21 additions & 13 deletions test/dotnet-watch.Tests/Aspire/AspireLauncherIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,21 @@ public async Task ServerAndResources()
serviceA.Start(testAsset, ["--entrypoint", serviceProjectA]);
serviceB.Start(testAsset, ["--entrypoint", serviceProjectB]);

using var statusCancellationSource = new CancellationTokenSource();
var statusReaderTask = PipeUtilities.ReadStatusEventsAsync(statusPipeName, statusCancellationSource.Token);
// The expected status events delivered by the server over the status pipe (listed in causal order;
// the assertion below compares them order-independently). The reader stops once it has received this
// many events, so we don't need to cancel based on the server's output, which races with delivery.
string[] expectedStatusEvents =
[
$"type=build_complete, projects=[{serviceProjectA};{serviceProjectB}]",
$"type=building, projects=[{serviceProjectA};{serviceProjectB}]",
$"type=hot_reload_applied, projects=[{serviceProjectA};{serviceProjectB}]",
$"type=process_started, projects=[{serviceProjectA}]",
$"type=process_started, projects=[{serviceProjectA}]",
$"type=process_started, projects=[{serviceProjectB}]",
$"type=restarting, projects=[{serviceProjectA}]",
];

var statusReaderTask = PipeUtilities.ReadStatusEventsAsync(statusPipeName, expectedStatusEvents.Length, TestContext.CancellationToken);

server.Start(testAsset,
[
Expand Down Expand Up @@ -168,19 +181,14 @@ await controlPipeWriter.WriteLineAsync(JsonSerializer.Serialize(new WatchControl
// initial updates are applied when the process restarts:
await server.WaitUntilOutputContains(MessageDescriptor.SendingUpdateBatch.GetMessage(0), $"A ({tfm})");

statusCancellationSource.Cancel();
// The reader completes once all expected status events have been received (or the test times out),
// so there is no race between cancellation and status delivery.
var statusEvents = await statusReaderTask;

// validate that we received the expected status events from the server, ignoring the order:
// validate that we received the expected status events from the server, ignoring the order
// (both sequences are sorted so the comparison does not depend on event arrival order):
AssertEx.SequenceEqual(
[
$"type=build_complete, projects=[{serviceProjectA};{serviceProjectB}]",
$"type=building, projects=[{serviceProjectA};{serviceProjectB}]",
$"type=hot_reload_applied, projects=[{serviceProjectA};{serviceProjectB}]",
$"type=process_started, projects=[{serviceProjectA}]",
$"type=process_started, projects=[{serviceProjectA}]",
$"type=process_started, projects=[{serviceProjectB}]",
$"type=restarting, projects=[{serviceProjectA}]",
], statusEvents.Select(e => $"type={e.Type}, projects=[{string.Join(";", e.Projects.Order())}]").Order());
expectedStatusEvents.Order(),
statusEvents.Select(e => $"type={e.Type}, projects=[{string.Join(";", e.Projects.Order())}]").Order());
Comment on lines 190 to +192
}
}
7 changes: 5 additions & 2 deletions test/dotnet-watch.Tests/Aspire/PipeUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Microsoft.DotNet.Watch.UnitTests;

internal static class PipeUtilities
{
public static async Task<IReadOnlyList<WatchStatusEvent>> ReadStatusEventsAsync(string pipeName, CancellationToken cancellationToken)
public static async Task<IReadOnlyList<WatchStatusEvent>> ReadStatusEventsAsync(string pipeName, int expectedEventCount, CancellationToken cancellationToken)
{
var lines = new List<WatchStatusEvent>();

Expand All @@ -20,7 +20,10 @@ public static async Task<IReadOnlyList<WatchStatusEvent>> ReadStatusEventsAsync(

try
{
while (!cancellationToken.IsCancellationRequested)
// Read until we have observed the expected number of events. The status events are delivered
// over a separate pipe from the server's output, so stopping based on output (instead of the
// event count) races with delivery and can drop trailing events.
while (lines.Count < expectedEventCount && !cancellationToken.IsCancellationRequested)
{
var line = await reader.ReadLineAsync(cancellationToken);
if (line == null)
Expand Down
6 changes: 5 additions & 1 deletion test/dotnet-watch.Tests/FileWatcher/FileWatcherTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,10 @@ await TestOperation(
],
usePolling,
watchSubdirectories: true,
() => Directory.Delete(subdir, recursive: true));
() => Directory.Delete(subdir, recursive: true),
// Restrict to the watched files so the macOS event replay (which intermittently includes a
// directory-level "subdir" Add event) doesn't displace one of the expected file events and
// make the test flaky.
watchedFileNames: ["foo1", "foo2", "foo3"]);
}
}
Loading