diff --git a/Directory.Packages.props b/Directory.Packages.props
index a17e2fe..bc94040 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -11,7 +11,7 @@
-
+
@@ -43,12 +43,12 @@
-
-
+
+
-
-
+
+
diff --git a/global.json b/global.json
index bd1ddef..e3f13c3 100644
--- a/global.json
+++ b/global.json
@@ -4,7 +4,7 @@
"rollForward": "latestFeature"
},
"msbuild-sdks": {
- "MSTest.Sdk": "4.1.0"
+ "MSTest.Sdk": "4.4.0"
},
"test": {
"runner": "Microsoft.Testing.Platform"
diff --git a/src/AzurePipelines.Tests/SelectorPublicationTests.cs b/src/AzurePipelines.Tests/SelectorPublicationTests.cs
index cfafb50..fa9bace 100644
--- a/src/AzurePipelines.Tests/SelectorPublicationTests.cs
+++ b/src/AzurePipelines.Tests/SelectorPublicationTests.cs
@@ -112,15 +112,14 @@ async Task TryPublishAsync(
Assert.IsTrue(await firstPublication);
Assert.IsTrue(await secondPublication);
- CollectionAssert.AreEquivalent(
- new[] { existingSelector, firstSelector, secondSelector },
- finalWinner!.Selectors.ToArray());
+ Assert.AreSequenceEqual(
+ new[] { existingSelector, firstSelector, secondSelector }, finalWinner!.Selectors.ToArray(), SequenceOrder.InAnyOrder);
string initialWriteKey = PipelineCachingCacheClient.ComputeSelectorsWriteKey(Universe, weakFingerprint, initial.Id);
- CollectionAssert.AreEqual(new[] { initialWriteKey }, conflictQueries.ToArray());
+ Assert.AreSequenceEqual(new[] { initialWriteKey }, conflictQueries.ToArray());
Assert.AreEqual(
PipelineCachingCacheClient.ComputeSelectorsWriteKey(Universe, weakFingerprint, firstWinner!.Id),
finalWriteKey);
- StringAssert.StartsWith(latestKey, "selector6|", StringComparison.Ordinal);
+ Assert.StartsWith("selector6|", latestKey, StringComparison.Ordinal);
}
[TestMethod]
@@ -136,9 +135,9 @@ public void OutputKeyIncludesSelectorOutput()
string secondKey = PipelineCachingCacheClient.ComputeOutputKey("universe", second, forWrite: false, writeId: 0);
Assert.AreNotEqual(firstKey, secondKey);
- StringAssert.StartsWith(firstKey, "outputs6|", StringComparison.Ordinal);
- StringAssert.Contains(firstKey, "|01|", StringComparison.Ordinal);
- StringAssert.Contains(secondKey, "|02|", StringComparison.Ordinal);
+ Assert.StartsWith("outputs6|", firstKey, StringComparison.Ordinal);
+ Assert.Contains("|01|", firstKey, StringComparison.Ordinal);
+ Assert.Contains("|02|", secondKey, StringComparison.Ordinal);
}
private static Selector CreateSelector(byte output)
diff --git a/src/Common.Tests/CasCacheClientTests.cs b/src/Common.Tests/CasCacheClientTests.cs
index cf2cd0d..80f25d3 100644
--- a/src/Common.Tests/CasCacheClientTests.cs
+++ b/src/Common.Tests/CasCacheClientTests.cs
@@ -57,7 +57,7 @@ public async Task AddNodeDoesNotUploadContentWhenRemoteCacheIsReadOnly()
Assert.AreEqual(0, remoteSession.PutFileCallCount, "Content must not be uploaded when the remote cache is read-only.");
// The node still needs to be written to the local cache.
- Assert.IsTrue(localSession.PutStreamCallCount > 0, "The node metadata must still be written to the local cache.");
+ Assert.IsGreaterThan(0, localSession.PutStreamCallCount, "The node metadata must still be written to the local cache.");
Assert.AreEqual(1, localSession.AddOrGetContentHashListCallCount, "The content hash list must still be added to the local cache.");
}
@@ -71,9 +71,9 @@ public async Task AddNodeUploadsContentWhenRemoteCacheIsWritable()
await cacheClient.AddNodeInternalAsync(CreateNodeContext(), pathSet: null, CreateNodeBuildResult(), CancellationToken.None);
}
- Assert.IsTrue(remoteSession.PinCallCount > 0, "The remote session should be pinned to determine what to upload.");
- Assert.IsTrue(remoteSession.PutStreamCallCount > 0, "Content should be uploaded when the remote cache is writable.");
- Assert.IsTrue(localSession.PutStreamCallCount > 0, "The node metadata should be written to the local cache.");
+ Assert.IsGreaterThan(0, remoteSession.PinCallCount, "The remote session should be pinned to determine what to upload.");
+ Assert.IsGreaterThan(0, remoteSession.PutStreamCallCount, "Content should be uploaded when the remote cache is writable.");
+ Assert.IsGreaterThan(0, localSession.PutStreamCallCount, "The node metadata should be written to the local cache.");
}
private static (CasCacheClient CacheClient, RecordingCacheSession LocalSession, RecordingCacheSession RemoteSession) CreateCacheClient(bool remoteCacheIsReadOnly)
diff --git a/src/Common.Tests/FingerprintFactoryTests.cs b/src/Common.Tests/FingerprintFactoryTests.cs
index 7c008c6..1d45bcb 100644
--- a/src/Common.Tests/FingerprintFactoryTests.cs
+++ b/src/Common.Tests/FingerprintFactoryTests.cs
@@ -46,7 +46,7 @@ public void WeakFingerprintFlagSegregation()
FingerprintEntry onFlagEntry = entriesOn.Single(e => e.Description.Contains(nameof(PluginSettings.EnableProbeAndEnumerationFingerprinting), StringComparison.Ordinal));
Assert.AreNotEqual(offFlagEntry.Description, onFlagEntry.Description, "Flag descriptions should differ.");
- CollectionAssert.AreNotEqual(offFlagEntry.Hash, onFlagEntry.Hash, "Flag entry hashes should differ.");
+ Assert.AreNotSequenceEqual(offFlagEntry.Hash, onFlagEntry.Hash, "Flag entry hashes should differ.");
// And no other entry should differ between the two factories — the flag is the only setting toggled.
List commonOff = entriesOff
@@ -55,7 +55,7 @@ public void WeakFingerprintFlagSegregation()
List commonOn = entriesOn
.Where(e => !e.Description.Contains(nameof(PluginSettings.EnableProbeAndEnumerationFingerprinting), StringComparison.Ordinal))
.Select(e => e.Description).ToList();
- CollectionAssert.AreEqual(commonOff, commonOn);
+ Assert.AreSequenceEqual(commonOff, commonOn);
}
///
@@ -74,8 +74,8 @@ public void SentinelsAreDeterministicForHashType(HashType hashType)
FingerprintFactory a = CreateFactory(hasher);
FingerprintFactory b = CreateFactory(hasher);
- CollectionAssert.AreEqual(a.AbsentFileSentinel, b.AbsentFileSentinel, $"AbsentFileSentinel not deterministic for {hashType}.");
- CollectionAssert.AreEqual(a.ZeroHash, b.ZeroHash, $"ZeroHash not deterministic for {hashType}.");
+ Assert.AreSequenceEqual(a.AbsentFileSentinel, b.AbsentFileSentinel, $"AbsentFileSentinel not deterministic for {hashType}.");
+ Assert.AreSequenceEqual(a.ZeroHash, b.ZeroHash, $"ZeroHash not deterministic for {hashType}.");
}
///
@@ -91,7 +91,7 @@ public void SentinelsAreDistinct(HashType hashType)
using IContentHasher hasher = HashInfoLookup.Find(hashType).CreateContentHasher();
FingerprintFactory factory = CreateFactory(hasher);
- CollectionAssert.AreNotEqual(factory.AbsentFileSentinel, factory.ZeroHash, "AbsentFileSentinel and ZeroHash must not collide.");
+ Assert.AreNotSequenceEqual(factory.AbsentFileSentinel, factory.ZeroHash, "AbsentFileSentinel and ZeroHash must not collide.");
}
///
@@ -108,8 +108,8 @@ public void SentinelsMatchHasherByteLength(HashType hashType)
int expectedLength = hasher.Info.ByteLength;
FingerprintFactory factory = CreateFactory(hasher);
- Assert.AreEqual(expectedLength, factory.AbsentFileSentinel.Length, $"AbsentFileSentinel size mismatch for {hashType}.");
- Assert.AreEqual(expectedLength, factory.ZeroHash.Length, $"ZeroHash size mismatch for {hashType}.");
+ Assert.HasCount(expectedLength, factory.AbsentFileSentinel, $"AbsentFileSentinel size mismatch for {hashType}.");
+ Assert.HasCount(expectedLength, factory.ZeroHash, $"ZeroHash size mismatch for {hashType}.");
}
///
@@ -132,8 +132,7 @@ public async Task StrongFingerprintDistinguishesAbsentFromExistent()
Assert.IsNotNull(fpAbsent);
Assert.IsNotNull(fpExisting);
- CollectionAssert.AreNotEqual(fpAbsent.Hash, fpExisting.Hash,
- "AbsentPathProbe and ExistingProbe of the same path must produce different strong fingerprints.");
+ Assert.AreNotSequenceEqual(fpAbsent.Hash, fpExisting.Hash, "AbsentPathProbe and ExistingProbe of the same path must produce different strong fingerprints.");
}
///
@@ -169,7 +168,7 @@ public async Task StrongFingerprintDistinguishesProbeFromContentRead()
Assert.IsNotNull(fpProbe);
Assert.IsNotNull(fpRead);
- CollectionAssert.AreNotEqual(fpProbe.Hash, fpRead.Hash);
+ Assert.AreNotSequenceEqual(fpProbe.Hash, fpRead.Hash);
}
///
@@ -198,8 +197,7 @@ public async Task StrongFingerprintExistingProbeIgnoresContentChange()
Assert.IsNotNull(fpA);
Assert.IsNotNull(fpB);
- CollectionAssert.AreEqual(fpA.Hash, fpB.Hash,
- "ExistingProbe must NOT incorporate file content into the strong fingerprint.");
+ Assert.AreSequenceEqual(fpA.Hash, fpB.Hash, "ExistingProbe must NOT incorporate file content into the strong fingerprint.");
}
///
@@ -235,8 +233,7 @@ public async Task StrongFingerprintDirectoryMemberHashDetectsAddition()
Assert.IsNotNull(fpBefore);
Assert.IsNotNull(fpAfter);
- CollectionAssert.AreNotEqual(fpBefore.Hash, fpAfter.Hash,
- "PathSets that differ only in DirectoryEnumeration.Members must produce different strong fingerprints.");
+ Assert.AreNotSequenceEqual(fpBefore.Hash, fpAfter.Hash, "PathSets that differ only in DirectoryEnumeration.Members must produce different strong fingerprints.");
}
///
@@ -269,7 +266,7 @@ public async Task StrongFingerprintDirectoryMemberHashDetectsRemoval()
Assert.IsNotNull(fpBefore);
Assert.IsNotNull(fpAfter);
- CollectionAssert.AreNotEqual(fpBefore.Hash, fpAfter.Hash);
+ Assert.AreNotSequenceEqual(fpBefore.Hash, fpAfter.Hash);
}
///
@@ -305,8 +302,7 @@ public async Task StrongFingerprintDirectoryMemberHashIgnoresMemberContentChange
Assert.IsNotNull(fpBefore);
Assert.IsNotNull(fpAfter);
- CollectionAssert.AreEqual(fpBefore.Hash, fpAfter.Hash,
- "DirectoryEnumeration must NOT depend on member file contents — only on the member-name list.");
+ Assert.AreSequenceEqual(fpBefore.Hash, fpAfter.Hash, "DirectoryEnumeration must NOT depend on member file contents — only on the member-name list.");
}
///
@@ -338,7 +334,7 @@ public async Task StrongFingerprintDirectoryEnumerationMissingDirIsAbsent()
// The two should still differ because the Type tag differs, but the underlying member-hash payload
// for the missing-directory case is AbsentFileSentinel (same payload bytes as the AbsentPathProbe).
// Different Type entries → different overall fingerprint.
- CollectionAssert.AreNotEqual(fpMissingDir.Hash, fpAbsent.Hash);
+ Assert.AreNotSequenceEqual(fpMissingDir.Hash, fpAbsent.Hash);
}
///
@@ -370,8 +366,7 @@ public async Task StrongFingerprintIsDeterministic()
Assert.IsNotNull(fp1);
Assert.IsNotNull(fp2);
- CollectionAssert.AreEqual(fp1.Hash, fp2.Hash,
- "Strong fingerprint must be deterministic across factory instances.");
+ Assert.AreSequenceEqual(fp1.Hash, fp2.Hash, "Strong fingerprint must be deterministic across factory instances.");
}
// =========================================================================================
@@ -457,8 +452,7 @@ public async Task MatchesCurrentStateUnchangedHitsAndFingerprintMatches()
Fingerprint? lookupFp = await factoryAtLookup.GetStrongFingerprintAsync(cachedPathSet);
Assert.IsNotNull(populateFp);
Assert.IsNotNull(lookupFp);
- CollectionAssert.AreEqual(populateFp.Hash, lookupFp.Hash,
- "Cached strong FP must match recomputed FP when state is unchanged.");
+ Assert.AreSequenceEqual(populateFp.Hash, lookupFp.Hash, "Cached strong FP must match recomputed FP when state is unchanged.");
}
///
@@ -496,8 +490,7 @@ public async Task MatchesCurrentStateCleanDirtyCleanCycleRecoversHit()
Assert.IsNotNull(build1Fp);
Assert.IsNotNull(build3Fp);
- CollectionAssert.AreEqual(build1Fp.Hash, build3Fp.Hash,
- "Build 3 (clean again) must produce the Build 1 fingerprint so the cache hit is recovered.");
+ Assert.AreSequenceEqual(build1Fp.Hash, build3Fp.Hash, "Build 3 (clean again) must produce the Build 1 fingerprint so the cache hit is recovered.");
}
///
@@ -771,7 +764,7 @@ public void EnumerateAndSubtractUsesWin32StarDotStarSemantics()
FingerprintFactory.EnumerateAndSubtract(tempDir.Path, "*.*", writtenMembersToSubtract: null);
Assert.IsNotNull(result);
- CollectionAssert.AreEquivalent(new[] { "README", "a.cs" }, result.ToArray());
+ Assert.AreSequenceEqual(new[] { "README", "a.cs" }, result.ToArray(), SequenceOrder.InAnyOrder);
}
[TestMethod]
@@ -789,7 +782,7 @@ public void EnumerateAndSubtractExcludesIgnoredMembers()
ignoredInputPatterns: new[] { Glob.Parse(ignoredPath) });
Assert.IsNotNull(result);
- CollectionAssert.AreEqual(new[] { "stable.input" }, result.ToArray());
+ Assert.AreSequenceEqual(new[] { "stable.input" }, result.ToArray());
}
///
@@ -827,7 +820,7 @@ public void EnumerateAndSubtractIsCaseInsensitive()
IReadOnlyList? result = FingerprintFactory.EnumerateAndSubtract(tempDir.Path, enumerationPattern: "*.cs", writtenMembersToSubtract: null);
Assert.IsNotNull(result);
- CollectionAssert.AreEquivalent(new[] { "Foo.CS", "bar.cs" }, result.ToArray());
+ Assert.AreSequenceEqual(new[] { "Foo.CS", "bar.cs" }, result.ToArray(), SequenceOrder.InAnyOrder);
}
// =========================================================================================
@@ -851,7 +844,7 @@ public void FoldPathSetEntriesFlagOffIgnoresObservations()
},
enableProbeAndEnumeration: false);
- Assert.AreEqual(1, entries.Count);
+ Assert.HasCount(1, entries);
Assert.AreEqual("a.cs", entries[0].Path);
Assert.AreEqual(ObservationType.FileContentRead, entries[0].Type);
}
@@ -870,7 +863,7 @@ public void FoldPathSetEntriesPrecedenceKeepsContentReadOverProbe()
},
enableProbeAndEnumeration: true);
- Assert.AreEqual(1, entries.Count);
+ Assert.HasCount(1, entries);
Assert.AreEqual(ObservationType.FileContentRead, entries[0].Type);
}
@@ -890,7 +883,7 @@ public void FoldPathSetEntriesPrecedenceReplacesProbeWithRead()
},
enableProbeAndEnumeration: true);
- Assert.AreEqual(1, entries.Count);
+ Assert.HasCount(1, entries);
Assert.AreEqual(ObservationType.FileContentRead, entries[0].Type);
}
@@ -910,10 +903,9 @@ public void FoldPathSetEntriesDirectoryEnumerationKeepsDistinctPatterns()
},
enableProbeAndEnumeration: true);
- Assert.AreEqual(2, entries.Count, "Same-path DirectoryEnumeration with different patterns must produce TWO entries.");
- CollectionAssert.AreEqual(
- new[] { "*.cs", "*.dll" },
- entries.Select(e => e.EnumerationPattern).ToArray());
+ Assert.HasCount(2, entries, "Same-path DirectoryEnumeration with different patterns must produce TWO entries.");
+ Assert.AreSequenceEqual(
+ new[] { "*.cs", "*.dll" }, entries.Select(e => e.EnumerationPattern).ToArray());
Assert.IsTrue(entries.All(e => e.Type == ObservationType.DirectoryEnumeration));
Assert.IsTrue(entries.All(e => e.Path == "dir"));
}
@@ -934,7 +926,7 @@ public void FoldPathSetEntriesDirectoryEnumerationFoldsDuplicatePatterns()
},
enableProbeAndEnumeration: true);
- Assert.AreEqual(1, entries.Count);
+ Assert.HasCount(1, entries);
Assert.AreEqual("*.cs", entries[0].EnumerationPattern);
}
@@ -953,7 +945,7 @@ public void FoldPathSetEntriesContentReadOutranksEnumeration()
},
enableProbeAndEnumeration: true);
- Assert.AreEqual(1, entries.Count);
+ Assert.HasCount(1, entries);
Assert.AreEqual(ObservationType.FileContentRead, entries[0].Type);
Assert.IsNull(entries[0].EnumerationPattern);
}
@@ -974,7 +966,7 @@ public void FoldPathSetEntriesContentReadReplacesMultiPatternEnumeration()
},
enableProbeAndEnumeration: true);
- Assert.AreEqual(1, entries.Count);
+ Assert.HasCount(1, entries);
Assert.AreEqual(ObservationType.FileContentRead, entries[0].Type);
}
@@ -995,7 +987,7 @@ public void FoldPathSetEntriesProducesCanonicalSort()
},
enableProbeAndEnumeration: true);
- Assert.AreEqual(4, entries.Count);
+ Assert.HasCount(4, entries);
Assert.AreEqual("alpha", entries[0].Path);
Assert.AreEqual(ObservationType.ExistingProbe, entries[0].Type);
Assert.AreEqual("mike", entries[1].Path);
@@ -1031,10 +1023,10 @@ public void FilterObservationsFcrIncludedWhenHasherCanHash()
TestNormalizer,
EmptyIgnoredPatterns);
- Assert.AreEqual(1, included.Count);
+ Assert.HasCount(1, included);
Assert.AreEqual(@"{RepoRoot}src\foo.cs", included[0].Path);
Assert.AreEqual(ObservationType.FileContentRead, included[0].Type);
- Assert.AreEqual(0, excluded.Count);
+ Assert.IsEmpty(excluded);
}
///
@@ -1051,8 +1043,8 @@ public void FilterObservationsFcrExcludedWhenHasherCannotHash()
TestNormalizer,
EmptyIgnoredPatterns);
- Assert.AreEqual(0, included.Count);
- Assert.AreEqual(1, excluded.Count);
+ Assert.IsEmpty(included);
+ Assert.HasCount(1, excluded);
Assert.AreEqual(@"{RepoRoot}src\foo.cs", excluded[0]);
}
@@ -1069,10 +1061,10 @@ public void FilterObservationsKeepsInScopeProbe()
TestNormalizer,
EmptyIgnoredPatterns);
- Assert.AreEqual(1, included.Count);
+ Assert.HasCount(1, included);
Assert.AreEqual(@"{RepoRoot}src\foo.cs", included[0].Path);
Assert.AreEqual(ObservationType.ExistingProbe, included[0].Type);
- Assert.AreEqual(0, excluded.Count);
+ Assert.IsEmpty(excluded);
}
///
@@ -1088,8 +1080,8 @@ public void FilterObservationsDropsOutOfScopeProbe()
TestNormalizer,
EmptyIgnoredPatterns);
- Assert.AreEqual(0, included.Count);
- Assert.AreEqual(0, excluded.Count);
+ Assert.IsEmpty(included);
+ Assert.IsEmpty(excluded);
}
///
@@ -1118,8 +1110,8 @@ public void FilterObservationsDropsPredictedInputs()
TestNormalizer,
EmptyIgnoredPatterns);
- Assert.AreEqual(0, included.Count);
- Assert.AreEqual(0, excluded.Count);
+ Assert.IsEmpty(included);
+ Assert.IsEmpty(excluded);
}
///
@@ -1143,11 +1135,11 @@ public void FilterObservationsPreservesDirectoryEnumerationFields()
TestNormalizer,
EmptyIgnoredPatterns);
- Assert.AreEqual(1, included.Count);
+ Assert.HasCount(1, included);
Assert.AreEqual(ObservationType.DirectoryEnumeration, included[0].Type);
Assert.AreEqual("*.cs", included[0].EnumerationPattern);
- CollectionAssert.AreEqual(new[] { "a.cs", "b.cs" }, included[0].Members?.ToArray());
- CollectionAssert.AreEqual(new[] { "Foo.dll" }, included[0].WrittenMembers?.ToArray());
+ Assert.AreSequenceEqual(new[] { "a.cs", "b.cs" }, included[0].Members?.ToArray());
+ Assert.AreSequenceEqual(new[] { "Foo.dll" }, included[0].WrittenMembers?.ToArray());
}
///
@@ -1163,8 +1155,8 @@ public void FilterObservationsEmptyInput()
TestNormalizer,
EmptyIgnoredPatterns);
- Assert.AreEqual(0, included.Count);
- Assert.AreEqual(0, excluded.Count);
+ Assert.IsEmpty(included);
+ Assert.IsEmpty(excluded);
}
///
@@ -1193,9 +1185,9 @@ public void FilterObservationsDropsIgnoredInputPatterns()
TestNormalizer,
ignored);
- Assert.AreEqual(1, included.Count);
+ Assert.HasCount(1, included);
Assert.AreEqual(@"{RepoRoot}src\foo.cs", included[0].Path);
- Assert.AreEqual(0, excluded.Count, "Ignored paths must not appear in the excluded debug list either.");
+ Assert.IsEmpty(excluded, "Ignored paths must not appear in the excluded debug list either.");
}
private static FingerprintFactory CreateFactory(IContentHasher hasher, IInputHasher? inputHasher = null, PathNormalizer? pathNormalizer = null)
diff --git a/src/Common.Tests/Hashing/DirectoryFileHasherTests.cs b/src/Common.Tests/Hashing/DirectoryFileHasherTests.cs
index 341c143..f5d823e 100644
--- a/src/Common.Tests/Hashing/DirectoryFileHasherTests.cs
+++ b/src/Common.Tests/Hashing/DirectoryFileHasherTests.cs
@@ -47,14 +47,14 @@ public async Task ComputeHash(string relativePath, bool expectedToHaveHash)
#if NETFRAMEWORK
File.WriteAllText(absolutePath, fileContent);
#else
- await File.WriteAllTextAsync(absolutePath, fileContent);
+ await File.WriteAllTextAsync(absolutePath, fileContent, TestContext.CancellationToken);
#endif
byte[]? hash = await hasher.GetHashAsync(absolutePath);
if (expectedToHaveHash)
{
byte[] expectedHash = ContentHasher.GetContentHash(Encoding.Default.GetBytes(fileContent)).ToHashByteArray();
- CollectionAssert.AreEqual(expectedHash, hash);
+ Assert.AreSequenceEqual(expectedHash, hash);
}
else
{
diff --git a/src/Common.Tests/Hashing/HashingExtensionsTests.cs b/src/Common.Tests/Hashing/HashingExtensionsTests.cs
index 323ae58..728a1e3 100644
--- a/src/Common.Tests/Hashing/HashingExtensionsTests.cs
+++ b/src/Common.Tests/Hashing/HashingExtensionsTests.cs
@@ -25,7 +25,7 @@ public void CombineHashes()
// This doesn't mean anything to a human; it's just intended to exercise the code
byte[] expectedHash = new byte[] { 0x77, 0x8a, 0xaa, 0x14, 0x80, 0x06, 0x5d, 0xf8, 0x87, 0xe0, 0xab, 0xb5, 0x59, 0xd8, 0x26, 0xc5 };
- CollectionAssert.AreEqual(expectedHash, ContentHasher.CombineHashes(hashes));
+ Assert.AreSequenceEqual(expectedHash, ContentHasher.CombineHashes(hashes));
}
[TestMethod]
@@ -50,9 +50,8 @@ public void CombineHashesNullHashes()
null,
};
- CollectionAssert.AreEqual(
- ContentHasher.CombineHashes(hashesWithGaps),
- ContentHasher.CombineHashes(hashes));
+ Assert.AreSequenceEqual(
+ ContentHasher.CombineHashes(hashesWithGaps), ContentHasher.CombineHashes(hashes));
}
[TestMethod]
diff --git a/src/Common.Tests/Hashing/OutputHasherTests.cs b/src/Common.Tests/Hashing/OutputHasherTests.cs
index ed046b2..75d365e 100644
--- a/src/Common.Tests/Hashing/OutputHasherTests.cs
+++ b/src/Common.Tests/Hashing/OutputHasherTests.cs
@@ -33,7 +33,7 @@ public async Task ComputeHash()
#if NETFRAMEWORK
File.WriteAllText(file, "someContent");
#else
- await File.WriteAllTextAsync(file, "someContent");
+ await File.WriteAllTextAsync(file, "someContent", TestContext.CancellationToken);
#endif
ContentHash hash = await hasher.ComputeHashAsync(file, CancellationToken.None);
diff --git a/src/Common.Tests/MSBuildCachePluginBaseTests.cs b/src/Common.Tests/MSBuildCachePluginBaseTests.cs
index ae27d66..3c6488c 100644
--- a/src/Common.Tests/MSBuildCachePluginBaseTests.cs
+++ b/src/Common.Tests/MSBuildCachePluginBaseTests.cs
@@ -56,7 +56,7 @@ public void CheckForDuplicateOutputsDetectsConflictingDuplicateAfterUniqueOutput
Assert.AreSame(currentNode, outputProducer[TrailingUniqueOutputPath]);
Assert.HasCount(2, logger.LogEntries);
Assert.AreEqual(PluginLogLevel.Error, logger.LogEntries[1].LogLevel);
- StringAssert.Contains(logger.LogEntries[1].Message, "with a different hash", StringComparison.Ordinal);
+ Assert.Contains("with a different hash", logger.LogEntries[1].Message, StringComparison.Ordinal);
}
[TestMethod]
@@ -81,7 +81,7 @@ public void CheckForDuplicateOutputsAllowsOrderedIdenticalDuplicateAfterUniqueOu
Assert.AreSame(currentNode, outputProducer[TrailingUniqueOutputPath]);
Assert.HasCount(2, logger.LogEntries);
Assert.AreEqual(PluginLogLevel.Message, logger.LogEntries[1].LogLevel);
- StringAssert.Contains(logger.LogEntries[1].Message, "Allowing as content is the same", StringComparison.Ordinal);
+ Assert.Contains("Allowing as content is the same", logger.LogEntries[1].Message, StringComparison.Ordinal);
}
[TestMethod]
@@ -106,7 +106,7 @@ public void CheckForDuplicateOutputsWarnsForUnorderedIdenticalDuplicateAfterUniq
Assert.AreSame(currentNode, outputProducer[TrailingUniqueOutputPath]);
Assert.HasCount(2, logger.LogEntries);
Assert.AreEqual(PluginLogLevel.Warning, logger.LogEntries[1].LogLevel);
- StringAssert.Contains(logger.LogEntries[1].Message, "there is no ordering between the two nodes", StringComparison.Ordinal);
+ Assert.Contains("there is no ordering between the two nodes", logger.LogEntries[1].Message, StringComparison.Ordinal);
}
private static void CheckForDuplicateOutputs(
diff --git a/src/Common.Tests/NodeBuildResultTests.cs b/src/Common.Tests/NodeBuildResultTests.cs
index 921b0d8..92de0cc 100644
--- a/src/Common.Tests/NodeBuildResultTests.cs
+++ b/src/Common.Tests/NodeBuildResultTests.cs
@@ -28,7 +28,7 @@ public void SortWorksConsistently()
foreach (IList permutation in names.Permutations())
{
var ordinal_sorted = new SortedSet(permutation, StringComparer.OrdinalIgnoreCase);
- CollectionAssert.AreEqual(baseline, ordinal_sorted);
+ Assert.AreSequenceEqual(baseline, ordinal_sorted);
}
}
@@ -55,7 +55,7 @@ public void SortWorksConsistentlyAcrossJson()
string serialized = JsonSerializer.Serialize(nodeBuildResult, SourceGenerationContext.Default.NodeBuildResult);
NodeBuildResult deserialized = JsonSerializer.Deserialize(serialized, SourceGenerationContext.Default.NodeBuildResult)!;
- CollectionAssert.AreEqual(expected.Keys, deserialized.Outputs.Keys, "\n" +
+ Assert.AreSequenceEqual(expected.Keys, deserialized.Outputs.Keys, "\n" +
"Permutation: " + string.Join(", ", permutation) + "\n" +
"Serialized: " + serialized + "\n" +
"Deserialized: " + string.Join(", ", deserialized.Outputs.Keys) + "\n" +
diff --git a/src/Common.Tests/ObservationFilterTests.cs b/src/Common.Tests/ObservationFilterTests.cs
index a51496c..5197d04 100644
--- a/src/Common.Tests/ObservationFilterTests.cs
+++ b/src/Common.Tests/ObservationFilterTests.cs
@@ -104,12 +104,12 @@ public void BuildEverWrittenOrAncestorSetIncludesAllAncestors()
});
// Must include the file itself plus every ancestor up to drive root.
- Assert.IsTrue(result.Contains(@"X:\Repo\bin\Debug\net9.0\TestProject.dll"));
- Assert.IsTrue(result.Contains(@"X:\Repo\bin\Debug\net9.0"));
- Assert.IsTrue(result.Contains(@"X:\Repo\bin\Debug"));
- Assert.IsTrue(result.Contains(@"X:\Repo\bin"));
- Assert.IsTrue(result.Contains(@"X:\Repo"));
- Assert.IsTrue(result.Contains(@"X:\"));
+ Assert.Contains(@"X:\Repo\bin\Debug\net9.0\TestProject.dll", result);
+ Assert.Contains(@"X:\Repo\bin\Debug\net9.0", result);
+ Assert.Contains(@"X:\Repo\bin\Debug", result);
+ Assert.Contains(@"X:\Repo\bin", result);
+ Assert.Contains(@"X:\Repo", result);
+ Assert.Contains(@"X:\", result);
}
[TestMethod]
@@ -142,7 +142,7 @@ public void BuildEverWrittenOrAncestorSetCaseInsensitive()
// Both files plus shared ancestor chain @ "X:\Repo\BIN\Debug" + "X:\Repo\BIN" + "X:\Repo" + "X:\"
// First write's ancestors get added with their casing; second write's ancestors are deduped via
// OrdinalIgnoreCase.
- Assert.AreEqual(6, result.Count);
+ Assert.HasCount(6, result);
}
[TestMethod]
@@ -156,19 +156,19 @@ public void BuildEverWrittenOrAncestorSetTrimsTrailingSeparator()
@"X:\Repo\bin\Debug\net9.0\",
});
- Assert.IsTrue(result.Contains(@"X:\Repo\bin\Debug\net9.0"));
- Assert.IsFalse(result.Contains(@"X:\Repo\bin\Debug\net9.0\"));
- Assert.IsTrue(result.Contains(@"X:\Repo\bin\Debug"));
- Assert.IsTrue(result.Contains(@"X:\Repo\bin"));
- Assert.IsTrue(result.Contains(@"X:\Repo"));
- Assert.IsTrue(result.Contains(@"X:\"));
+ Assert.Contains(@"X:\Repo\bin\Debug\net9.0", result);
+ Assert.DoesNotContain(@"X:\Repo\bin\Debug\net9.0\", result);
+ Assert.Contains(@"X:\Repo\bin\Debug", result);
+ Assert.Contains(@"X:\Repo\bin", result);
+ Assert.Contains(@"X:\Repo", result);
+ Assert.Contains(@"X:\", result);
}
[TestMethod]
public void BuildEverWrittenOrAncestorSetEmptyInput()
{
HashSet result = FileAccessRepository.BuildEverWrittenOrAncestorSet(new List());
- Assert.AreEqual(0, result.Count);
+ Assert.IsEmpty(result);
}
[TestMethod]
diff --git a/src/Common.Tests/PathSetTests.cs b/src/Common.Tests/PathSetTests.cs
index 47bbbeb..781c2ca 100644
--- a/src/Common.Tests/PathSetTests.cs
+++ b/src/Common.Tests/PathSetTests.cs
@@ -27,7 +27,7 @@ public void DeserializingPayloadWithoutEntriesDoesNotThrow()
Assert.IsNotNull(deserialized);
Assert.IsNotNull(deserialized!.Entries, "Entries must never be null; it is hashed before it can be checked.");
- Assert.AreEqual(0, deserialized.Entries.Count);
+ Assert.IsEmpty(deserialized.Entries);
// Both must be callable, since the type is used as a cache key.
_ = deserialized.GetHashCode();
@@ -153,7 +153,7 @@ public void JsonRoundTripAllObservationTypes()
Assert.IsNotNull(deserialized);
Assert.AreEqual(original, deserialized);
// Sanity-check that every type round-tripped: equality covers it, but be explicit about the schema field.
- Assert.AreEqual(4, deserialized!.Entries.Count);
+ Assert.HasCount(4, deserialized!.Entries);
Assert.AreEqual(ObservationType.FileContentRead, deserialized.Entries[0].Type);
Assert.AreEqual(ObservationType.DirectoryEnumeration, deserialized.Entries[1].Type);
Assert.AreEqual("*.cs", deserialized.Entries[1].EnumerationPattern);
diff --git a/src/Common.Tests/PluginSettingsExtensibilityTests.cs b/src/Common.Tests/PluginSettingsExtensibilityTests.cs
index 073bf2e..40321a1 100644
--- a/src/Common.Tests/PluginSettingsExtensibilityTests.cs
+++ b/src/Common.Tests/PluginSettingsExtensibilityTests.cs
@@ -35,7 +35,7 @@ public void EffectiveSettingsLogging()
{
// All properties are { get; init; }
Assert.IsTrue(property.CanRead);
- Assert.IsTrue(property.GetSetMethod()!.ReturnParameter.GetRequiredCustomModifiers().Any(t => t.Name.Equals("IsExternalInit", StringComparison.Ordinal)));
+ Assert.Contains(t => t.Name.Equals("IsExternalInit", StringComparison.Ordinal), property.GetSetMethod()!.ReturnParameter.GetRequiredCustomModifiers());
// RepoRoot isn't included in the logging.
bool shouldBeLogged = !property.Name.Equals(nameof(PluginSettings.RepoRoot), StringComparison.Ordinal);
@@ -80,17 +80,17 @@ public void DefaultValue()
Assert.AreEqual(DefaultMockPluginSettings.GlobSetting.ToString(), pluginSettings.GlobSetting.ToString());
- CollectionAssert.AreEqual(DefaultMockPluginSettings.ArraySetting, pluginSettings.ArraySetting);
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.ArraySetting, pluginSettings.ArraySetting);
- CollectionAssert.AreEqual(DefaultMockPluginSettings.ListSetting, pluginSettings.ListSetting);
- CollectionAssert.AreEqual(DefaultMockPluginSettings.IListSetting.ToList(), pluginSettings.IListSetting.ToList());
- CollectionAssert.AreEqual(DefaultMockPluginSettings.ICollectionSetting.ToList(), pluginSettings.ICollectionSetting.ToList());
- CollectionAssert.AreEqual(DefaultMockPluginSettings.IEnumerableSetting.ToList(), pluginSettings.IEnumerableSetting.ToList());
- CollectionAssert.AreEqual(DefaultMockPluginSettings.IReadOnlyListSetting.ToList(), pluginSettings.IReadOnlyListSetting.ToList());
- CollectionAssert.AreEqual(DefaultMockPluginSettings.IReadOnlyCollectionSetting.ToList(), pluginSettings.IReadOnlyCollectionSetting.ToList());
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.ListSetting, pluginSettings.ListSetting);
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.IListSetting.ToList(), pluginSettings.IListSetting.ToList());
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.ICollectionSetting.ToList(), pluginSettings.ICollectionSetting.ToList());
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.IEnumerableSetting.ToList(), pluginSettings.IEnumerableSetting.ToList());
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.IReadOnlyListSetting.ToList(), pluginSettings.IReadOnlyListSetting.ToList());
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.IReadOnlyCollectionSetting.ToList(), pluginSettings.IReadOnlyCollectionSetting.ToList());
- CollectionAssert.AreEquivalent(DefaultMockPluginSettings.HashSetSetting.ToList(), pluginSettings.HashSetSetting.ToList());
- CollectionAssert.AreEquivalent(DefaultMockPluginSettings.ISetSetting.ToList(), pluginSettings.ISetSetting.ToList());
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.HashSetSetting.ToList(), pluginSettings.HashSetSetting.ToList(), SequenceOrder.InAnyOrder);
+ Assert.AreSequenceEqual(DefaultMockPluginSettings.ISetSetting.ToList(), pluginSettings.ISetSetting.ToList(), SequenceOrder.InAnyOrder);
AssertNotLogged(logger, PluginLogLevel.Warning, "has invalid value");
AssertNotLogged(logger, PluginLogLevel.Warning, "has unsupported type");
@@ -172,7 +172,7 @@ void AssertInvalidValueHandled(string settingName, Func(string settingName, Func> valueAccessor)
{
AssertLogged(logger, PluginLogLevel.Warning, $"'{settingName}' has invalid value");
- CollectionAssert.AreEqual(valueAccessor(DefaultMockPluginSettings).ToList(), valueAccessor(pluginSettings).ToList());
+ Assert.AreSequenceEqual(valueAccessor(DefaultMockPluginSettings).ToList(), valueAccessor(pluginSettings).ToList());
}
}
@@ -232,17 +232,17 @@ public void ExplicitValues()
Assert.AreEqual(@"X:\Repo\**\b.*", pluginSettings.GlobSetting.ToString());
- CollectionAssert.AreEqual(new[] { 4, 5, 6 }, pluginSettings.ArraySetting);
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.ArraySetting);
- CollectionAssert.AreEqual(new[] { 4, 5, 6 }, pluginSettings.ListSetting);
- CollectionAssert.AreEqual(new[] { 4, 5, 6 }, pluginSettings.IListSetting.ToList());
- CollectionAssert.AreEqual(new[] { 4, 5, 6 }, pluginSettings.ICollectionSetting.ToList());
- CollectionAssert.AreEqual(new[] { 4, 5, 6 }, pluginSettings.IEnumerableSetting.ToList());
- CollectionAssert.AreEqual(new[] { 4, 5, 6 }, pluginSettings.IReadOnlyListSetting.ToList());
- CollectionAssert.AreEqual(new[] { 4, 5, 6 }, pluginSettings.IReadOnlyCollectionSetting.ToList());
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.ListSetting);
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.IListSetting.ToList());
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.ICollectionSetting.ToList());
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.IEnumerableSetting.ToList());
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.IReadOnlyListSetting.ToList());
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.IReadOnlyCollectionSetting.ToList());
- CollectionAssert.AreEquivalent(new[] { 4, 5, 6 }, pluginSettings.HashSetSetting.ToList());
- CollectionAssert.AreEquivalent(new[] { 4, 5, 6 }, pluginSettings.ISetSetting.ToList());
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.HashSetSetting.ToList(), SequenceOrder.InAnyOrder);
+ Assert.AreSequenceEqual(new[] { 4, 5, 6 }, pluginSettings.ISetSetting.ToList(), SequenceOrder.InAnyOrder);
AssertNotLogged(logger, PluginLogLevel.Warning, "has invalid value");
AssertNotLogged(logger, PluginLogLevel.Warning, "has unsupported type");
diff --git a/src/Common.Tests/PluginSettingsTests.cs b/src/Common.Tests/PluginSettingsTests.cs
index ccbdfb0..5fd6c5c 100644
--- a/src/Common.Tests/PluginSettingsTests.cs
+++ b/src/Common.Tests/PluginSettingsTests.cs
@@ -41,7 +41,7 @@ public void EffectiveSettingsLogging()
{
// All properties are { get; init; }
Assert.IsTrue(property.CanRead);
- Assert.IsTrue(property.GetSetMethod()!.ReturnParameter.GetRequiredCustomModifiers().Any(t => t.Name.Equals("IsExternalInit", StringComparison.Ordinal)));
+ Assert.Contains(t => t.Name.Equals("IsExternalInit", StringComparison.Ordinal), property.GetSetMethod()!.ReturnParameter.GetRequiredCustomModifiers());
// RepoRoot isn't included in the logging.
bool isLogged = !property.Name.Equals(nameof(PluginSettings.RepoRoot), StringComparison.Ordinal);
@@ -135,18 +135,18 @@ public void ProbeAndEnumerationFingerprintingForcedOffWithoutCapability(bool req
"The setting must be forced off when the host MSBuild cannot report the required file access fields, "
+ "even when the user explicitly asked for it.");
- Assert.IsTrue(
- logger.LogEntries.Any(entry => entry.Message.Contains(
+ Assert.Contains(
+ entry => entry.Message.Contains(
nameof(PluginSettings.EnableProbeAndEnumerationFingerprinting), StringComparison.Ordinal)
- && entry.Message.Contains("forced to false", StringComparison.Ordinal)),
+ && entry.Message.Contains("forced to false", StringComparison.Ordinal), logger.LogEntries,
"Forcing the setting off must be logged so the cache-behavior change is diagnosable.");
// Naming the running version is what makes the message actionable — otherwise a user is told the
// feature is off but not what they are on or that upgrading would fix it.
if (FileAccessDataCapabilities.MSBuildVersion is string msbuildVersion)
{
- Assert.IsTrue(
- logger.LogEntries.Any(entry => entry.Message.Contains(msbuildVersion, StringComparison.Ordinal)),
+ Assert.Contains(
+ entry => entry.Message.Contains(msbuildVersion, StringComparison.Ordinal), logger.LogEntries,
$"The message must name the running MSBuild version ('{msbuildVersion}').");
}
}
@@ -267,11 +267,11 @@ public void AllowFileAccessAfterProjectFinishFilePatternsSupportsMachineLocalCat
supportsProbeAndEnumerationCapture: true);
IReadOnlyCollection patterns = pluginSettings.AllowFileAccessAfterProjectFinishFilePatterns;
- Assert.IsTrue(patterns.Any(pattern => pattern.IsMatch(@"C:\Program Files\Telemetry\ApplicationInsights.config")));
- Assert.IsTrue(patterns.Any(pattern => pattern.IsMatch(@"C:\Users\Test\AppData\Local\Microsoft\VSApplicationInsights\config.json")));
- Assert.IsTrue(patterns.Any(pattern => pattern.IsMatch(@"C:\Users\Test\AppData\Local\Microsoft\Windows\INetCache\IE\ABC\dyntelconfig[2].cache")));
- Assert.IsTrue(patterns.Any(pattern => pattern.IsMatch(@"C:\Windows\System32\ci.dll")));
- Assert.IsFalse(patterns.Any(pattern => pattern.IsMatch(@"X:\Repo\src\Program.cs")));
+ Assert.Contains(pattern => pattern.IsMatch(@"C:\Program Files\Telemetry\ApplicationInsights.config"), patterns);
+ Assert.Contains(pattern => pattern.IsMatch(@"C:\Users\Test\AppData\Local\Microsoft\VSApplicationInsights\config.json"), patterns);
+ Assert.Contains(pattern => pattern.IsMatch(@"C:\Users\Test\AppData\Local\Microsoft\Windows\INetCache\IE\ABC\dyntelconfig[2].cache"), patterns);
+ Assert.Contains(pattern => pattern.IsMatch(@"C:\Windows\System32\ci.dll"), patterns);
+ Assert.DoesNotContain(pattern => pattern.IsMatch(@"X:\Repo\src\Program.cs"), patterns);
}
[TestMethod]
@@ -403,7 +403,7 @@ private static void TestStringListSetting(
RepoRoot,
supportsProbeAndEnumerationCapture: true);
- CollectionAssert.AreEqual(testCase.ExpectedValues.ToList(), valueAccessor(pluginSettings).ToList());
+ Assert.AreSequenceEqual(testCase.ExpectedValues.ToList(), valueAccessor(pluginSettings).ToList());
}
public static IEnumerable