From ecd7a0253f4e70bd199e9e892671b982c74a7d03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Oct 2026 01:30:50 +0000 Subject: [PATCH 1/3] Bump NUnit and NUnit3TestAdapter Bumps NUnit from 4.6.1 to 5.0.0 Bumps NUnit3TestAdapter from 6.2.0 to 6.3.1 --- updated-dependencies: - dependency-name: NUnit dependency-version: 5.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: testing - dependency-name: NUnit3TestAdapter dependency-version: 6.3.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: testing ... Signed-off-by: dependabot[bot] --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 1fb2c7a..7e7d31a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,8 +9,8 @@ - - + + From cdb156ca86ff6665b0355343a098238fb35ef44e Mon Sep 17 00:00:00 2001 From: James A Sutherland Date: Tue, 13 Oct 2026 20:36:04 -0500 Subject: [PATCH 2/3] Fix tests for NUnit 5 API changes - Await Assert.ThrowsAsync/CatchAsync, which now return Task. Six calls in synchronous test methods compiled without warning but no longer asserted anything; those methods are now async - Narrow Has.Some.Matches lambda parameter, now nullable, via pattern match - Remove obsolete [Order]; SetUp/TearDown already reset NativeCodecs state --- .../NativeCodecsTests.cs | 1 - tests/SharpDicom.Tests/Cli/LintCommandTests.cs | 2 +- .../Deidentification/BatchDeidentifierTests.cs | 2 +- .../IO/DicomFileReaderSequenceTests.cs | 4 ++-- .../IO/DicomFileReaderTests.cs | 6 +++--- .../IO/PixelDataSourceTests.cs | 8 ++++---- .../Integration/ImplicitVRSequenceTests.cs | 4 ++-- tests/SharpDicom.Tests/Network/CEchoTests.cs | 6 +++--- .../Network/DicomClientTests.cs | 4 ++-- .../Network/Tls/TlsIntegrationTests.cs | 18 +++++++++--------- 10 files changed, 27 insertions(+), 28 deletions(-) diff --git a/tests/SharpDicom.Codecs.Tests/NativeCodecsTests.cs b/tests/SharpDicom.Codecs.Tests/NativeCodecsTests.cs index 5f89dec..c0ba0db 100644 --- a/tests/SharpDicom.Codecs.Tests/NativeCodecsTests.cs +++ b/tests/SharpDicom.Codecs.Tests/NativeCodecsTests.cs @@ -43,7 +43,6 @@ public void IsAvailable_BeforeInitialize_ReturnsFalse() #if NET5_0_OR_GREATER [Test] [Category("NativeCodecs")] - [Order(0)] // Run this test first to check initialization before other tests public void InitializeWithCustomPath_WhenLibraryExists_Succeeds() { // This test explicitly passes the library path to Initialize diff --git a/tests/SharpDicom.Tests/Cli/LintCommandTests.cs b/tests/SharpDicom.Tests/Cli/LintCommandTests.cs index cfd8aeb..a298369 100644 --- a/tests/SharpDicom.Tests/Cli/LintCommandTests.cs +++ b/tests/SharpDicom.Tests/Cli/LintCommandTests.cs @@ -122,7 +122,7 @@ public async Task InvalidUidFile_StrictProfile_ThrowsOnError() }; // Strict mode throws on invalid UIDs rather than collecting them - Assert.ThrowsAsync( + await Assert.ThrowsAsync( async () => await DicomFile.OpenAsync(path, options)); } diff --git a/tests/SharpDicom.Tests/Deidentification/BatchDeidentifierTests.cs b/tests/SharpDicom.Tests/Deidentification/BatchDeidentifierTests.cs index eda38df..1129b5b 100644 --- a/tests/SharpDicom.Tests/Deidentification/BatchDeidentifierTests.cs +++ b/tests/SharpDicom.Tests/Deidentification/BatchDeidentifierTests.cs @@ -391,7 +391,7 @@ public async Task BatchDeidentifier_InvalidInputDir_ThrowsDirectoryNotFoundExcep var config = DeidentificationConfigLoader.GetPreset("basic-profile"); await using var batch = new BatchDeidentifier(_dbPath, config); - Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await batch.ProcessDirectoryAsync("/nonexistent/path", _outputDir); }); diff --git a/tests/SharpDicom.Tests/IO/DicomFileReaderSequenceTests.cs b/tests/SharpDicom.Tests/IO/DicomFileReaderSequenceTests.cs index 13859ff..430f972 100644 --- a/tests/SharpDicom.Tests/IO/DicomFileReaderSequenceTests.cs +++ b/tests/SharpDicom.Tests/IO/DicomFileReaderSequenceTests.cs @@ -58,7 +58,7 @@ public async Task ReadElementsAsync_FileWithSequence_YieldsSequenceElement() // Assert - should have the sequence Assert.That(elements, Has.Some.Matches(e => - e.Tag == new DicomTag(0x0040, 0x0100) && e is DicomSequence)); + e is DicomSequence sequence && sequence.Tag == new DicomTag(0x0040, 0x0100))); } [Test] @@ -128,7 +128,7 @@ public async Task ReadElementsAsync_ImplicitVRFileWithSequence_ParsesCorrectly() // Assert - should have the sequence Assert.That(elements, Has.Some.Matches(e => - e.Tag == new DicomTag(0x0040, 0x0100) && e is DicomSequence)); + e is DicomSequence sequence && sequence.Tag == new DicomTag(0x0040, 0x0100))); } [Test] diff --git a/tests/SharpDicom.Tests/IO/DicomFileReaderTests.cs b/tests/SharpDicom.Tests/IO/DicomFileReaderTests.cs index d305976..53340c0 100644 --- a/tests/SharpDicom.Tests/IO/DicomFileReaderTests.cs +++ b/tests/SharpDicom.Tests/IO/DicomFileReaderTests.cs @@ -38,7 +38,7 @@ public async Task ReadFileMetaInfoAsync_EmptyFile_Throws() await using var reader = new DicomFileReader(stream); - Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => await reader.ReadFileMetaInfoAsync()); } @@ -106,7 +106,7 @@ public async Task ReadElementsAsync_RespectsCancellation() cts.Cancel(); // TaskCanceledException is a subclass of OperationCanceledException - var ex = Assert.CatchAsync(async () => + var ex = await Assert.CatchAsync(async () => { await foreach (var _ in reader.ReadElementsAsync(cts.Token)) { @@ -219,7 +219,7 @@ public async Task AfterDispose_ReadThrows() var reader = new DicomFileReader(stream); await reader.DisposeAsync(); - Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => await reader.ReadFileMetaInfoAsync()); } diff --git a/tests/SharpDicom.Tests/IO/PixelDataSourceTests.cs b/tests/SharpDicom.Tests/IO/PixelDataSourceTests.cs index c4a9af5..12aa1b6 100644 --- a/tests/SharpDicom.Tests/IO/PixelDataSourceTests.cs +++ b/tests/SharpDicom.Tests/IO/PixelDataSourceTests.cs @@ -354,24 +354,24 @@ public void SkippedPixelDataSource_GetData_ThrowsInvalidOperationException() } [Test] - public void SkippedPixelDataSource_GetDataAsync_ThrowsInvalidOperationException() + public async Task SkippedPixelDataSource_GetDataAsync_ThrowsInvalidOperationException() { // Arrange using var source = new SkippedPixelDataSource(0, 100); // Act & Assert - Assert.ThrowsAsync(async () => await source.GetDataAsync()); + await Assert.ThrowsAsync(async () => await source.GetDataAsync()); } [Test] - public void SkippedPixelDataSource_CopyToAsync_ThrowsInvalidOperationException() + public async Task SkippedPixelDataSource_CopyToAsync_ThrowsInvalidOperationException() { // Arrange using var source = new SkippedPixelDataSource(0, 100); using var destination = new MemoryStream(); // Act & Assert - Assert.ThrowsAsync(async () => await source.CopyToAsync(destination)); + await Assert.ThrowsAsync(async () => await source.CopyToAsync(destination)); } [Test] diff --git a/tests/SharpDicom.Tests/Integration/ImplicitVRSequenceTests.cs b/tests/SharpDicom.Tests/Integration/ImplicitVRSequenceTests.cs index 335c561..8c243cd 100644 --- a/tests/SharpDicom.Tests/Integration/ImplicitVRSequenceTests.cs +++ b/tests/SharpDicom.Tests/Integration/ImplicitVRSequenceTests.cs @@ -141,14 +141,14 @@ public async Task Parse_NestedSequences_ParentChainCorrect() } [Test] - public void Parse_NestedSequences_ExceedsMaxDepth_ThrowsException() + public async Task Parse_NestedSequences_ExceedsMaxDepth_ThrowsException() { var data = CreateDeeplyNestedSequenceFile(10); using var stream = new MemoryStream(data); var options = new DicomReaderOptions { MaxSequenceDepth = 4 }; - Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await DicomFile.OpenAsync(stream, options); }); diff --git a/tests/SharpDicom.Tests/Network/CEchoTests.cs b/tests/SharpDicom.Tests/Network/CEchoTests.cs index 03ea1a2..6b75d82 100644 --- a/tests/SharpDicom.Tests/Network/CEchoTests.cs +++ b/tests/SharpDicom.Tests/Network/CEchoTests.cs @@ -259,7 +259,7 @@ public async Task AssociationReject_ClientThrowsException() }; // Act & Assert - var ex = Assert.ThrowsAsync( + var ex = await Assert.ThrowsAsync( async () => await client.ConnectAsync(contexts)); Assert.That(ex, Is.Not.Null); @@ -281,7 +281,7 @@ public async Task CEchoAsync_BeforeConnect_ThrowsException() await using var client = new DicomClient(clientOptions); // Act & Assert - var ex = Assert.ThrowsAsync( + var ex = await Assert.ThrowsAsync( async () => await client.CEchoAsync()); Assert.That(ex, Is.Not.Null); @@ -326,7 +326,7 @@ public async Task CEchoAsync_VerificationNotNegotiated_ThrowsException() await client.ConnectAsync(contexts); // Act & Assert - C-ECHO should fail because Verification SOP Class not negotiated - var ex = Assert.ThrowsAsync( + var ex = await Assert.ThrowsAsync( async () => await client.CEchoAsync()); Assert.That(ex, Is.Not.Null); diff --git a/tests/SharpDicom.Tests/Network/DicomClientTests.cs b/tests/SharpDicom.Tests/Network/DicomClientTests.cs index a4d5599..c46a5e6 100644 --- a/tests/SharpDicom.Tests/Network/DicomClientTests.cs +++ b/tests/SharpDicom.Tests/Network/DicomClientTests.cs @@ -290,7 +290,7 @@ public async Task ConnectAsync_AfterDispose_ThrowsObjectDisposedException() new PresentationContext(1, DicomUID.Verification, TransferSyntax.ImplicitVRLittleEndian) }; - Assert.ThrowsAsync( + await Assert.ThrowsAsync( async () => await client.ConnectAsync(contexts)); } @@ -301,7 +301,7 @@ public async Task CEchoAsync_AfterDispose_ThrowsObjectDisposedException() var client = new DicomClient(options); await client.DisposeAsync(); - Assert.ThrowsAsync( + await Assert.ThrowsAsync( async () => await client.CEchoAsync()); } diff --git a/tests/SharpDicom.Tests/Network/Tls/TlsIntegrationTests.cs b/tests/SharpDicom.Tests/Network/Tls/TlsIntegrationTests.cs index 232be51..0c24e8f 100644 --- a/tests/SharpDicom.Tests/Network/Tls/TlsIntegrationTests.cs +++ b/tests/SharpDicom.Tests/Network/Tls/TlsIntegrationTests.cs @@ -429,7 +429,7 @@ public async Task SelfSigned_AcceptedViaThumbprint() } [Test] - public void SelfSigned_RejectedWithoutThumbprint() + public async Task SelfSigned_RejectedWithoutThumbprint() { // Arrange var serverCert = TlsCertificateHelper.CreateSelfSignedCertificate("localhost", TimeSpan.FromHours(1)); @@ -450,7 +450,7 @@ public void SelfSigned_RejectedWithoutThumbprint() var server = new DicomServer(serverOptions); server.Start(); - Task.Delay(100).Wait(); + await Task.Delay(100); try { @@ -476,7 +476,7 @@ public void SelfSigned_RejectedWithoutThumbprint() }; // Act & Assert - Assert.ThrowsAsync(async () => + await Assert.ThrowsAsync(async () => { await using (client) { @@ -561,7 +561,7 @@ public async Task CustomCA_AcceptedWithCAInTrustStore() #if NET6_0_OR_GREATER [Test] - public void MutualTls_MissingClientCert_Fails() + public async Task MutualTls_MissingClientCert_Fails() { // Arrange using var serverCert = TlsCertificateHelper.CreateSelfSignedCertificate("localhost", TimeSpan.FromHours(1)); @@ -584,7 +584,7 @@ public void MutualTls_MissingClientCert_Fails() var server = new DicomServer(serverOptions); server.Start(); - Task.Delay(100).Wait(); + await Task.Delay(100); try { @@ -612,7 +612,7 @@ public void MutualTls_MissingClientCert_Fails() // Act & Assert - Server will close connection when client doesn't provide cert // This can manifest as TlsHandshakeException or EndOfStreamException - Assert.CatchAsync(async () => + await Assert.CatchAsync(async () => { await using (client) { @@ -628,7 +628,7 @@ public void MutualTls_MissingClientCert_Fails() } [Test] - public void InvalidCert_Rejected() + public async Task InvalidCert_Rejected() { // Arrange - Use expired certificate var expiredCert = TlsCertificateHelper.CreateExpiredCertificate("localhost"); @@ -649,7 +649,7 @@ public void InvalidCert_Rejected() var server = new DicomServer(serverOptions); server.Start(); - Task.Delay(100).Wait(); + await Task.Delay(100); try { @@ -675,7 +675,7 @@ public void InvalidCert_Rejected() }; // Act & Assert - var ex = Assert.ThrowsAsync(async () => + var ex = await Assert.ThrowsAsync(async () => { await using (client) { From c0c08d3f88d02e15c402494b81f4c2ae473d0e3a Mon Sep 17 00:00:00 2001 From: James A Sutherland Date: Wed, 21 Oct 2026 13:32:40 -0500 Subject: [PATCH 3/3] Hold NUnit3TestAdapter at 6.2.0 Adapter 6.3.x makes the MTP code coverage extension search the whole working directory recursively for modules to statically instrument, instead of the test assembly's output directory. With all test projects running in parallel under --coverage, each host rewrites the others' binaries; locally a single project goes from 3s to ~60s and the full run hangs, and on CI the test step stalled until the runner was shut down. NUnit 5.0.0 works with adapter 6.2.0. Ignore 6.3.0/6.3.1 in Dependabot. --- .github/dependabot.yml | 6 ++++++ Directory.Packages.props | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 76c7588..9704300 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,6 +15,12 @@ updates: patterns: - "NUnit*" - "coverlet*" + ignore: + # 6.3.x makes Microsoft.Testing.Extensions.CodeCoverage statically + # instrument every DLL under the repo root instead of the test bin dir, + # so parallel test hosts rewrite each other's binaries and CI hangs. + - dependency-name: "NUnit3TestAdapter" + versions: ["6.3.0", "6.3.1"] - package-ecosystem: github-actions directory: / diff --git a/Directory.Packages.props b/Directory.Packages.props index 7b620b7..49b907b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,7 +10,7 @@ - +