diff --git a/Directory.Build.props b/Directory.Build.props
index 649270c..f3ca6d5 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -19,6 +19,7 @@
true
true
Recommended
+ All
true
diff --git a/global.json b/global.json
index 4a062a8..edf4d5d 100644
--- a/global.json
+++ b/global.json
@@ -1,7 +1,7 @@
{
"msbuild-sdks": {
"Microsoft.Build.NoTargets": "3.7.56",
- "MSTest.Sdk": "4.1.0"
+ "MSTest.Sdk": "4.4.0"
},
"test": {
"runner": "Microsoft.Testing.Platform"
diff --git a/src/Tests/AnalyzerTests.cs b/src/Tests/AnalyzerTests.cs
index ede799e..3ef0a72 100644
--- a/src/Tests/AnalyzerTests.cs
+++ b/src/Tests/AnalyzerTests.cs
@@ -9,7 +9,7 @@
namespace ReferenceTrimmer.Tests;
[TestClass]
-public sealed class AnalyzerTests
+public sealed class AnalyzerTests(TestContext testContext)
{
[TestMethod]
public async Task UsedViaMethodCall()
@@ -28,7 +28,7 @@ public async Task UnusedReportsDiagnostic()
var diagnostics = await RunAnalyzerAsync(
"class C { }",
dep);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
}
@@ -239,9 +239,9 @@ public async Task MultipleDepsOnlyUnusedReported()
"class C : Used.Foo { }",
[(used.Reference, used.Path, "ProjectReference", "../Used/Used.csproj"),
(unused.Reference, unused.Path, "ProjectReference", "../Unused/Unused.csproj")]);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- Assert.IsTrue(diagnostics[0].GetMessage(CultureInfo.InvariantCulture).Contains("Unused"));
+ Assert.Contains("Unused", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -298,7 +298,7 @@ public async Task UnusedViaCrefWhenDocModeDisabled()
class C { }",
[(dep.Reference, dep.Path, "ProjectReference", "../Dependency/Dependency.csproj")],
new CSharpParseOptions(documentationMode: DocumentationMode.None));
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
}
@@ -314,14 +314,14 @@ public async Task UsedViaTypeForwarding()
var facadeTree = CSharpSyntaxTree.ParseText(@"
using System.Runtime.CompilerServices;
[assembly: TypeForwardedTo(typeof(Dep.Foo))]
- ");
+ ", cancellationToken: testContext.CancellationToken);
var facadeComp = CSharpCompilation.Create(
"Facade",
[facadeTree],
[CorlibRef, runtime.Reference],
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
string facadePath = Path.Combine(Path.GetTempPath(), $"RT_Test_Facade_{Guid.NewGuid():N}.dll");
- var facadeResult = facadeComp.Emit(facadePath);
+ var facadeResult = facadeComp.Emit(facadePath, cancellationToken: testContext.CancellationToken);
Assert.IsTrue(facadeResult.Success, $"Facade compilation failed:\n{string.Join("\n", facadeResult.Diagnostics)}");
var facadeRef = MetadataReference.CreateFromFile(facadePath);
@@ -445,9 +445,9 @@ public async Task UnusedPackageReportsRT0003()
var diagnostics = await RunAnalyzerAsync(
"class C { }",
[(dep.Reference, dep.Path, "PackageReference", "Dep.Package")]);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0003", diagnostics[0].Id);
- Assert.IsTrue(diagnostics[0].GetMessage(CultureInfo.InvariantCulture).Contains("Dep.Package"));
+ Assert.Contains("Dep.Package", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -484,7 +484,7 @@ public async Task UnusedBareReferenceReportsRT0001()
var diagnostics = await RunAnalyzerAsync(
"class C { }",
[(dep.Reference, dep.Path, "Reference", dep.Path)]);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0001", diagnostics[0].Id);
}
@@ -544,7 +544,7 @@ public async Task UnusedDelegateNoExternalTypesReportsDiagnostic()
var diagnostics = await RunAnalyzerAsync(
"public class Local {} public delegate Local Produce(Local x);",
dep);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
}
@@ -693,9 +693,9 @@ public async Task UnrelatedReferenceNotMarkedByOverride()
[(baseAsm.Reference, baseAsm.Path, "ProjectReference", "../Base/Base.csproj"),
(derivedAsm.Reference, derivedAsm.Path, "ProjectReference", "../Derived/Derived.csproj"),
(unrelated.Reference, unrelated.Path, "ProjectReference", "../Unrelated/Unrelated.csproj")]);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- StringAssert.Contains(diagnostics[0].GetMessage(CultureInfo.InvariantCulture), "Unrelated");
+ Assert.Contains("Unrelated", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -850,9 +850,9 @@ public async Task UnrelatedReferenceNotMarkedByInheritance()
[(aAsm.Reference, aAsm.Path, "ProjectReference", "../A/A.csproj"),
(bAsm.Reference, bAsm.Path, "ProjectReference", "../B/B.csproj"),
(unrelated.Reference, unrelated.Path, "ProjectReference", "../Unrelated/Unrelated.csproj")]);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- StringAssert.Contains(diagnostics[0].GetMessage(CultureInfo.InvariantCulture), "Unrelated");
+ Assert.Contains("Unrelated", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -977,9 +977,9 @@ public Consumer() : base(""1"") { }
[(provider.Reference, provider.Path, "ProjectReference", "../Provider/Provider.csproj"),
(providerDep.Reference, providerDep.Path, "ProjectReference", "../ProviderDependency/ProviderDependency.csproj"),
(unrelated.Reference, unrelated.Path, "ProjectReference", "../Unrelated/Unrelated.csproj")]);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- StringAssert.Contains(diagnostics[0].GetMessage(CultureInfo.InvariantCulture), "Unrelated");
+ Assert.Contains("Unrelated", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -1245,9 +1245,9 @@ public void Foo(int i) { }
@"public class Consumer { void M(Provider.P p) { p.Foo(""x""); } }",
[(provider.Reference, provider.Path, "ProjectReference", "../Provider/Provider.csproj"),
(dep.Reference, dep.Path, "ProjectReference", "../Dep/Dep.csproj")]);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- StringAssert.Contains(diagnostics[0].GetMessage(CultureInfo.InvariantCulture), "Dep");
+ Assert.Contains("Dep", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -1272,9 +1272,9 @@ public void Bar(Dep.T t) { }
@"public class Consumer { void M(Provider.P p) { p.Foo(""x""); } }",
[(provider.Reference, provider.Path, "ProjectReference", "../Provider/Provider.csproj"),
(dep.Reference, dep.Path, "ProjectReference", "../Dep/Dep.csproj")]);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- StringAssert.Contains(diagnostics[0].GetMessage(CultureInfo.InvariantCulture), "Dep");
+ Assert.Contains("Dep", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -1310,7 +1310,7 @@ public async Task UnusedCompilationReferenceStillReportsDiagnostic(bool useSymbo
[(dependency.Reference, dependency.Path, "ProjectReference", "../Dependency/Dependency.csproj", dependency.Identity)],
useSymbolAnalysis: useSymbolAnalysis);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
}
@@ -1334,7 +1334,7 @@ public async Task CompilationReferenceRequiresFullAssemblyIdentityMatch(bool use
[(dependency.Reference, dependency.Path, "ProjectReference", "../Dependency/Dependency.csproj", differentVersionIdentity)],
useSymbolAnalysis: useSymbolAnalysis);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
}
@@ -1416,9 +1416,9 @@ public async Task DuplicateCompilationReferenceIdentityUnionsTransitiveDependenc
useSymbolAnalysis: useSymbolAnalysis,
disableTransitiveProjectReferences: true);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- StringAssert.Contains(diagnostics[0].GetMessage(CultureInfo.InvariantCulture), "../Unrelated/Unrelated.csproj");
+ Assert.Contains("../Unrelated/Unrelated.csproj", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -1471,7 +1471,7 @@ public async Task UnparseableProjectAssemblyIdentityDoesNotSuppressPeBackedDiagn
[(dependency.Reference, dependency.Path, "ProjectReference", "../Dependency/Dependency.csproj", "LogicalAssembly, Version=invalid")],
useSymbolAnalysis: useSymbolAnalysis);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
}
@@ -1501,9 +1501,9 @@ public async Task CompilationReferenceDoesNotSuppressPeBackedLegacyDiagnostic(
(unused.Reference, unused.Path, "ProjectReference", "../Unused/Unused.csproj", unusedIdentity)],
useSymbolAnalysis: useSymbolAnalysis);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- StringAssert.Contains(diagnostics[0].GetMessage(CultureInfo.InvariantCulture), "../Unused/Unused.csproj");
+ Assert.Contains("../Unused/Unused.csproj", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
[TestMethod]
@@ -1533,9 +1533,9 @@ public async Task CompilationReferenceTransitiveChainRetainsDependenciesButNotUn
useSymbolAnalysis: useSymbolAnalysis,
disableTransitiveProjectReferences: true);
- Assert.AreEqual(1, diagnostics.Length);
+ Assert.HasCount(1, diagnostics);
Assert.AreEqual("RT0002", diagnostics[0].Id);
- StringAssert.Contains(diagnostics[0].GetMessage(CultureInfo.InvariantCulture), "../C/C.csproj");
+ Assert.Contains("../C/C.csproj", diagnostics[0].GetMessage(CultureInfo.InvariantCulture));
}
// ──────────────────────────────────────────────────────────────────────
diff --git a/src/Tests/CollectDeclaredReferencesTaskTests.cs b/src/Tests/CollectDeclaredReferencesTaskTests.cs
index 73618df..157ac4d 100644
--- a/src/Tests/CollectDeclaredReferencesTaskTests.cs
+++ b/src/Tests/CollectDeclaredReferencesTaskTests.cs
@@ -30,7 +30,7 @@ public void ExecuteWithAllNullCollectionInputsDoesNotThrow()
bool result = task.Execute();
Assert.IsTrue(result, "Task should succeed when all collection inputs are null. Errors: " + string.Join("; ", engine.Errors));
- Assert.AreEqual(0, engine.Errors.Count, "No errors should be logged. Errors: " + string.Join("; ", engine.Errors));
+ Assert.IsEmpty(engine.Errors, "No errors should be logged. Errors: " + string.Join("; ", engine.Errors));
}
finally
{
@@ -65,7 +65,7 @@ public void ExecuteWithNullResolvedReferencesAndUnresolvableReferenceDoesNotThro
bool result = task.Execute();
Assert.IsTrue(result, "Task should succeed even when ResolvedReferences is null. Errors: " + string.Join("; ", engine.Errors));
- Assert.AreEqual(0, engine.Errors.Count, "No errors should be logged. Errors: " + string.Join("; ", engine.Errors));
+ Assert.IsEmpty(engine.Errors, "No errors should be logged. Errors: " + string.Join("; ", engine.Errors));
}
finally
{
@@ -99,7 +99,7 @@ public void ExecuteWithPackageReferencesAndNullProjectAssetsFileDoesNotThrow()
bool result = task.Execute();
Assert.IsTrue(result, "Task should succeed when ProjectAssetsFile is null. Errors: " + string.Join("; ", engine.Errors));
- Assert.AreEqual(0, engine.Errors.Count, "No errors should be logged. Errors: " + string.Join("; ", engine.Errors));
+ Assert.IsEmpty(engine.Errors, "No errors should be logged. Errors: " + string.Join("; ", engine.Errors));
}
finally
{
@@ -171,7 +171,7 @@ public void ProjectAssemblyIdentityParticipatesInIncrementalHash()
.SingleOrDefault(include => include?.Contains("_ReferenceTrimmerProjectReferences", StringComparison.Ordinal) == true);
Assert.IsNotNull(projectHashInput);
- StringAssert.Contains(projectHashInput, "%(FusionName)");
+ Assert.Contains("%(FusionName)", projectHashInput);
}
private sealed class MockBuildEngine : IBuildEngine
diff --git a/src/Tests/E2ETests.cs b/src/Tests/E2ETests.cs
index 2f909a6..5d88775 100644
--- a/src/Tests/E2ETests.cs
+++ b/src/Tests/E2ETests.cs
@@ -8,7 +8,7 @@
namespace ReferenceTrimmer.Tests;
[TestClass]
-public sealed class E2ETests
+public sealed class E2ETests(TestContext testContext)
{
private readonly record struct Warning(string Message, string Project, IEnumerable? AltMessages = null);
@@ -19,15 +19,15 @@ public sealed class E2ETests
@".+: (warning|error) (?.+) \[(?.+)\]",
RegexOptions.Compiled | RegexOptions.ExplicitCapture);
- public TestContext? TestContext { get; set; }
-
[ClassInitialize]
public static void ClassInitialize(TestContext _)
{
// Delete the package cache to avoid reusing old content
if (Directory.Exists("Packages"))
{
+#pragma warning disable MSTEST0077 // Avoid hardcoded or shared filesystem paths in a parallelized test
Directory.Delete("Packages", recursive: true);
+#pragma warning restore MSTEST0077 // Avoid hardcoded or shared filesystem paths in a parallelized test
}
}
@@ -821,14 +821,14 @@ private async Task RunMSBuildAsync(
bool useSymbolAnalysis = false,
IReadOnlyDictionary? globalProperties = null)
{
- var testDataSourcePath = Path.GetFullPath(Path.Combine("TestData", TestContext?.TestName ?? string.Empty));
+ var testDataSourcePath = Path.GetFullPath(Path.Combine("TestData", testContext.TestName ?? string.Empty));
string logDirBase = Path.Combine(testDataSourcePath, "Logs");
string binlogFilePath = Path.Combine(logDirBase, Path.GetFileName(projectFile) + ".binlog");
string warningsFilePath = Path.Combine(logDirBase, Path.GetFileName(projectFile) + ".warnings.log");
string errorsFilePath = Path.Combine(logDirBase, Path.GetFileName(projectFile) + ".errors.log");
- TestContext?.WriteLine($"Log directory: {logDirBase}");
+ testContext.WriteLine($"Log directory: {logDirBase}");
string unusedLibraryLogPath = Path.Combine(testDataSourcePath, ForwardingLogger.HelpKeyword + ".json.log");
if (File.Exists(unusedLibraryLogPath))
@@ -868,19 +868,19 @@ private async Task RunMSBuildAsync(
}.WithVsDevEnvironment());
Assert.IsNotNull(process);
- string stdOut = await process.StandardOutput.ReadToEndAsync();
- string stdErr = await process.StandardError.ReadToEndAsync();
+ string stdOut = await process.StandardOutput.ReadToEndAsync(testContext!.CancellationToken);
+ string stdErr = await process.StandardError.ReadToEndAsync(testContext.CancellationToken);
- await process.WaitForExitAsync();
+ await process.WaitForExitAsync(testContext.CancellationToken);
Assert.AreEqual(0, process.ExitCode, $"Build of {projectFile} was not successful.{Environment.NewLine}StandardError: {stdErr}{Environment.NewLine}StandardOutput: {stdOut}");
Assert.AreEqual(File.Exists(unusedLibraryLogPath), expectUnusedMsvcLibrariesLog);
- string errors = await File.ReadAllTextAsync(errorsFilePath);
+ string errors = await File.ReadAllTextAsync(errorsFilePath, testContext.CancellationToken);
Assert.AreEqual(0, errors.Length, $"Build of {projectFile} was not successful.{Environment.NewLine}Error log: {errors}");
List actualWarnings = new();
- foreach (string line in await File.ReadAllLinesAsync(warningsFilePath))
+ foreach (string line in await File.ReadAllLinesAsync(warningsFilePath, testContext.CancellationToken))
{
Match match = WarningErrorRegex.Match(line);
if (match.Success)
diff --git a/src/Tests/MsvcLoggerTests.cs b/src/Tests/MsvcLoggerTests.cs
index dd053fe..40d3234 100644
--- a/src/Tests/MsvcLoggerTests.cs
+++ b/src/Tests/MsvcLoggerTests.cs
@@ -6,7 +6,7 @@
namespace ReferenceTrimmer.Tests;
[TestClass]
-public sealed class MsvcLoggerTests
+public sealed class MsvcLoggerTests(TestContext testContext)
{
private sealed class MockEventSource : IEventSource
{
@@ -372,7 +372,7 @@ public async Task CentralLogger_ProcessesUnusedLibEventsFromPrimaryNode()
centralLogger.Shutdown();
Assert.IsTrue(File.Exists(jsonPath));
- string json = await File.ReadAllTextAsync(jsonPath);
+ string json = await File.ReadAllTextAsync(jsonPath, testContext.CancellationToken);
Assert.Contains("user32.lib", json);
}
@@ -394,7 +394,7 @@ public async Task CentralLogger_JsonOnUnusedLibEvents()
centralLogger.Shutdown();
Assert.IsTrue(File.Exists(jsonPath));
Assert.AreEqual($"[{Environment.NewLine}{{ \"aProp\": \"aValue\" }},{Environment.NewLine}{{ \"aProp2\": \"aValue2\" }}{Environment.NewLine}]{Environment.NewLine}",
- await File.ReadAllTextAsync(jsonPath));
+ await File.ReadAllTextAsync(jsonPath, testContext.CancellationToken));
}
private static void SendLinkTaskStarted(