Skip to content
Draft
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
419 changes: 419 additions & 0 deletions eng/scripts/analyze-watch-test-output.cs

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion src/Cli/dotnet/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,15 @@ public static int Main(string[] args)
{
// If telemetry object has not been initialized yet. It cannot be collected
TelemetryEventEntry.SendFiltered(e);
Reporter.Error.WriteLine(e.ToString().Red().Bold());

var str = e.ToString();
if (str.Contains("Internal CLR error"))
{
Reporter.Error.WriteLine("!!!".Red().Bold());
Debugger.Break();
}

Reporter.Error.WriteLine(str.Red().Bold());

return 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private async Task ListenForResponsesAsync(CancellationToken cancellationToken)
{
if (!cancellationToken.IsCancellationRequested)
{
Logger.LogError("Failed to read response: {Message}", e.Message);
Logger.LogError("Failed to read response: {Exception}", e.ToString());
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/Dotnet.Watch/Watch/Build/ProjectBuildManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ internal sealed class ProjectBuildManager(ProjectCollection collection, BuildRep
public readonly ProjectCollection Collection = collection;
public readonly BuildReporter BuildReporter = reporter;

/// <summary>
/// Used by tests to ensure no more than one build is running at a time, which is required by MSBuild.
/// </summary>
internal static SemaphoreSlim Test_BuildSemaphore
=> s_buildSemaphore;

/// <summary>
/// Executes the specified build requests.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal class AwaitableProcess(ITestOutputHelper logger) : IDisposable
{
// cancel just before we hit timeout used on CI (XUnitWorkItemTimeout value in sdk\test\UnitTests.proj)
private static readonly TimeSpan s_timeout = Environment.GetEnvironmentVariable("HELIX_WORK_ITEM_TIMEOUT") is { } value
? TimeSpan.Parse(value).Subtract(TimeSpan.FromSeconds(10)) : TimeSpan.FromMinutes(1);
? TimeSpan.Parse(value).Subtract(TimeSpan.FromSeconds(10)) : TimeSpan.FromMinutes(10);

private readonly object _testOutputLock = new();

Expand Down
2 changes: 1 addition & 1 deletion test/dotnet-watch.Tests/Build/EvaluationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public async Task StaticAssets(bool isWeb, [CombinatorialValues(true, false, nul
},
};

var testAsset = _testAssets.CreateTestProject(project, identifier: enableStaticWebAssets.ToString());
var testAsset = _testAssets.CreateTestProject(project, identifier: $"{isWeb}_{enableStaticWebAssets}");

await VerifyEvaluation(testAsset,
isWeb && enableStaticWebAssets != false ?
Expand Down
41 changes: 24 additions & 17 deletions test/dotnet-watch.Tests/CommandLine/BinaryLoggerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

namespace Microsoft.DotNet.Watch.UnitTests;

[Collection(nameof(InProcBuildTestCollection))]
public class BinaryLoggerTests
{
[Theory]
Expand All @@ -27,29 +26,37 @@ public class BinaryLoggerTests
[InlineData("\"\"\"path{}\"", "path{}.binlog", false)] // wildcard {} not supported
public void ParseBinaryLogFilePath(string? value, string? expected, bool matchesMSBuildImpl = true)
{
Assert.Equal(expected, CommandLineOptions.ParseBinaryLogFilePath(value));

if (!matchesMSBuildImpl)
ProjectBuildManager.Test_BuildSemaphore.Wait();
try
{
return;
}
Assert.Equal(expected, CommandLineOptions.ParseBinaryLogFilePath(value));

if (!matchesMSBuildImpl)
{
return;
}

Assert.NotNull(value);
Assert.NotNull(expected);
Assert.NotNull(value);
Assert.NotNull(expected);

var dir = TestContext.Current.TestExecutionDirectory;
Directory.SetCurrentDirectory(dir);
var dir = TestContext.Current.TestExecutionDirectory;
Directory.SetCurrentDirectory(dir);

var bl = new BinaryLogger() { Parameters = value };
bl.Initialize(new EventSource());
var bl = new BinaryLogger() { Parameters = value };
bl.Initialize(new EventSource());

var actualPath = bl.GetType().GetProperty("FilePath", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)?.GetValue(bl);
if (actualPath != null)
var actualPath = bl.GetType().GetProperty("FilePath", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)?.GetValue(bl);
if (actualPath != null)
{
Assert.Equal(Path.Combine(dir, expected), actualPath);
}

bl.Shutdown();
}
finally
{
Assert.Equal(Path.Combine(dir, expected), actualPath);
ProjectBuildManager.Test_BuildSemaphore.Release();
}

bl.Shutdown();
}

private class EventSource : IEventSource
Expand Down
142 changes: 142 additions & 0 deletions test/dotnet-watch.Tests/CommandLine/ProgramTests.Arguments.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// 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 ProgramTests_Arguments(ITestOutputHelper output) : DotNetWatchTestBase(output)
{
[Theory]
[InlineData(new[] { "--no-hot-reload", "run" }, "")]
[InlineData(new[] { "--no-hot-reload", "run", "args" }, "args")]
[InlineData(new[] { "--no-hot-reload", "--", "run", "args" }, "run,args")]
[InlineData(new[] { "--no-hot-reload" }, "")]
[InlineData(new string[] { }, "")]
[InlineData(new[] { "run" }, "")]
[InlineData(new[] { "run", "args" }, "args")]
[InlineData(new[] { "--", "run", "args" }, "run,args")]
[InlineData(new[] { "--", "test", "args" }, "test,args")]
[InlineData(new[] { "--", "build", "args" }, "build,args")]
[InlineData(new[] { "abc" }, "abc")]
public async Task Arguments(string[] arguments, string expectedApplicationArgs)
{
var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp", identifier: string.Join(",", arguments))
.WithSource();

App.SuppressVerboseLogging();
App.Start(testAsset, arguments);

Assert.Equal(expectedApplicationArgs, await App.AssertOutputLineStartsWith("Arguments = "));
}

// https://github.com/dotnet/sdk/issues/49665
[PlatformSpecificFact(TestPlatforms.Any & ~TestPlatforms.OSX)]
public async Task RunArguments_NoHotReload()
{
var testAsset = TestAssets.CopyTestAsset("WatchHotReloadAppMultiTfm")
.WithSource();

App.SuppressVerboseLogging();
App.Start(testAsset, arguments:
[
"--no-hot-reload",
"run",
"-f",
"net6.0",
"--property:AssemblyVersion=1.2.3.4",
"--property",
"AssemblyTitle= | A=B'\tC | ",
"-v",
"minimal",
"--", // the following args are application args
"-v",
]);

Assert.Equal("-v", await App.AssertOutputLineStartsWith("Arguments = "));
Assert.Equal("WatchHotReloadAppMultiTfm, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null", await App.AssertOutputLineStartsWith("AssemblyName = "));
Assert.Equal("' | A=B'\tC | '", await App.AssertOutputLineStartsWith("AssemblyTitle = "));
Assert.Equal(".NETCoreApp,Version=v6.0", await App.AssertOutputLineStartsWith("TFM = "));

// expected output from build (-v minimal):
Assert.Contains(App.Process.Output, l => l.Contains("Determining projects to restore..."));

// not expected to find verbose output of dotnet watch
Assert.DoesNotContain(App.Process.Output, l => l.Contains("Working directory:"));
}

// https://github.com/dotnet/sdk/issues/49665
[PlatformSpecificFact(TestPlatforms.Any & ~TestPlatforms.OSX)]
public async Task RunArguments_HotReload()
{
var testAsset = TestAssets.CopyTestAsset("WatchHotReloadAppMultiTfm")
.WithSource();

App.SuppressVerboseLogging();
App.Start(testAsset, arguments:
[
"run",
"-f", // dotnet watch does not recognize this arg -> dotnet run arg
"net6.0",
"--property",
"AssemblyVersion=1.2.3.4",
"--property",
"AssemblyTitle= | A=B'\tC | ",
"--", // the following args are not dotnet run args
"-v", // dotnet build argument
"minimal"
]);

Assert.Equal("WatchHotReloadAppMultiTfm, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null", await App.AssertOutputLineStartsWith("AssemblyName = "));
Assert.Equal("' | A=B'\tC | '", await App.AssertOutputLineStartsWith("AssemblyTitle = "));
Assert.Equal(".NETCoreApp,Version=v6.0", await App.AssertOutputLineStartsWith("TFM = "));

// not expected to find verbose output of dotnet watch
Assert.DoesNotContain(App.Process.Output, l => l.Contains("Working directory:"));

Assert.Contains(App.Process.Output, l => l.Contains("Hot reload enabled."));
}

[Theory]
[InlineData("P1", "argP1")]
[InlineData("P and Q and \"R\"", "argPQR")]
public async Task ArgumentsFromLaunchSettings_Watch(string profileName, string expectedArgs)
{
var testAsset = TestAssets.CopyTestAsset("WatchAppWithLaunchSettings", identifier: profileName)
.WithSource();

App.Start(testAsset, arguments: new[]
{
"--verbose",
"--no-hot-reload",
"-lp",
profileName
});

Assert.Equal(expectedArgs, await App.AssertOutputLineStartsWith("Arguments: "));

Assert.Contains(App.Process.Output, l => l.Contains($"Found named launch profile '{profileName}'."));
Assert.Contains(App.Process.Output, l => l.Contains("Hot Reload disabled by command line switch."));
}

[Theory]
[InlineData("P1", "argP1")]
[InlineData("P and Q and \"R\"", "argPQR")]
public async Task ArgumentsFromLaunchSettings_HotReload(string profileName, string expectedArgs)
{
var testAsset = TestAssets.CopyTestAsset("WatchAppWithLaunchSettings", identifier: profileName)
.WithSource();

App.Start(testAsset, arguments: new[]
{
"--verbose",
"-lp",
profileName
});

Assert.Equal(expectedArgs, await App.AssertOutputLineStartsWith("Arguments: "));

Assert.Contains(App.Process.Output, l => l.Contains($"Found named launch profile '{profileName}'."));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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 ProgramTests_HostArguments(ITestOutputHelper output) : DotNetWatchTestBase(output)
{
[Theory]
[InlineData(new[] { "--no-hot-reload", "--", "run", "args" }, "Argument Specified in Props,run,args")]
[InlineData(new[] { "--", "run", "args" }, "Argument Specified in Props,run,args")]
// if arguments specified on command line the ones from launch profile are ignored
[InlineData(new[] { "-lp", "P1", "--", "run", "args" },"Argument Specified in Props,run,args")]
// arguments specified in build file override arguments in launch profile
[InlineData(new[] { "-lp", "P1" }, "Argument Specified in Props")]
public async Task Arguments_HostArguments(string[] arguments, string expectedApplicationArgs)
{
var testAsset = TestAssets.CopyTestAsset("WatchHotReloadAppCustomHost", identifier: string.Join(",", arguments))
.WithSource();

App.Start(testAsset, arguments);

AssertEx.Equal(expectedApplicationArgs, await App.AssertOutputLineStartsWith("Arguments = "));
}
}
}
Loading
Loading