diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index a87cf069..76c75882 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -15,7 +15,6 @@ updates:
patterns:
- "NUnit*"
- "coverlet*"
- - "Verify*"
- package-ecosystem: github-actions
directory: /
diff --git a/Directory.Packages.props b/Directory.Packages.props
index ef3978fa..1fb2c7ad 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -33,9 +33,6 @@
-
-
-
diff --git a/tests/SharpDicom.Codecs.Tests/NativeCodecsTests.cs b/tests/SharpDicom.Codecs.Tests/NativeCodecsTests.cs
index e93b447b..5f89dec5 100644
--- a/tests/SharpDicom.Codecs.Tests/NativeCodecsTests.cs
+++ b/tests/SharpDicom.Codecs.Tests/NativeCodecsTests.cs
@@ -428,8 +428,8 @@ public void NativeCodecOptions_CanSetAllProperties()
[Test]
public void Initialize_WithEnableOptions_SetsProperties()
{
- // Note: Enable options are only applied during successful initialization
- // When native library is unavailable, options aren't applied after DllNotFoundException
+ // Enable options are applied before the native library is probed,
+ // so they take effect whether or not the library loads
NativeCodecs.Initialize(new NativeCodecOptions
{
SuppressInitializationErrors = true,
@@ -439,23 +439,10 @@ public void Initialize_WithEnableOptions_SetsProperties()
EnableVideo = false
});
- // If native library was loaded successfully, enable options should be set
- if (NativeCodecs.IsAvailable)
- {
- Assert.That(NativeCodecs.EnableJpeg, Is.False);
- Assert.That(NativeCodecs.EnableJpeg2000, Is.False);
- Assert.That(NativeCodecs.EnableJpegLs, Is.False);
- Assert.That(NativeCodecs.EnableVideo, Is.False);
- }
- else
- {
- // When library unavailable, options weren't applied - properties remain default
- // This is a design limitation when native libraries are missing
- Assert.That(NativeCodecs.EnableJpeg, Is.True, "Default value when library unavailable");
- Assert.That(NativeCodecs.EnableJpeg2000, Is.True, "Default value when library unavailable");
- Assert.That(NativeCodecs.EnableJpegLs, Is.True, "Default value when library unavailable");
- Assert.That(NativeCodecs.EnableVideo, Is.True, "Default value when library unavailable");
- }
+ Assert.That(NativeCodecs.EnableJpeg, Is.False);
+ Assert.That(NativeCodecs.EnableJpeg2000, Is.False);
+ Assert.That(NativeCodecs.EnableJpegLs, Is.False);
+ Assert.That(NativeCodecs.EnableVideo, Is.False);
}
#endregion
diff --git a/tests/SharpDicom.Tests.Polyfills/SharpDicom.Tests.Polyfills.csproj b/tests/SharpDicom.Tests.Polyfills/SharpDicom.Tests.Polyfills.csproj
index d2255072..eb595df0 100644
--- a/tests/SharpDicom.Tests.Polyfills/SharpDicom.Tests.Polyfills.csproj
+++ b/tests/SharpDicom.Tests.Polyfills/SharpDicom.Tests.Polyfills.csproj
@@ -18,8 +18,6 @@
-
-
@@ -30,8 +28,13 @@
-
+
-
+
+
+
+
+
+
diff --git a/tests/SharpDicom.Tests/Generators/DicomDictionaryGeneratorTests.cs b/tests/SharpDicom.Tests/Generators/DicomDictionaryGeneratorTests.cs
index 9a6dbe2b..284ae4e7 100644
--- a/tests/SharpDicom.Tests/Generators/DicomDictionaryGeneratorTests.cs
+++ b/tests/SharpDicom.Tests/Generators/DicomDictionaryGeneratorTests.cs
@@ -1,21 +1,19 @@
using System.Collections.Immutable;
-using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using NUnit.Framework;
using SharpDicom.Generators;
-using VerifyNUnit;
namespace SharpDicom.Tests.Generators;
///
-/// Snapshot tests for the DicomDictionaryGenerator using Verify.SourceGenerators.
+/// Snapshot tests for the DicomDictionaryGenerator.
///
[TestFixture]
public class DicomDictionaryGeneratorTests
{
[Test]
- public Task GeneratesTagsFromMinimalXml()
+ public void GeneratesTagsFromMinimalXml()
{
// Minimal Part 6-like XML with one tag
var xml = """
@@ -39,11 +37,11 @@ public Task GeneratesTagsFromMinimalXml()
""";
- return VerifyGenerator(xml, "part06.xml");
+ VerifyGenerator(xml, "part06.xml");
}
[Test]
- public Task GeneratesMultiVRTag()
+ public void GeneratesMultiVRTag()
{
// Tag with multiple VRs (US or SS)
var xml = """
@@ -67,11 +65,11 @@ public Task GeneratesMultiVRTag()
""";
- return VerifyGenerator(xml, "part06.xml");
+ VerifyGenerator(xml, "part06.xml");
}
[Test]
- public Task GeneratesRetiredTag()
+ public void GeneratesRetiredTag()
{
// Retired tag (should be marked with IsRetired=true)
var xml = """
@@ -95,11 +93,11 @@ public Task GeneratesRetiredTag()
""";
- return VerifyGenerator(xml, "part06.xml");
+ VerifyGenerator(xml, "part06.xml");
}
[Test]
- public Task GeneratesUIDs()
+ public void GeneratesUIDs()
{
// UID registry table
var xml = """
@@ -129,11 +127,11 @@ public Task GeneratesUIDs()
""";
- return VerifyGenerator(xml, "part06.xml");
+ VerifyGenerator(xml, "part06.xml");
}
[Test]
- public Task HandlesMaskedTag()
+ public void HandlesMaskedTag()
{
// Masked tag (50xx,0010)
var xml = """
@@ -157,11 +155,11 @@ public Task HandlesMaskedTag()
""";
- return VerifyGenerator(xml, "part06.xml");
+ VerifyGenerator(xml, "part06.xml");
}
[Test]
- public Task HandlesEmptyXml()
+ public void HandlesEmptyXml()
{
var xml = """
@@ -169,19 +167,19 @@ public Task HandlesEmptyXml()
""";
- return VerifyGenerator(xml, "part06.xml");
+ VerifyGenerator(xml, "part06.xml");
}
[Test]
- public Task ReportsDiagnosticForInvalidXml()
+ public void ReportsDiagnosticForInvalidXml()
{
var xml = "not valid xml";
- return VerifyGenerator(xml, "part06.xml");
+ VerifyGenerator(xml, "part06.xml");
}
[Test]
- public Task GeneratesTagsAndUIDsCombined()
+ public void GeneratesTagsAndUIDsCombined()
{
// Combined tags and UIDs in same XML
var xml = """
@@ -217,10 +215,10 @@ public Task GeneratesTagsAndUIDsCombined()
""";
- return VerifyGenerator(xml, "part06.xml");
+ VerifyGenerator(xml, "part06.xml");
}
- private static Task VerifyGenerator(string xmlContent, string fileName)
+ private static void VerifyGenerator(string xmlContent, string fileName)
{
// Create compilation
// IL3000: Assembly.Location is required for source generator tests to get metadata references
@@ -246,6 +244,6 @@ private static Task VerifyGenerator(string xmlContent, string fileName)
driver = driver.RunGenerators(compilation);
// Verify snapshot
- return Verifier.Verify(driver);
+ GeneratorSnapshot.Verify(driver);
}
}
diff --git a/tests/SharpDicom.Tests/Generators/GeneratorSnapshot.cs b/tests/SharpDicom.Tests/Generators/GeneratorSnapshot.cs
new file mode 100644
index 00000000..3fe2c461
--- /dev/null
+++ b/tests/SharpDicom.Tests/Generators/GeneratorSnapshot.cs
@@ -0,0 +1,107 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using NUnit.Framework;
+
+namespace SharpDicom.Tests.Generators;
+
+///
+/// Snapshot assertions for source generator output.
+///
+///
+/// Each generated source is compared against
+/// {TestClass}.{TestMethod}#{HintName}.verified.cs next to the calling test file.
+/// On mismatch a .received.cs file is written alongside for review; accept a change
+/// by renaming it to .verified.cs.
+///
+internal static class GeneratorSnapshot
+{
+ private static readonly UTF8Encoding Utf8WithBom = new(encoderShouldEmitUTF8Identifier: true);
+
+ ///
+ /// Asserts that the generator run produced no diagnostics or exceptions and that
+ /// its output matches the verified snapshots for the current test.
+ ///
+ /// The generator driver after RunGenerators.
+ /// Path of the calling test file (supplied by the compiler).
+ public static void Verify(GeneratorDriver driver, [CallerFilePath] string callerFilePath = "")
+ {
+ var runResult = driver.GetRunResult();
+ var exceptions = runResult.Results.Where(r => r.Exception is not null).Select(r => r.Exception!.ToString());
+ Assert.That(exceptions, Is.Empty, "Generator threw an exception");
+ Assert.That(runResult.Diagnostics.Select(d => d.ToString()), Is.Empty, "Generator reported diagnostics");
+
+ var directory = Path.GetDirectoryName(ResolveSourcePath(callerFilePath))!;
+ var test = TestContext.CurrentContext.Test;
+ var className = test.ClassName!.Substring(test.ClassName.LastIndexOf('.') + 1);
+ var prefix = $"{className}.{test.MethodName}#";
+
+ var failures = new List();
+ var expectedFiles = new HashSet(StringComparer.Ordinal);
+
+ foreach (var generated in runResult.Results.SelectMany(r => r.GeneratedSources))
+ {
+ var baseName = prefix + Path.GetFileNameWithoutExtension(generated.HintName);
+ var verifiedPath = Path.Combine(directory, baseName + ".verified.cs");
+ var receivedPath = Path.Combine(directory, baseName + ".received.cs");
+ expectedFiles.Add(Path.GetFileName(verifiedPath));
+
+ var actual = Normalize($"//HintName: {generated.HintName}\n{generated.SourceText}");
+ var expected = File.Exists(verifiedPath) ? Normalize(File.ReadAllText(verifiedPath)) : null;
+
+ if (actual == expected)
+ {
+ if (File.Exists(receivedPath))
+ {
+ File.Delete(receivedPath);
+ }
+
+ continue;
+ }
+
+ File.WriteAllText(receivedPath, actual + "\n", Utf8WithBom);
+ failures.Add(expected is null
+ ? $"Missing snapshot {Path.GetFileName(verifiedPath)}; wrote {Path.GetFileName(receivedPath)}"
+ : $"Snapshot mismatch for {Path.GetFileName(verifiedPath)}; wrote {Path.GetFileName(receivedPath)}");
+ }
+
+ foreach (var stale in Directory.GetFiles(directory, prefix + "*.verified.cs"))
+ {
+ if (!expectedFiles.Contains(Path.GetFileName(stale)))
+ {
+ failures.Add($"Snapshot {Path.GetFileName(stale)} has no corresponding generated source");
+ }
+ }
+
+ Assert.That(failures, Is.Empty, "Generator output does not match snapshots");
+ }
+
+ ///
+ /// Maps a compiler-recorded source path back to the local file system. Deterministic
+ /// CI builds record source paths under /_/ rather than the real repository root.
+ ///
+ private static string ResolveSourcePath(string callerFilePath)
+ {
+ const string MappedRoot = "/_/";
+
+ if (File.Exists(callerFilePath) || !callerFilePath.StartsWith(MappedRoot, StringComparison.Ordinal))
+ {
+ return callerFilePath;
+ }
+
+ var repositoryRoot = typeof(GeneratorSnapshot).Assembly
+ .GetCustomAttributes()
+ .Single(a => a.Key == "RepositoryRoot")
+ .Value!;
+
+ return Path.Combine(repositoryRoot, callerFilePath.Substring(MappedRoot.Length));
+ }
+
+ private static string Normalize(string text) =>
+ text.TrimStart('').Replace("\r\n", "\n").TrimEnd();
+}
diff --git a/tests/SharpDicom.Tests/Generators/Part15ParserTests.cs b/tests/SharpDicom.Tests/Generators/Part15ParserTests.cs
index b6766bae..9c0e47ad 100644
--- a/tests/SharpDicom.Tests/Generators/Part15ParserTests.cs
+++ b/tests/SharpDicom.Tests/Generators/Part15ParserTests.cs
@@ -1,10 +1,8 @@
using System.Collections.Immutable;
-using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using NUnit.Framework;
using SharpDicom.Generators;
-using VerifyNUnit;
namespace SharpDicom.Tests.Generators;
@@ -15,7 +13,7 @@ namespace SharpDicom.Tests.Generators;
public class Part15ParserTests
{
[Test]
- public Task ParsesBasicProfileActions()
+ public void ParsesBasicProfileActions()
{
// Table E.1-1 structure with common attributes
var xml = """
@@ -67,11 +65,11 @@ public Task ParsesBasicProfileActions()
""";
- return VerifyGenerator(xml);
+ VerifyGenerator(xml);
}
[Test]
- public Task ParsesPatientName()
+ public void ParsesPatientName()
{
// Patient Name with Z action
var xml = """
@@ -104,11 +102,11 @@ public Task ParsesPatientName()
""";
- return VerifyGenerator(xml);
+ VerifyGenerator(xml);
}
[Test]
- public Task ParsesSOPInstanceUID()
+ public void ParsesSOPInstanceUID()
{
// SOP Instance UID with U action and RetainUIDs option
var xml = """
@@ -141,11 +139,11 @@ public Task ParsesSOPInstanceUID()
""";
- return VerifyGenerator(xml);
+ VerifyGenerator(xml);
}
[Test]
- public Task ParsesRetiredAttribute()
+ public void ParsesRetiredAttribute()
{
// Retired attribute (Acquisition Comments)
var xml = """
@@ -178,11 +176,11 @@ public Task ParsesRetiredAttribute()
""";
- return VerifyGenerator(xml);
+ VerifyGenerator(xml);
}
[Test]
- public Task ParsesCompoundActions()
+ public void ParsesCompoundActions()
{
// Compound action X/Z and X/Z/D
var xml = """
@@ -232,11 +230,11 @@ public Task ParsesCompoundActions()
""";
- return VerifyGenerator(xml);
+ VerifyGenerator(xml);
}
[Test]
- public Task ParsesAllProfileOptions()
+ public void ParsesAllProfileOptions()
{
// Institution Name with multiple profile options
var xml = """
@@ -269,10 +267,10 @@ public Task ParsesAllProfileOptions()
""";
- return VerifyGenerator(xml);
+ VerifyGenerator(xml);
}
- private static Task VerifyGenerator(string xmlContent)
+ private static void VerifyGenerator(string xmlContent)
{
// Create compilation
#pragma warning disable IL3000 // Required for source generator tests
@@ -297,6 +295,6 @@ private static Task VerifyGenerator(string xmlContent)
driver = driver.RunGenerators(compilation);
// Verify snapshot
- return Verifier.Verify(driver);
+ GeneratorSnapshot.Verify(driver);
}
}
diff --git a/tests/SharpDicom.Tests/ModuleInitializer.cs b/tests/SharpDicom.Tests/ModuleInitializer.cs
deleted file mode 100644
index e0404289..00000000
--- a/tests/SharpDicom.Tests/ModuleInitializer.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Runtime.CompilerServices;
-using VerifyTests;
-
-namespace SharpDicom.Tests;
-
-///
-/// Module initializer for Verify.SourceGenerators setup.
-///
-public static class ModuleInitializer
-{
- ///
- /// Initializes Verify.SourceGenerators settings.
- ///
- [ModuleInitializer]
- public static void Initialize()
- {
- VerifySourceGenerators.Initialize();
- }
-}
diff --git a/tests/SharpDicom.Tests/SharpDicom.Tests.csproj b/tests/SharpDicom.Tests/SharpDicom.Tests.csproj
index 877f654d..708f117c 100644
--- a/tests/SharpDicom.Tests/SharpDicom.Tests.csproj
+++ b/tests/SharpDicom.Tests/SharpDicom.Tests.csproj
@@ -17,8 +17,6 @@
-
-
@@ -28,4 +26,14 @@
+
+
+
+
+
+
+
+
+
+