From 8832d5a7a31912582c226b2ab660c6add357bba3 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Sat, 27 Jun 2026 16:12:52 +0200 Subject: [PATCH 1/6] refactor --- .../Reporting/Reporters/Console/ConsoleReporter.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ModVerify/Reporting/Reporters/Console/ConsoleReporter.cs b/src/ModVerify/Reporting/Reporters/Console/ConsoleReporter.cs index 79892f70..f4a1a1ac 100644 --- a/src/ModVerify/Reporting/Reporters/Console/ConsoleReporter.cs +++ b/src/ModVerify/Reporting/Reporters/Console/ConsoleReporter.cs @@ -78,11 +78,12 @@ private void PrintResolvedStats(VerificationResult verificationResult) $"Reduced issues: {resolvedCount} error(s) present in the baseline are no longer reported."); Console.ResetColor(); - if (Settings.Verbose #if DEBUG - || true + const bool debugBuild = true; +#else + const bool debugBuild = false; #endif - ) + if (Settings.Verbose || debugBuild) { foreach (var baseline in resolvedErrors) { From a489399e2f4b0fb1678bfb3b6a81a84416bca27e Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 6 Jul 2026 10:48:19 +0200 Subject: [PATCH 2/6] add reporting documentation --- .../Reporting/Baseline/BaselineCollection.cs | 28 ++++++++++++++ .../Reporting/Baseline/BaselineSource.cs | 3 ++ .../Baseline/BaselineVerificationTarget.cs | 21 +++++++++-- .../Reporting/Baseline/IdentifiedBaseline.cs | 10 +++++ .../Baseline/InvalidBaselineException.cs | 3 +- .../Baseline/VerificationBaseline.cs | 37 ++++++++++++++++++- .../Engine/EngineErrorReporterBase.cs | 2 +- .../Engine/GameEngineErrorCollection.cs | 12 ++++-- .../Engine/IGameEngineErrorCollection.cs | 8 +++- .../Console/ConsoleReporterSettings.cs | 8 +++- .../Reporting/Reporters/ExtensionMethods.cs | 25 ++++++++++++- .../Reporting/Reporters/FileBasedReporter.cs | 11 +++++- .../Reporters/FileBasedReporterSettings.cs | 6 ++- .../Reporters/IVerificationReporter.cs | 8 +++- .../Reporters/JSON/JsonReporterSettings.cs | 9 ++++- .../Reporting/Reporters/ReporterBase.cs | 13 ++++++- .../Reporting/Reporters/ReporterSettings.cs | 11 +++++- .../Text/TextFileReporterSettings.cs | 8 +++- .../Reporters/VerificationReportBroker.cs | 11 +++++- .../Suppressions/SuppressionFilter.cs | 23 +++++++++++- .../Reporting/Suppressions/SuppressionList.cs | 21 ++++++++++- 21 files changed, 251 insertions(+), 27 deletions(-) diff --git a/src/ModVerify/Reporting/Baseline/BaselineCollection.cs b/src/ModVerify/Reporting/Baseline/BaselineCollection.cs index 6e9dda00..b3293fa8 100644 --- a/src/ModVerify/Reporting/Baseline/BaselineCollection.cs +++ b/src/ModVerify/Reporting/Baseline/BaselineCollection.cs @@ -7,16 +7,25 @@ namespace AET.ModVerify.Reporting.Baseline; +/// Represents a set of identified verification baselines, each distinguished by a unique identifier. public sealed class BaselineCollection : IReadOnlyCollection { + /// Gets an empty that contains no baselines. public static readonly BaselineCollection Empty = new([]); private readonly IReadOnlyList _baselines; + /// public int Count => _baselines.Count; + /// Gets a value that indicates whether the collection contains no baselines. + /// if the collection contains no baselines; otherwise, . public bool IsEmpty => _baselines.Count == 0; + /// Initializes a new instance of the class with the specified baselines. + /// The identified baselines to include in the collection. + /// is . + /// An entry in is , or two entries share the same identifier. public BaselineCollection(IEnumerable baselines) { if (baselines is null) @@ -35,6 +44,9 @@ public BaselineCollection(IEnumerable baselines) _baselines = list; } + /// Determines whether any baseline in the collection contains the specified error. + /// The verification error to locate. + /// if a baseline in the collection contains ; otherwise, . public bool Contains(VerificationError error) { foreach (var entry in _baselines) @@ -45,6 +57,10 @@ public bool Contains(VerificationError error) return false; } + /// Gets the identifier of the first baseline in the collection that contains the specified error. + /// The verification error to locate. + /// When this method returns, contains the identifier of the matching baseline if a match was found, or if no baseline contains the error. This parameter is treated as uninitialized. + /// if a baseline containing was found; otherwise, . public bool TryGetMatchingBaseline(VerificationError error, [NotNullWhen(true)] out string? identifier) { foreach (var entry in _baselines) @@ -59,6 +75,10 @@ public bool TryGetMatchingBaseline(VerificationError error, [NotNullWhen(true)] return false; } + /// Filters out the errors that are contained in any baseline of the collection. + /// The errors to filter. + /// The errors that are not contained in any baseline of the collection. + /// is . public IEnumerable Apply(IEnumerable errors) { if (errors is null) @@ -68,6 +88,13 @@ public IEnumerable Apply(IEnumerable error return errors.Where(e => !Contains(e)); } + /// Categorizes the specified errors into new, persistent, and resolved errors relative to the baselines in the collection. + /// The errors found during verification. + /// + /// The errors grouped into those not present in any baseline, those matching a baseline, and those present in a + /// baseline but not found again during verification. + /// + /// is . public CategorizedVerificationErrors Categorize(IEnumerable errors) { if (errors is null) @@ -102,6 +129,7 @@ public CategorizedVerificationErrors Categorize(IEnumerable e new ReadOnlyValueListDictionary(resolved)); } + /// public IEnumerator GetEnumerator() { return _baselines.GetEnumerator(); diff --git a/src/ModVerify/Reporting/Baseline/BaselineSource.cs b/src/ModVerify/Reporting/Baseline/BaselineSource.cs index e018e418..7442927a 100644 --- a/src/ModVerify/Reporting/Baseline/BaselineSource.cs +++ b/src/ModVerify/Reporting/Baseline/BaselineSource.cs @@ -1,7 +1,10 @@ namespace AET.ModVerify.Reporting.Baseline; +/// Specifies the origin of a verification baseline. public enum BaselineSource { + /// The baseline was loaded from a file on disk. File, + /// The baseline is the default baseline embedded in the application. EmbeddedDefault, } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Baseline/BaselineVerificationTarget.cs b/src/ModVerify/Reporting/Baseline/BaselineVerificationTarget.cs index ca77fa85..dde29b5a 100644 --- a/src/ModVerify/Reporting/Baseline/BaselineVerificationTarget.cs +++ b/src/ModVerify/Reporting/Baseline/BaselineVerificationTarget.cs @@ -1,12 +1,27 @@ -using PG.StarWarsGame.Engine; +using PG.StarWarsGame.Engine; namespace AET.ModVerify.Reporting.Baseline; +/// Represents the target that a verification baseline was created for. public sealed record BaselineVerificationTarget -{ +{ + /// Gets or sets the game engine type of the target. public required GameEngineType Engine { get; init; } + + /// Gets or sets the name of the target. public required string Name { get; init; } - public GameLocations? Location { get; init; } // Optional compared to Verification Target + + /// Gets or sets the game locations of the target, or if not specified. + /// The location is optional for a baseline target, unlike for a verification target. + public GameLocations? Location { get; init; } + + /// Gets or sets the version of the target, or if not specified. public string? Version { get; init; } + + /// Gets or sets a value that indicates whether the target is the base game rather than a mod. + /// + /// if the target is the base game; otherwise, . + /// The default is . + /// public bool IsGame { get; init; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Baseline/IdentifiedBaseline.cs b/src/ModVerify/Reporting/Baseline/IdentifiedBaseline.cs index 7726d84c..97c34b10 100644 --- a/src/ModVerify/Reporting/Baseline/IdentifiedBaseline.cs +++ b/src/ModVerify/Reporting/Baseline/IdentifiedBaseline.cs @@ -2,14 +2,24 @@ namespace AET.ModVerify.Reporting.Baseline; +/// Associates a verification baseline with a unique identifier and the source it was loaded from. public sealed record IdentifiedBaseline { + /// Gets the unique identifier of the baseline within its collection. public string Identifier { get; } + /// Gets the verification baseline. public VerificationBaseline Baseline { get; } + /// Gets the source the baseline was loaded from. public BaselineSource Source { get; } + /// Initializes a new instance of the class. + /// The unique identifier of the baseline within its collection. + /// The verification baseline. + /// One of the enumeration values that specifies the source the baseline was loaded from. + /// is or empty. + /// is . public IdentifiedBaseline(string identifier, VerificationBaseline baseline, BaselineSource source) { if (string.IsNullOrEmpty(identifier)) diff --git a/src/ModVerify/Reporting/Baseline/InvalidBaselineException.cs b/src/ModVerify/Reporting/Baseline/InvalidBaselineException.cs index 58e180e4..79e0ddeb 100644 --- a/src/ModVerify/Reporting/Baseline/InvalidBaselineException.cs +++ b/src/ModVerify/Reporting/Baseline/InvalidBaselineException.cs @@ -1,7 +1,8 @@ -using System; +using System; namespace AET.ModVerify.Reporting.Baseline; +/// The exception that is thrown when a verification baseline cannot be parsed or is otherwise invalid. public sealed class InvalidBaselineException : Exception { internal InvalidBaselineException(string message) : base(message) diff --git a/src/ModVerify/Reporting/Baseline/VerificationBaseline.cs b/src/ModVerify/Reporting/Baseline/VerificationBaseline.cs index 80f95b8c..1cd26d0e 100644 --- a/src/ModVerify/Reporting/Baseline/VerificationBaseline.cs +++ b/src/ModVerify/Reporting/Baseline/VerificationBaseline.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.IO; @@ -11,24 +11,34 @@ namespace AET.ModVerify.Reporting.Baseline; +/// Represents a frozen set of verification errors that are already known and should be ignored during verification. public sealed class VerificationBaseline : IReadOnlyCollection { + /// Gets the latest supported baseline format version. public static readonly Version LatestVersion = new(2, 2); + + /// Gets the latest supported baseline format version as a string with two components. public static readonly string LatestVersionString = LatestVersion.ToString(2); + /// Gets an empty that contains no errors. public static readonly VerificationBaseline Empty = new(VerificationSeverity.Information, [], null); private readonly HashSet _errors; + /// Gets the target that this baseline was created for, or if not specified. public BaselineVerificationTarget? Target { get; } - + + /// Gets the format version of this baseline, or if unknown. public Version? Version { get; } + /// Gets the minimum severity of the errors recorded in this baseline. public VerificationSeverity MinimumSeverity { get; } /// public int Count => _errors.Count; + /// Gets a value that indicates whether the baseline contains no errors. + /// if the baseline contains no errors; otherwise, . public bool IsEmpty => Count == 0; internal VerificationBaseline(JsonVerificationBaseline baseline) @@ -39,33 +49,55 @@ internal VerificationBaseline(JsonVerificationBaseline baseline) Target = JsonVerificationTarget.ToTarget(baseline.Target); } + /// Initializes a new instance of the class. + /// One of the enumeration values that specifies the minimum severity of the errors recorded in the baseline. + /// The errors to record in the baseline. + /// The target the baseline was created for, or if not specified. + /// is . public VerificationBaseline(VerificationSeverity minimumSeverity, IEnumerable errors, BaselineVerificationTarget? target) { + if (errors == null) throw new ArgumentNullException(nameof(errors)); _errors = [..errors]; Version = LatestVersion; MinimumSeverity = minimumSeverity; Target = target; } + /// Determines whether the baseline contains the specified error. + /// The verification error to locate. + /// if the baseline contains ; otherwise, . public bool Contains(VerificationError error) { return _errors.Contains(error); } + /// Filters out the errors that are contained in the baseline. + /// The errors to filter. + /// The errors that are not contained in the baseline. public IEnumerable Apply(IEnumerable errors) { return Count == 0 ? errors : errors.Where(e => !_errors.Contains(e)); } + /// Serializes the baseline as JSON to the specified stream. + /// The stream to write the JSON representation to. public void ToJson(Stream stream) { JsonSerializer.Serialize(stream, new JsonVerificationBaseline(this), ModVerifyJsonSettings.JsonSettings); } + + /// Asynchronously serializes the baseline as JSON to the specified stream. + /// The stream to write the JSON representation to. + /// A task that represents the asynchronous serialization operation. public Task ToJsonAsync(Stream stream) { return JsonSerializer.SerializeAsync(stream, new JsonVerificationBaseline(this), ModVerifyJsonSettings.JsonSettings); } + /// Deserializes a baseline from its JSON representation. + /// The stream to read the JSON representation from. + /// The deserialized baseline. + /// The JSON representation is invalid or cannot be parsed. public static VerificationBaseline FromJson(Stream stream) { return JsonBaselineParser.Parse(stream); @@ -82,6 +114,7 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } + /// public override string ToString() { var sb = new StringBuilder($"Baseline [Version={Version}, MinSeverity={MinimumSeverity}, NumErrors={Count}"); diff --git a/src/ModVerify/Reporting/Engine/EngineErrorReporterBase.cs b/src/ModVerify/Reporting/Engine/EngineErrorReporterBase.cs index f8c7cd28..f99be722 100644 --- a/src/ModVerify/Reporting/Engine/EngineErrorReporterBase.cs +++ b/src/ModVerify/Reporting/Engine/EngineErrorReporterBase.cs @@ -15,7 +15,7 @@ internal abstract class EngineErrorReporterBase : IGameVerifierInfo public IReadOnlyList VerifierChain { get; } - public string Name => GetType().FullName; + public string Name => GetType().FullName!; public abstract string FriendlyName { get; } diff --git a/src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs b/src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs index 8cb67c06..251debcb 100644 --- a/src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs +++ b/src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs @@ -1,4 +1,4 @@ -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using PG.StarWarsGame.Engine.ErrorReporting; @@ -6,16 +6,20 @@ namespace AET.ModVerify.Reporting.Engine; +/// Collects the XML, initialization, and assertion errors reported by the game engine during verification. public sealed class GameEngineErrorCollection : IGameEngineErrorCollection, IGameEngineErrorReporter { - private readonly ConcurrentBag _xmlErrors = new(); - private readonly ConcurrentBag _initializationErrors = new(); - private readonly ConcurrentBag _asserts = new(); + private readonly ConcurrentBag _xmlErrors = []; + private readonly ConcurrentBag _initializationErrors = []; + private readonly ConcurrentBag _asserts = []; + /// public IEnumerable XmlErrors => _xmlErrors.ToList(); + /// public IEnumerable InitializationErrors => _initializationErrors.ToList(); + /// public IEnumerable Asserts => _asserts.ToList(); void IXmlParserErrorReporter.Report(XmlError error) diff --git a/src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs b/src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs index 286d047d..7c1d8b5e 100644 --- a/src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs +++ b/src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs @@ -1,12 +1,18 @@ -using System.Collections.Generic; +using System.Collections.Generic; using PG.StarWarsGame.Engine.ErrorReporting; using PG.StarWarsGame.Files.XML.ErrorHandling; namespace AET.ModVerify.Reporting.Engine; +/// Provides the errors and assertions collected from the game engine during verification. public interface IGameEngineErrorCollection { + /// Gets the XML parser errors reported by the engine. IEnumerable XmlErrors { get; } + + /// Gets the initialization errors reported by the engine. IEnumerable InitializationErrors { get; } + + /// Gets the assertions raised by the engine. IEnumerable Asserts { get; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/Console/ConsoleReporterSettings.cs b/src/ModVerify/Reporting/Reporters/Console/ConsoleReporterSettings.cs index 4dc2a774..156c3390 100644 --- a/src/ModVerify/Reporting/Reporters/Console/ConsoleReporterSettings.cs +++ b/src/ModVerify/Reporting/Reporters/Console/ConsoleReporterSettings.cs @@ -1,6 +1,12 @@ -namespace AET.ModVerify.Reporting.Reporters; +namespace AET.ModVerify.Reporting.Reporters; +/// Provides the settings for the console verification reporter. public sealed record ConsoleReporterSettings : ReporterSettings { + /// Gets or sets a value that indicates whether only a summary is written to the console instead of individual findings. + /// + /// to write only a summary; otherwise, to write individual findings. + /// The default is . + /// public bool SummaryOnly { get; init; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/ExtensionMethods.cs b/src/ModVerify/Reporting/Reporters/ExtensionMethods.cs index 480e956a..27c8ded3 100644 --- a/src/ModVerify/Reporting/Reporters/ExtensionMethods.cs +++ b/src/ModVerify/Reporting/Reporters/ExtensionMethods.cs @@ -1,31 +1,50 @@ -using System; +using System; namespace AET.ModVerify.Reporting.Reporters; +/// Provides factory methods for creating verification reporters. public static class ExtensionMethods { extension(IVerificationReporter) { + /// Creates a JSON reporter with default settings. + /// The service provider used to resolve dependencies. + /// A new JSON reporter. public static IVerificationReporter CreateJson(IServiceProvider serviceProvider) { return IVerificationReporter.CreateJson(new JsonReporterSettings(), serviceProvider); } + /// Creates a JSON reporter with the specified settings. + /// The settings that control the reporter's behavior. + /// The service provider used to resolve dependencies. + /// A new JSON reporter. public static IVerificationReporter CreateJson(JsonReporterSettings settings, IServiceProvider serviceProvider) { return new JsonReporter(settings, serviceProvider); } + /// Creates a text-file reporter with default settings. + /// The service provider used to resolve dependencies. + /// A new text-file reporter. public static IVerificationReporter CreateText(IServiceProvider serviceProvider) { return IVerificationReporter.CreateText(new TextFileReporterSettings(), serviceProvider); } + /// Creates a text-file reporter with the specified settings. + /// The settings that control the reporter's behavior. + /// The service provider used to resolve dependencies. + /// A new text-file reporter. public static IVerificationReporter CreateText(TextFileReporterSettings settings, IServiceProvider serviceProvider) { return new TextFileReporter(settings, serviceProvider); } + /// Creates a console reporter that reports findings with a severity of or higher. + /// The service provider used to resolve dependencies. + /// to write only a summary; otherwise, to write individual findings. + /// A new console reporter. public static IVerificationReporter CreateConsole(IServiceProvider serviceProvider, bool summaryOnly = false) { var settings = new ConsoleReporterSettings @@ -36,6 +55,10 @@ public static IVerificationReporter CreateConsole(IServiceProvider serviceProvid return IVerificationReporter.CreateConsole(settings, serviceProvider); } + /// Creates a console reporter with the specified settings. + /// The settings that control the reporter's behavior. + /// The service provider used to resolve dependencies. + /// A new console reporter. public static IVerificationReporter CreateConsole(ConsoleReporterSettings settings, IServiceProvider serviceProvider) { return new ConsoleReporter(settings, serviceProvider); diff --git a/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs b/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs index 4c60121a..026b4d1e 100644 --- a/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs +++ b/src/ModVerify/Reporting/Reporters/FileBasedReporter.cs @@ -1,15 +1,22 @@ -using System; +using System; using System.IO; using System.IO.Abstractions; using Microsoft.Extensions.DependencyInjection; namespace AET.ModVerify.Reporting.Reporters; -public abstract class FileBasedReporter(T settings, IServiceProvider serviceProvider) +/// Provides a base class for verification reporters that write their output to files. +/// The type of settings used by the reporter. +/// The settings that control the reporter's behavior. +/// The service provider used to resolve dependencies. +public abstract class FileBasedReporter(T settings, IServiceProvider serviceProvider) : ReporterBase(settings, serviceProvider) where T : FileBasedReporterSettings { private readonly IFileSystem _fileSystem = serviceProvider.GetRequiredService(); + /// Creates a writable file stream for the specified file name in the configured output directory. + /// The name of the file to create. + /// A writable stream for the newly created file. protected Stream CreateFile(string fileName) { var outputDirectory = Settings.OutputDirectory; diff --git a/src/ModVerify/Reporting/Reporters/FileBasedReporterSettings.cs b/src/ModVerify/Reporting/Reporters/FileBasedReporterSettings.cs index aa468fcc..d2da5335 100644 --- a/src/ModVerify/Reporting/Reporters/FileBasedReporterSettings.cs +++ b/src/ModVerify/Reporting/Reporters/FileBasedReporterSettings.cs @@ -1,9 +1,13 @@ -using System; +using System; namespace AET.ModVerify.Reporting.Reporters; +/// Provides the settings shared by file-based verification reporters. public record FileBasedReporterSettings : ReporterSettings { + /// Gets or sets the directory that report files are written to. + /// The directory that report files are written to. The default is the current working directory. + /// Setting the value to or an empty string resets it to the current working directory. public string OutputDirectory { get; diff --git a/src/ModVerify/Reporting/Reporters/IVerificationReporter.cs b/src/ModVerify/Reporting/Reporters/IVerificationReporter.cs index 5ce7a650..660f4394 100644 --- a/src/ModVerify/Reporting/Reporters/IVerificationReporter.cs +++ b/src/ModVerify/Reporting/Reporters/IVerificationReporter.cs @@ -1,8 +1,12 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; namespace AET.ModVerify.Reporting.Reporters; -public interface IVerificationReporter +/// Defines a reporter that writes a verification result to an output sink. +public interface IVerificationReporter { + /// Asynchronously reports the specified verification result. + /// The verification result to report. + /// A task that represents the asynchronous report operation. public Task ReportAsync(VerificationResult verificationResult); } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/JSON/JsonReporterSettings.cs b/src/ModVerify/Reporting/Reporters/JSON/JsonReporterSettings.cs index a2503ed5..08bf836b 100644 --- a/src/ModVerify/Reporting/Reporters/JSON/JsonReporterSettings.cs +++ b/src/ModVerify/Reporting/Reporters/JSON/JsonReporterSettings.cs @@ -1,5 +1,12 @@ -namespace AET.ModVerify.Reporting.Reporters; +namespace AET.ModVerify.Reporting.Reporters; + +/// Provides the settings for the JSON verification reporter. public record JsonReporterSettings : FileBasedReporterSettings { + /// Gets or sets a value that indicates whether errors are aggregated in the JSON report. + /// + /// if errors are aggregated; otherwise, . + /// The default is . + /// public bool AggregateResults { get; init; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/ReporterBase.cs b/src/ModVerify/Reporting/Reporters/ReporterBase.cs index 47cafc5e..fdfb6bf3 100644 --- a/src/ModVerify/Reporting/Reporters/ReporterBase.cs +++ b/src/ModVerify/Reporting/Reporters/ReporterBase.cs @@ -1,18 +1,29 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AET.ModVerify.Reporting.Reporters; +/// Provides a base class for verification reporters that share settings and access to services. +/// The type of settings used by the reporter. +/// The settings that control the reporter's behavior. +/// The service provider used to resolve dependencies. +/// or is . public abstract class ReporterBase(T settings, IServiceProvider serviceProvider) : IVerificationReporter where T : ReporterSettings { + /// Gets the service provider used to resolve dependencies. protected IServiceProvider ServiceProvider { get; } = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + /// Gets the settings that control the reporter's behavior. protected T Settings { get; } = settings ?? throw new ArgumentNullException(nameof(settings)); + /// public abstract Task ReportAsync(VerificationResult verificationResult); + /// Filters the specified errors to those meeting the configured minimum report severity. + /// The errors to filter. + /// The errors whose severity is at least . protected IEnumerable FilteredErrors(IReadOnlyCollection errors) { return errors.Where(x => x.Severity >= Settings.MinimumReportSeverity); diff --git a/src/ModVerify/Reporting/Reporters/ReporterSettings.cs b/src/ModVerify/Reporting/Reporters/ReporterSettings.cs index 6cd56d3d..633c446f 100644 --- a/src/ModVerify/Reporting/Reporters/ReporterSettings.cs +++ b/src/ModVerify/Reporting/Reporters/ReporterSettings.cs @@ -1,7 +1,16 @@ -namespace AET.ModVerify.Reporting.Reporters; +namespace AET.ModVerify.Reporting.Reporters; +/// Provides the base settings shared by all verification reporters. public record ReporterSettings { + /// Gets or sets the minimum severity a finding must have to be reported. + /// The minimum severity of reported findings. The default is . public VerificationSeverity MinimumReportSeverity { get; init; } = VerificationSeverity.Information; + + /// Gets or sets a value that indicates whether the reporter emits verbose output. + /// + /// if the reporter emits verbose output; otherwise, . + /// The default is . + /// public bool Verbose { get; init; } } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/Text/TextFileReporterSettings.cs b/src/ModVerify/Reporting/Reporters/Text/TextFileReporterSettings.cs index 2d0e3488..c81b8cdd 100644 --- a/src/ModVerify/Reporting/Reporters/Text/TextFileReporterSettings.cs +++ b/src/ModVerify/Reporting/Reporters/Text/TextFileReporterSettings.cs @@ -1,6 +1,12 @@ -namespace AET.ModVerify.Reporting.Reporters; +namespace AET.ModVerify.Reporting.Reporters; +/// Provides the settings for the text-file verification reporter. public sealed record TextFileReporterSettings : FileBasedReporterSettings { + /// Gets or sets a value that indicates whether findings are split across multiple files instead of written to a single file. + /// + /// to split findings across multiple files; otherwise, to write a single file. + /// The default is . + /// public bool SplitIntoFiles { get; init; } = true; } \ No newline at end of file diff --git a/src/ModVerify/Reporting/Reporters/VerificationReportBroker.cs b/src/ModVerify/Reporting/Reporters/VerificationReportBroker.cs index 0ea6630b..6c16cdc9 100644 --- a/src/ModVerify/Reporting/Reporters/VerificationReportBroker.cs +++ b/src/ModVerify/Reporting/Reporters/VerificationReportBroker.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -6,11 +6,16 @@ namespace AET.ModVerify.Reporting.Reporters; +/// Distributes a verification result to multiple reporters, isolating the failure of any individual reporter. public sealed class VerificationReportBroker : IVerificationReporter { private readonly ILogger? _logger; private readonly IReadOnlyCollection _reporters; + /// Initializes a new instance of the class. + /// The reporters to distribute the verification result to. + /// The service provider used to resolve a logger. + /// is . public VerificationReportBroker( IReadOnlyCollection reporters, IServiceProvider serviceProvider) @@ -19,6 +24,8 @@ public VerificationReportBroker( _logger = serviceProvider.GetService()?.CreateLogger(typeof(VerificationReportBroker)); } + /// + /// An exception thrown by an individual reporter is logged and does not prevent the remaining reporters from running. public async Task ReportAsync(VerificationResult result) { foreach (var reporter in _reporters) @@ -29,7 +36,7 @@ public async Task ReportAsync(VerificationResult result) } catch (Exception e) { - _logger?.LogError(e, "Exception while reporting verification error"); + _logger?.LogError(e, "Exception while reporting verification error. Reporter: {Reporter}", reporter.GetType().FullName); } } } diff --git a/src/ModVerify/Reporting/Suppressions/SuppressionFilter.cs b/src/ModVerify/Reporting/Suppressions/SuppressionFilter.cs index 35a21d7a..572933d9 100644 --- a/src/ModVerify/Reporting/Suppressions/SuppressionFilter.cs +++ b/src/ModVerify/Reporting/Suppressions/SuppressionFilter.cs @@ -1,19 +1,29 @@ -using System; +using System; using System.Linq; using AET.ModVerify.Reporting.Suppressions.Json; namespace AET.ModVerify.Reporting.Suppressions; +/// Represents a filter that suppresses verification errors matching a combination of error identifier, verifier, and asset. public sealed class SuppressionFilter : IEquatable { + /// Gets the error identifier to match, or to match any identifier. public string? Id { get; } + /// Gets the verifier name to match, or to match any verifier. public string? Verifier { get; } + /// Gets the asset to match, or to match any asset. public string? Asset { get; } + /// Gets a value that indicates whether the filter is disabled because no matching criterion is set. + /// if none of , , and is set; otherwise, . public bool IsDisabled => Id == null && Verifier == null && Asset == null; + /// Initializes a new instance of the class. + /// The error identifier to match, or to match any identifier. + /// The verifier name to match, or to match any verifier. + /// The asset to match, or to match any asset. public SuppressionFilter(string? id, string? verifier, string? asset) { Id = id; @@ -28,6 +38,9 @@ internal SuppressionFilter(JsonSuppressionFilter filter) Asset = filter.Asset; } + /// Determines whether this filter suppresses the specified error. + /// The verification error to test. + /// if the filter suppresses ; otherwise, . public bool Suppresses(VerificationError error) { var suppresses = false; @@ -59,6 +72,8 @@ public bool Suppresses(VerificationError error) return suppresses; } + /// Gets the number of criteria that this filter matches on. + /// The number of non-null criteria, between 0 and 3. public int Specificity() { var specificity = 0; @@ -71,6 +86,9 @@ public int Specificity() return specificity; } + /// Determines whether this filter is made redundant by another, less specific filter. + /// The filter to compare against. + /// if is a broader filter that makes this filter redundant; otherwise, . public bool IsSupersededBy(SuppressionFilter other) { if (Id != null && other.Id != null && other.Id != Id) @@ -85,6 +103,7 @@ public bool IsSupersededBy(SuppressionFilter other) return other.Specificity() < Specificity(); } + /// public bool Equals(SuppressionFilter? other) { if (ReferenceEquals(null, other)) @@ -99,11 +118,13 @@ public bool Equals(SuppressionFilter? other) return Asset == other.Asset; } + /// public override bool Equals(object? obj) { return obj is SuppressionFilter other && Equals(other); } + /// public override int GetHashCode() { var hashCode = new HashCode(); diff --git a/src/ModVerify/Reporting/Suppressions/SuppressionList.cs b/src/ModVerify/Reporting/Suppressions/SuppressionList.cs index a5fc8f9a..f029a77d 100644 --- a/src/ModVerify/Reporting/Suppressions/SuppressionList.cs +++ b/src/ModVerify/Reporting/Suppressions/SuppressionList.cs @@ -1,4 +1,4 @@ -using AET.ModVerify.Reporting.Json; +using AET.ModVerify.Reporting.Json; using System; using System.Collections; using System.Collections.Generic; @@ -9,15 +9,21 @@ namespace AET.ModVerify.Reporting.Suppressions; +/// Represents a set of suppression filters that remove known verification errors from a result. public sealed class SuppressionList : IReadOnlyCollection { + /// Gets an empty that suppresses nothing. public static readonly SuppressionList Empty = new([]); private readonly IReadOnlyCollection _filters; private readonly IReadOnlyCollection _minimizedFilters; + /// public int Count => _filters.Count; + /// Initializes a new instance of the class with the specified filters. + /// The suppression filters to include in the list. + /// is . public SuppressionList(IEnumerable suppressionFilters) { if (suppressionFilters == null) @@ -36,11 +42,17 @@ internal SuppressionList(JsonSuppressionList suppressionList) _minimizedFilters = MinimizeSuppressions(_filters); } + /// Serializes the suppression list as JSON to the specified stream. + /// The stream to write the JSON representation to. public void ToJson(Stream stream) { JsonSerializer.Serialize(stream, new JsonSuppressionList(this), ModVerifyJsonSettings.JsonSettings); } + /// Deserializes a suppression list from its JSON representation. + /// The stream to read the JSON representation from. + /// The deserialized suppression list. + /// The JSON representation cannot be deserialized. public static SuppressionList FromJson(Stream stream) { var baselineJson = JsonSerializer.Deserialize(stream, JsonSerializerOptions.Default); @@ -49,11 +61,17 @@ public static SuppressionList FromJson(Stream stream) return new SuppressionList(baselineJson); } + /// Filters out the errors that are suppressed by any filter in the list. + /// The errors to filter. + /// The errors that are not suppressed by any filter in the list. public IEnumerable Apply(IEnumerable errors) { return Count == 0 ? errors : errors.Where(e => !Suppresses(e)); } + /// Determines whether any filter in the list suppresses the specified error. + /// The verification error to test. + /// if a filter in the list suppresses ; otherwise, . public bool Suppresses(VerificationError error) { foreach (var filter in _minimizedFilters) @@ -83,6 +101,7 @@ private static IReadOnlyCollection MinimizeSuppressions(IEnum return result; } + /// public IEnumerator GetEnumerator() { return _filters.GetEnumerator(); From df09ede71b06fc5dc39c1183cb5cf0e509842a1b Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Mon, 6 Jul 2026 12:49:43 +0200 Subject: [PATCH 3/6] docs: document public API of ModVerify.Reporting Add XML doc comments to the exposed (public) types in AET.ModVerify.Reporting following dotnet/runtime conventions: baselines, suppressions, engine error collection, and reporters. Internal JSON DTOs and concrete reporter implementations are left undocumented. --- .../Suppressions/SuppressionFilter.cs | 31 ----------- .../Reporting/Suppressions/SuppressionList.cs | 23 +------- .../Reporting/SuppressionListTest.cs | 52 +++++++++++++++++++ 3 files changed, 54 insertions(+), 52 deletions(-) create mode 100644 test/ModVerify.Test/Reporting/SuppressionListTest.cs diff --git a/src/ModVerify/Reporting/Suppressions/SuppressionFilter.cs b/src/ModVerify/Reporting/Suppressions/SuppressionFilter.cs index 572933d9..2bbfcc2d 100644 --- a/src/ModVerify/Reporting/Suppressions/SuppressionFilter.cs +++ b/src/ModVerify/Reporting/Suppressions/SuppressionFilter.cs @@ -72,37 +72,6 @@ public bool Suppresses(VerificationError error) return suppresses; } - /// Gets the number of criteria that this filter matches on. - /// The number of non-null criteria, between 0 and 3. - public int Specificity() - { - var specificity = 0; - if (Id != null) - specificity++; - if (Verifier != null) - specificity++; - if (Asset != null) - specificity++; - return specificity; - } - - /// Determines whether this filter is made redundant by another, less specific filter. - /// The filter to compare against. - /// if is a broader filter that makes this filter redundant; otherwise, . - public bool IsSupersededBy(SuppressionFilter other) - { - if (Id != null && other.Id != null && other.Id != Id) - return false; - - if (Verifier != null && other.Verifier != null && other.Verifier != Verifier) - return false; - - if (Asset != null && other.Asset != null) - return false; - - return other.Specificity() < Specificity(); - } - /// public bool Equals(SuppressionFilter? other) { diff --git a/src/ModVerify/Reporting/Suppressions/SuppressionList.cs b/src/ModVerify/Reporting/Suppressions/SuppressionList.cs index f029a77d..0d3588fb 100644 --- a/src/ModVerify/Reporting/Suppressions/SuppressionList.cs +++ b/src/ModVerify/Reporting/Suppressions/SuppressionList.cs @@ -16,7 +16,6 @@ public sealed class SuppressionList : IReadOnlyCollection public static readonly SuppressionList Empty = new([]); private readonly IReadOnlyCollection _filters; - private readonly IReadOnlyCollection _minimizedFilters; /// public int Count => _filters.Count; @@ -26,11 +25,10 @@ public sealed class SuppressionList : IReadOnlyCollection /// is . public SuppressionList(IEnumerable suppressionFilters) { - if (suppressionFilters == null) + if (suppressionFilters == null) throw new ArgumentNullException(nameof(suppressionFilters)); _filters = [..suppressionFilters]; - _minimizedFilters = MinimizeSuppressions(_filters); } internal SuppressionList(JsonSuppressionList suppressionList) @@ -39,7 +37,6 @@ internal SuppressionList(JsonSuppressionList suppressionList) throw new ArgumentNullException(nameof(suppressionList)); _filters = suppressionList.Filters.Select(x => new SuppressionFilter(x)).ToList(); - _minimizedFilters = MinimizeSuppressions(_filters); } /// Serializes the suppression list as JSON to the specified stream. @@ -74,7 +71,7 @@ public IEnumerable Apply(IEnumerable error /// if a filter in the list suppresses ; otherwise, . public bool Suppresses(VerificationError error) { - foreach (var filter in _minimizedFilters) + foreach (var filter in _filters) { if (filter.Suppresses(error)) { @@ -85,22 +82,6 @@ public bool Suppresses(VerificationError error) return false; } - private static IReadOnlyCollection MinimizeSuppressions(IEnumerable filters) - { - var sortedFilters = filters.Where(f => !f.IsDisabled) - .OrderBy(x => x.Specificity()); - - var result = new List(); - - foreach (var filter in sortedFilters) - { - if (result.All(x => !filter.IsSupersededBy(x))) - result.Add(filter); - } - - return result; - } - /// public IEnumerator GetEnumerator() { diff --git a/test/ModVerify.Test/Reporting/SuppressionListTest.cs b/test/ModVerify.Test/Reporting/SuppressionListTest.cs new file mode 100644 index 00000000..b6d9363b --- /dev/null +++ b/test/ModVerify.Test/Reporting/SuppressionListTest.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting.Suppressions; +using AET.ModVerify.Verifiers; +using Xunit; + +namespace ModVerify.Test.Reporting; + +public class SuppressionListTest +{ + [Fact] + public void Suppresses_ErrorMatchedOnlyByDisjointFilter_IsStillSuppressed() + { + var list = new SuppressionList( + [ + new SuppressionFilter(id: "E1", verifier: null, asset: null), + new SuppressionFilter(id: null, verifier: "V", asset: "a"), + ]); + + // Matches only the second filter (Verifier "V" + Asset "a"); the first (Id "E1") does not apply. + var error = CreateError(id: "OTHER", verifier: "V", asset: "a"); + + Assert.True(list.Suppresses(error)); + } + + [Fact] + public void Suppresses_ErrorMatchedByBroadFilter_IsSuppressed() + { + var list = new SuppressionList( + [ + new SuppressionFilter(id: "E1", verifier: null, asset: null), + new SuppressionFilter(id: null, verifier: "V", asset: "a"), + ]); + + var error = CreateError(id: "E1", verifier: "AnyVerifier", asset: "any-asset"); + + Assert.True(list.Suppresses(error)); + } + + private static VerificationError CreateError(string id, string verifier, string asset) + { + return new VerificationError(id, "message", new FakeVerifierInfo(verifier), ["ctx"], asset, VerificationSeverity.Warning); + } + + private sealed class FakeVerifierInfo(string name) : IGameVerifierInfo + { + public IGameVerifierInfo? Parent => null; + public IReadOnlyList VerifierChain => [this]; + public string Name { get; } = name; + public string FriendlyName => Name; + } +} From 321bda3db5473bd83e0444571eb1edb8329b03c9 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Thu, 9 Jul 2026 10:06:53 +0200 Subject: [PATCH 4/6] add verifier documentation --- .../Caching/IAlreadyVerifiedCache.cs | 19 ++++++- .../Verifiers/Caching/VerifiedCacheEntry.cs | 10 +++- .../IDuplicateVerificationContext.cs | 14 +++++- src/ModVerify/Verifiers/GameVerifier.cs | 37 +++++++++++++- src/ModVerify/Verifiers/GameVerifierBase.cs | 49 +++++++++++++++++-- src/ModVerify/Verifiers/IGameVerifierInfo.cs | 7 ++- .../Verifiers/NamedGameEntityVerifier.cs | 23 ++++++++- .../DuplicateVerificationContextExtensions.cs | 14 +++++- .../Verifiers/VerificationErrorEventArgs.cs | 6 ++- 9 files changed, 164 insertions(+), 15 deletions(-) diff --git a/src/ModVerify/Verifiers/Caching/IAlreadyVerifiedCache.cs b/src/ModVerify/Verifiers/Caching/IAlreadyVerifiedCache.cs index 68f39b40..e7707b55 100644 --- a/src/ModVerify/Verifiers/Caching/IAlreadyVerifiedCache.cs +++ b/src/ModVerify/Verifiers/Caching/IAlreadyVerifiedCache.cs @@ -1,8 +1,25 @@ -namespace AET.ModVerify.Verifiers.Caching; +namespace AET.ModVerify.Verifiers.Caching; +/// Provides a cache that tracks which assets have already been verified within a single verification run. public interface IAlreadyVerifiedCache { + /// Adds an entry to the cache if it is not already present. + /// The identifier of the asset, such as a file name. + /// + /// When this method returns, contains a value indicating whether the asset was found. + /// if the asset was found; otherwise, . + /// + /// + /// if the entry was added; + /// if an entry for already existed. + /// bool TryAddEntry(string entry, bool assetExists); + /// Gets the cache entry for the specified asset. + /// The identifier of the asset, such as a file name. + /// + /// The cache entry, or a default entry whose is + /// if the asset has not been verified. + /// VerifiedCacheEntry GetEntry(string entry); } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/Caching/VerifiedCacheEntry.cs b/src/ModVerify/Verifiers/Caching/VerifiedCacheEntry.cs index 252a7c09..c35f32a8 100644 --- a/src/ModVerify/Verifiers/Caching/VerifiedCacheEntry.cs +++ b/src/ModVerify/Verifiers/Caching/VerifiedCacheEntry.cs @@ -1,11 +1,19 @@ -namespace AET.ModVerify.Verifiers.Caching; +namespace AET.ModVerify.Verifiers.Caching; +/// Represents the result of a lookup in an . public readonly struct VerifiedCacheEntry { + /// Gets a value that indicates whether the asset has already been verified. + /// if the asset has already been verified; otherwise, . public bool AlreadyVerified { get; } + /// Gets a value that indicates whether the asset was found during its earlier verification. + /// if the asset was found; otherwise, . public bool AssetExists { get; } + /// Initializes a new instance of the struct. + /// if the asset has already been verified; otherwise, . + /// if the asset was found; otherwise, . public VerifiedCacheEntry(bool alreadyVerified, bool assetExists) { AlreadyVerified = alreadyVerified; diff --git a/src/ModVerify/Verifiers/Commons/Duplicates/IDuplicateVerificationContext.cs b/src/ModVerify/Verifiers/Commons/Duplicates/IDuplicateVerificationContext.cs index 4301e543..24d7b95c 100644 --- a/src/ModVerify/Verifiers/Commons/Duplicates/IDuplicateVerificationContext.cs +++ b/src/ModVerify/Verifiers/Commons/Duplicates/IDuplicateVerificationContext.cs @@ -1,11 +1,23 @@ -using System.Collections.Generic; +using System.Collections.Generic; using PG.Commons.Hashing; namespace AET.ModVerify.Verifiers.Commons; +/// Provides the data a duplicate verifier needs to detect duplicate entries within a source. public interface IDuplicateVerificationContext { + /// Gets the name of the source being checked for duplicates. string SourceName { get; } + + /// Gets the CRC-32 checksums of the entries in the source. + /// The checksums of all entries. IEnumerable GetCrcs(); + + /// Determines whether the entry with the specified checksum has duplicates. + /// The checksum of the entry to check. + /// When this method returns, contains the name of the entry. This parameter is treated as uninitialized. + /// When this method returns, contains the context entries describing the duplicates. This parameter is treated as uninitialized. + /// When this method returns, contains the message describing the duplication. This parameter is treated as uninitialized. + /// if the entry has duplicates; otherwise, . bool HasDuplicates(Crc32 crc, out string entryNames, out IEnumerable duplicateContext, out string errorMessage); } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/GameVerifier.cs b/src/ModVerify/Verifiers/GameVerifier.cs index 83871d99..66ba9021 100644 --- a/src/ModVerify/Verifiers/GameVerifier.cs +++ b/src/ModVerify/Verifiers/GameVerifier.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using AET.ModVerify.Settings; @@ -6,8 +6,15 @@ namespace AET.ModVerify.Verifiers; +/// Provides the base class for verifiers that verify individual entities of a given type. +/// The type of entity to verify. public abstract class GameVerifier : GameVerifierBase where T : notnull { + /// Initializes a new instance of the class. + /// The parent verifier, or if this is a root verifier. + /// The game engine to verify against. + /// The settings that control the verification run. + /// The service provider used to resolve dependencies. protected GameVerifier( IGameVerifierInfo? parent, IStarWarsGameEngine gameEngine, @@ -15,10 +22,17 @@ protected GameVerifier( IServiceProvider serviceProvider) : base(parent, gameEngine, settings, serviceProvider) { } + + /// Initializes a new instance of the class as a child of the specified parent verifier. + /// The parent verifier whose engine, settings, and services are inherited. protected GameVerifier(GameVerifierBase parent) : base(parent) { } + /// Initializes a new instance of the class as a root verifier. + /// The game engine to verify against. + /// The settings that control the verification run. + /// The service provider used to resolve dependencies. protected GameVerifier( IStarWarsGameEngine gameEngine, GameVerifySettings settings, @@ -26,23 +40,40 @@ protected GameVerifier( { } + /// Verifies the specified entity. + /// The entity to verify. + /// Context entries describing where the entity was found, used for error reporting. + /// The token to monitor for cancellation requests. public abstract void Verify(T toVerify, IReadOnlyCollection contextInfo, CancellationToken token); } +/// Provides the base class for verifiers that perform a single verification pass. public abstract class GameVerifier : GameVerifierBase { + /// Initializes a new instance of the class. + /// The parent verifier, or if this is a root verifier. + /// The game engine to verify against. + /// The settings that control the verification run. + /// The service provider used to resolve dependencies. protected GameVerifier( IGameVerifierInfo? parent, IStarWarsGameEngine gameEngine, GameVerifySettings settings, - IServiceProvider serviceProvider) + IServiceProvider serviceProvider) : base(parent, gameEngine, settings, serviceProvider) { } + + /// Initializes a new instance of the class as a child of the specified parent verifier. + /// The parent verifier whose engine, settings, and services are inherited. protected GameVerifier(GameVerifierBase parent) : base(parent) { } + /// Initializes a new instance of the class as a root verifier. + /// The game engine to verify against. + /// The settings that control the verification run. + /// The service provider used to resolve dependencies. protected GameVerifier( IStarWarsGameEngine gameEngine, GameVerifySettings settings, @@ -51,5 +82,7 @@ protected GameVerifier( { } + /// Runs the verification. + /// The token to monitor for cancellation requests. public abstract void Verify(CancellationToken token); } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/GameVerifierBase.cs b/src/ModVerify/Verifiers/GameVerifierBase.cs index 2da47bc6..90ead1bb 100644 --- a/src/ModVerify/Verifiers/GameVerifierBase.cs +++ b/src/ModVerify/Verifiers/GameVerifierBase.cs @@ -1,4 +1,4 @@ -using AET.ModVerify.Reporting; +using AET.ModVerify.Reporting; using AET.ModVerify.Settings; using AnakinRaW.CommonUtilities.SimplePipeline.Progress; using Microsoft.Extensions.DependencyInjection; @@ -15,40 +15,65 @@ namespace AET.ModVerify.Verifiers; +/// Provides the base class for game verifiers, handling error collection, progress reporting, and access to the game engine. public abstract class GameVerifierBase : IGameVerifierInfo { + /// Occurs when the verifier reports a new verification error. public event EventHandler? Error; - public event EventHandler>? Progress; + + /// Occurs when the verifier reports progress. + public event EventHandler>? Progress; private readonly ConcurrentDictionary _verifyErrors = new(); + /// The file system used by the verifier. protected readonly IFileSystem FileSystem; + + /// The service provider used to resolve dependencies. protected readonly IServiceProvider Services; + + /// The settings that control the verification run. protected readonly GameVerifySettings Settings; + + /// The logger used by the verifier. protected readonly ILogger Logger; + /// Gets the verification errors collected by this verifier. public IReadOnlyCollection VerifyErrors => [.. _verifyErrors.Keys]; + /// + /// The default implementation returns the runtime type name. public virtual string FriendlyName => GetType().Name; - public string Name => GetType().FullName; + /// + public string Name => GetType().FullName!; + /// public IGameVerifierInfo? Parent { get; } + /// Gets the game engine that the verifier runs against. protected IStarWarsGameEngine GameEngine { get; } + /// Gets the game repository of the . protected IGameRepository Repository => GameEngine.GameRepository; + /// public IReadOnlyList VerifierChain { get; } - protected GameVerifierBase(GameVerifierBase parent) + /// Initializes a new instance of the class as a child of the specified parent verifier. + /// The parent verifier whose engine, settings, and services are inherited. + protected GameVerifierBase(GameVerifierBase parent) : this(parent, parent.GameEngine, parent.Settings, parent.Services) { if (parent == null) throw new ArgumentNullException(nameof(parent)); } + /// Initializes a new instance of the class as a root verifier. + /// The game engine to verify against. + /// The settings that control the verification run. + /// The service provider used to resolve dependencies. protected GameVerifierBase( IStarWarsGameEngine gameEngine, GameVerifySettings settings, @@ -57,6 +82,12 @@ protected GameVerifierBase( { } + /// Initializes a new instance of the class. + /// The parent verifier, or if this is a root verifier. + /// The game engine to verify against. + /// The settings that control the verification run. + /// The service provider used to resolve dependencies. + /// , , or is . protected GameVerifierBase( IGameVerifierInfo? parent, IStarWarsGameEngine gameEngine, @@ -74,6 +105,9 @@ protected GameVerifierBase( VerifierChain = this.GetVerifierChain(); } + /// Records a verification error, raising the event the first time the error is seen. + /// The verification error to record. + /// The severity of is at least the configured throw threshold. protected void AddError(VerificationError error) { if (_verifyErrors.TryAdd(error, 0)) @@ -85,6 +119,10 @@ protected void AddError(VerificationError error) } } + /// Runs the specified action, routing exceptions that match the filter to the handler. + /// The verification action to run. + /// A predicate that selects which exceptions are handled; exceptions that do not match are not caught. + /// The handler invoked for an exception that matches . protected void GuardedVerify(Action action, Predicate exceptionFilter, Action exceptionHandler) { try @@ -97,6 +135,9 @@ protected void GuardedVerify(Action action, Predicate exceptionFilter } } + /// Raises the event with the specified progress value and message. + /// The progress value, between 0.0 and 1.0. + /// The progress message, or if none. protected void OnProgress(double progress, string? message) { Progress?.Invoke(this, new(progress, message)); diff --git a/src/ModVerify/Verifiers/IGameVerifierInfo.cs b/src/ModVerify/Verifiers/IGameVerifierInfo.cs index 731d6056..f7049685 100644 --- a/src/ModVerify/Verifiers/IGameVerifierInfo.cs +++ b/src/ModVerify/Verifiers/IGameVerifierInfo.cs @@ -1,14 +1,19 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace AET.ModVerify.Verifiers; +/// Provides identity and lineage information about a game verifier. public interface IGameVerifierInfo { + /// Gets the parent verifier that created this verifier, or if this is a root verifier. IGameVerifierInfo? Parent { get; } + /// Gets the chain of verifiers from the root down to and including this verifier. IReadOnlyList VerifierChain { get; } + /// Gets the unique name of the verifier. string Name { get; } + /// Gets the human-readable name of the verifier. string FriendlyName { get; } } \ No newline at end of file diff --git a/src/ModVerify/Verifiers/NamedGameEntityVerifier.cs b/src/ModVerify/Verifiers/NamedGameEntityVerifier.cs index 18ec9c30..ce54e976 100644 --- a/src/ModVerify/Verifiers/NamedGameEntityVerifier.cs +++ b/src/ModVerify/Verifiers/NamedGameEntityVerifier.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using AET.ModVerify.Settings; using AET.ModVerify.Verifiers.Commons; @@ -9,17 +9,25 @@ namespace AET.ModVerify.Verifiers; +/// Provides the base class for verifiers that iterate the named entities of a game manager, checking each entity and its duplicates. +/// The type of named XML entity to verify. +/// The game engine to verify against. +/// The settings that control the verification run. +/// The service provider used to resolve dependencies. public abstract partial class NamedGameEntityVerifier( IStarWarsGameEngine gameEngine, GameVerifySettings settings, - IServiceProvider serviceProvider) + IServiceProvider serviceProvider) : GameVerifier(null, gameEngine, settings, serviceProvider) where T : NamedXmlObject { + /// Gets the game manager that provides the entities to verify. public abstract IGameManager GameManager { get; } + /// Gets the display name of the entity type being verified. public abstract string EntityTypeName { get; } + /// public sealed override void Verify(CancellationToken token) { OnProgress(0.0, $"Verifying GameManager for '{EntityTypeName}'"); @@ -41,12 +49,23 @@ public sealed override void Verify(CancellationToken token) PostEntityVerify(token); } + /// Performs verification after all entities have been verified. + /// The token to monitor for cancellation requests. + /// The default implementation does nothing. protected virtual void PostEntityVerify(CancellationToken token) { } + /// Verifies a single entity. + /// The entity to verify. + /// The context entries describing the entity, used for error reporting. + /// The current progress value, between 0.0 and 1.0. + /// The token to monitor for cancellation requests. protected abstract void VerifyEntity(T entity, string[] context, double progress, CancellationToken token); + /// Performs verification before any entity is verified. + /// The token to monitor for cancellation requests. + /// The default implementation checks the game manager for duplicate entries. protected virtual void PreEntityVerify(CancellationToken token) { VerifyDuplicates(token); diff --git a/src/ModVerify/Verifiers/Utilities/DuplicateVerificationContextExtensions.cs b/src/ModVerify/Verifiers/Utilities/DuplicateVerificationContextExtensions.cs index cd81de69..b1c2f74a 100644 --- a/src/ModVerify/Verifiers/Utilities/DuplicateVerificationContextExtensions.cs +++ b/src/ModVerify/Verifiers/Utilities/DuplicateVerificationContextExtensions.cs @@ -1,20 +1,30 @@ -using AET.ModVerify.Verifiers.Commons; +using AET.ModVerify.Verifiers.Commons; using PG.StarWarsGame.Engine; using PG.StarWarsGame.Files.MTD.Files; using PG.StarWarsGame.Files.XML.Data; namespace AET.ModVerify.Verifiers.Utilities; +/// Provides factory methods for creating instances. public static class DuplicateVerificationContextExtensions { extension(IDuplicateVerificationContext) { + /// Creates a duplicate verification context for the entries of an MTD file. + /// The MTD file whose entries are checked for duplicates. + /// A new duplicate verification context. public static IDuplicateVerificationContext CreateForMtd(IMtdFile mtdFile) { return new MtdDuplicateVerificationContext(mtdFile); } - public static IDuplicateVerificationContext CreateForNamedXmlObjects(IGameManager gameManager, string databaseName) where T : NamedXmlObject + /// Creates a duplicate verification context for the named entities of a game manager. + /// The type of named XML entity. + /// The game manager whose entities are checked for duplicates. + /// The name of the database, used for error reporting. + /// A new duplicate verification context. + public static IDuplicateVerificationContext CreateForNamedXmlObjects(IGameManager gameManager, string databaseName) + where T : NamedXmlObject { return new NamedXmlObjectDuplicateVerificationContext(databaseName, gameManager); } diff --git a/src/ModVerify/Verifiers/VerificationErrorEventArgs.cs b/src/ModVerify/Verifiers/VerificationErrorEventArgs.cs index dcb85666..9891c882 100644 --- a/src/ModVerify/Verifiers/VerificationErrorEventArgs.cs +++ b/src/ModVerify/Verifiers/VerificationErrorEventArgs.cs @@ -1,9 +1,13 @@ -using System; +using System; using AET.ModVerify.Reporting; namespace AET.ModVerify.Verifiers; +/// Provides data for the event raised when a verifier reports a verification error. +/// The verification error that was reported. +/// is . public sealed class VerificationErrorEventArgs(VerificationError error) : EventArgs { + /// Gets the verification error that was reported. public VerificationError Error { get; } = error ?? throw new ArgumentNullException(nameof(error)); } \ No newline at end of file From f0632e332b95bdfeeb54462c9bf4a376a7366de1 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Thu, 9 Jul 2026 10:13:50 +0200 Subject: [PATCH 5/6] update deps --- .../PG.StarWarsGame.Engine.FileSystem.Test.csproj | 2 +- .../PG.StarWarsGame.Engine.Test.csproj | 2 +- test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj | 2 +- test/ModVerify.Test/ModVerify.Test.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine.FileSystem.Test/PG.StarWarsGame.Engine.FileSystem.Test.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine.FileSystem.Test/PG.StarWarsGame.Engine.FileSystem.Test.csproj index 3b616cdd..8bc56ecb 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine.FileSystem.Test/PG.StarWarsGame.Engine.FileSystem.Test.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine.FileSystem.Test/PG.StarWarsGame.Engine.FileSystem.Test.csproj @@ -18,7 +18,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/PetroglyphTools/PG.StarWarsGame.Engine.Test/PG.StarWarsGame.Engine.Test.csproj b/src/PetroglyphTools/PG.StarWarsGame.Engine.Test/PG.StarWarsGame.Engine.Test.csproj index 059819a0..1d746d3e 100644 --- a/src/PetroglyphTools/PG.StarWarsGame.Engine.Test/PG.StarWarsGame.Engine.Test.csproj +++ b/src/PetroglyphTools/PG.StarWarsGame.Engine.Test/PG.StarWarsGame.Engine.Test.csproj @@ -19,7 +19,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj index ff2021d5..ec09fa28 100644 --- a/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj +++ b/test/ModVerify.CliApp.Test/ModVerify.CliApp.Test.csproj @@ -20,7 +20,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/ModVerify.Test/ModVerify.Test.csproj b/test/ModVerify.Test/ModVerify.Test.csproj index 44a43c31..d3dce840 100644 --- a/test/ModVerify.Test/ModVerify.Test.csproj +++ b/test/ModVerify.Test/ModVerify.Test.csproj @@ -19,7 +19,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From 5cf95f4803cc335da203b544431f056b807b8ad4 Mon Sep 17 00:00:00 2001 From: AnakinRaW Date: Thu, 9 Jul 2026 10:14:17 +0200 Subject: [PATCH 6/6] update sub and deps --- Directory.Build.props | 2 +- modules/ModdingToolBase | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index dbdcc82a..dec65c7d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,7 +36,7 @@ all - 3.10.85 + 3.10.91 diff --git a/modules/ModdingToolBase b/modules/ModdingToolBase index 151cfb97..cc699325 160000 --- a/modules/ModdingToolBase +++ b/modules/ModdingToolBase @@ -1 +1 @@ -Subproject commit 151cfb97441442cf252fcb368dd3d423ddc5e078 +Subproject commit cc699325106a2aca3e1bf68e7015796602e66736