Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.300" PrivateAssets="All"/>
<PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')">
<PrivateAssets>all</PrivateAssets>
<Version>3.10.85</Version>
<Version>3.10.91</Version>
</PackageReference>
</ItemGroup>

Expand Down
28 changes: 28 additions & 0 deletions src/ModVerify/Reporting/Baseline/BaselineCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,25 @@

namespace AET.ModVerify.Reporting.Baseline;

/// <summary>Represents a set of identified verification baselines, each distinguished by a unique identifier.</summary>
public sealed class BaselineCollection : IReadOnlyCollection<IdentifiedBaseline>
{
/// <summary>Gets an empty <see cref="BaselineCollection"/> that contains no baselines.</summary>
public static readonly BaselineCollection Empty = new([]);

private readonly IReadOnlyList<IdentifiedBaseline> _baselines;

/// <inheritdoc />
public int Count => _baselines.Count;

/// <summary>Gets a value that indicates whether the collection contains no baselines.</summary>
/// <value><see langword="true"/> if the collection contains no baselines; otherwise, <see langword="false"/>.</value>
public bool IsEmpty => _baselines.Count == 0;

/// <summary>Initializes a new instance of the <see cref="BaselineCollection"/> class with the specified baselines.</summary>
/// <param name="baselines">The identified baselines to include in the collection.</param>
/// <exception cref="ArgumentNullException"><paramref name="baselines"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException">An entry in <paramref name="baselines"/> is <see langword="null"/>, or two entries share the same identifier.</exception>
public BaselineCollection(IEnumerable<IdentifiedBaseline> baselines)
{
if (baselines is null)
Expand All @@ -35,6 +44,9 @@ public BaselineCollection(IEnumerable<IdentifiedBaseline> baselines)
_baselines = list;
}

/// <summary>Determines whether any baseline in the collection contains the specified error.</summary>
/// <param name="error">The verification error to locate.</param>
/// <returns><see langword="true"/> if a baseline in the collection contains <paramref name="error"/>; otherwise, <see langword="false"/>.</returns>
public bool Contains(VerificationError error)
{
foreach (var entry in _baselines)
Expand All @@ -45,6 +57,10 @@ public bool Contains(VerificationError error)
return false;
}

/// <summary>Gets the identifier of the first baseline in the collection that contains the specified error.</summary>
/// <param name="error">The verification error to locate.</param>
/// <param name="identifier">When this method returns, contains the identifier of the matching baseline if a match was found, or <see langword="null"/> if no baseline contains the error. This parameter is treated as uninitialized.</param>
/// <returns><see langword="true"/> if a baseline containing <paramref name="error"/> was found; otherwise, <see langword="false"/>.</returns>
public bool TryGetMatchingBaseline(VerificationError error, [NotNullWhen(true)] out string? identifier)
{
foreach (var entry in _baselines)
Expand All @@ -59,6 +75,10 @@ public bool TryGetMatchingBaseline(VerificationError error, [NotNullWhen(true)]
return false;
}

/// <summary>Filters out the errors that are contained in any baseline of the collection.</summary>
/// <param name="errors">The errors to filter.</param>
/// <returns>The errors that are not contained in any baseline of the collection.</returns>
/// <exception cref="ArgumentNullException"><paramref name="errors"/> is <see langword="null"/>.</exception>
public IEnumerable<VerificationError> Apply(IEnumerable<VerificationError> errors)
{
if (errors is null)
Expand All @@ -68,6 +88,13 @@ public IEnumerable<VerificationError> Apply(IEnumerable<VerificationError> error
return errors.Where(e => !Contains(e));
}

/// <summary>Categorizes the specified errors into new, persistent, and resolved errors relative to the baselines in the collection.</summary>
/// <param name="errors">The errors found during verification.</param>
/// <returns>
/// 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.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="errors"/> is <see langword="null"/>.</exception>
public CategorizedVerificationErrors Categorize(IEnumerable<VerificationError> errors)
{
if (errors is null)
Expand Down Expand Up @@ -102,6 +129,7 @@ public CategorizedVerificationErrors Categorize(IEnumerable<VerificationError> e
new ReadOnlyValueListDictionary<string, VerificationError>(resolved));
}

/// <inheritdoc />
public IEnumerator<IdentifiedBaseline> GetEnumerator()
{
return _baselines.GetEnumerator();
Expand Down
3 changes: 3 additions & 0 deletions src/ModVerify/Reporting/Baseline/BaselineSource.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
namespace AET.ModVerify.Reporting.Baseline;

/// <summary>Specifies the origin of a verification baseline.</summary>
public enum BaselineSource
{
/// <summary>The baseline was loaded from a file on disk.</summary>
File,
/// <summary>The baseline is the default baseline embedded in the application.</summary>
EmbeddedDefault,
}
21 changes: 18 additions & 3 deletions src/ModVerify/Reporting/Baseline/BaselineVerificationTarget.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
using PG.StarWarsGame.Engine;
using PG.StarWarsGame.Engine;

namespace AET.ModVerify.Reporting.Baseline;

/// <summary>Represents the target that a verification baseline was created for.</summary>
public sealed record BaselineVerificationTarget
{
{
/// <summary>Gets or sets the game engine type of the target.</summary>
public required GameEngineType Engine { get; init; }

/// <summary>Gets or sets the name of the target.</summary>
public required string Name { get; init; }
public GameLocations? Location { get; init; } // Optional compared to Verification Target

/// <summary>Gets or sets the game locations of the target, or <see langword="null"/> if not specified.</summary>
/// <remarks>The location is optional for a baseline target, unlike for a verification target.</remarks>
public GameLocations? Location { get; init; }

/// <summary>Gets or sets the version of the target, or <see langword="null"/> if not specified.</summary>
public string? Version { get; init; }

/// <summary>Gets or sets a value that indicates whether the target is the base game rather than a mod.</summary>
/// <value>
/// <see langword="true"/> if the target is the base game; otherwise, <see langword="false"/>.
/// The default is <see langword="false"/>.
/// </value>
public bool IsGame { get; init; }
}
10 changes: 10 additions & 0 deletions src/ModVerify/Reporting/Baseline/IdentifiedBaseline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@

namespace AET.ModVerify.Reporting.Baseline;

/// <summary>Associates a verification baseline with a unique identifier and the source it was loaded from.</summary>
public sealed record IdentifiedBaseline
{
/// <summary>Gets the unique identifier of the baseline within its collection.</summary>
public string Identifier { get; }

/// <summary>Gets the verification baseline.</summary>
public VerificationBaseline Baseline { get; }

/// <summary>Gets the source the baseline was loaded from.</summary>
public BaselineSource Source { get; }

/// <summary>Initializes a new instance of the <see cref="IdentifiedBaseline"/> class.</summary>
/// <param name="identifier">The unique identifier of the baseline within its collection.</param>
/// <param name="baseline">The verification baseline.</param>
/// <param name="source">One of the enumeration values that specifies the source the baseline was loaded from.</param>
/// <exception cref="ArgumentException"><paramref name="identifier"/> is <see langword="null"/> or empty.</exception>
/// <exception cref="ArgumentNullException"><paramref name="baseline"/> is <see langword="null"/>.</exception>
public IdentifiedBaseline(string identifier, VerificationBaseline baseline, BaselineSource source)
{
if (string.IsNullOrEmpty(identifier))
Expand Down
3 changes: 2 additions & 1 deletion src/ModVerify/Reporting/Baseline/InvalidBaselineException.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
using System;
using System;

namespace AET.ModVerify.Reporting.Baseline;

/// <summary>The exception that is thrown when a verification baseline cannot be parsed or is otherwise invalid.</summary>
public sealed class InvalidBaselineException : Exception
{
internal InvalidBaselineException(string message) : base(message)
Expand Down
37 changes: 35 additions & 2 deletions src/ModVerify/Reporting/Baseline/VerificationBaseline.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
Expand All @@ -11,24 +11,34 @@

namespace AET.ModVerify.Reporting.Baseline;

/// <summary>Represents a frozen set of verification errors that are already known and should be ignored during verification.</summary>
public sealed class VerificationBaseline : IReadOnlyCollection<VerificationError>
{
/// <summary>Gets the latest supported baseline format version.</summary>
public static readonly Version LatestVersion = new(2, 2);

/// <summary>Gets the latest supported baseline format version as a string with two components.</summary>
public static readonly string LatestVersionString = LatestVersion.ToString(2);

/// <summary>Gets an empty <see cref="VerificationBaseline"/> that contains no errors.</summary>
public static readonly VerificationBaseline Empty = new(VerificationSeverity.Information, [], null);

private readonly HashSet<VerificationError> _errors;

/// <summary>Gets the target that this baseline was created for, or <see langword="null"/> if not specified.</summary>
public BaselineVerificationTarget? Target { get; }


/// <summary>Gets the format version of this baseline, or <see langword="null"/> if unknown.</summary>
public Version? Version { get; }

/// <summary>Gets the minimum severity of the errors recorded in this baseline.</summary>
public VerificationSeverity MinimumSeverity { get; }

/// <inheritdoc />
public int Count => _errors.Count;

/// <summary>Gets a value that indicates whether the baseline contains no errors.</summary>
/// <value><see langword="true"/> if the baseline contains no errors; otherwise, <see langword="false"/>.</value>
public bool IsEmpty => Count == 0;

internal VerificationBaseline(JsonVerificationBaseline baseline)
Expand All @@ -39,33 +49,55 @@ internal VerificationBaseline(JsonVerificationBaseline baseline)
Target = JsonVerificationTarget.ToTarget(baseline.Target);
}

/// <summary>Initializes a new instance of the <see cref="VerificationBaseline"/> class.</summary>
/// <param name="minimumSeverity">One of the enumeration values that specifies the minimum severity of the errors recorded in the baseline.</param>
/// <param name="errors">The errors to record in the baseline.</param>
/// <param name="target">The target the baseline was created for, or <see langword="null"/> if not specified.</param>
/// <exception cref="ArgumentNullException"><paramref name="errors"/> is <see langword="null"/>.</exception>
public VerificationBaseline(VerificationSeverity minimumSeverity, IEnumerable<VerificationError> errors, BaselineVerificationTarget? target)
{
if (errors == null) throw new ArgumentNullException(nameof(errors));
_errors = [..errors];
Version = LatestVersion;
MinimumSeverity = minimumSeverity;
Target = target;
}

/// <summary>Determines whether the baseline contains the specified error.</summary>
/// <param name="error">The verification error to locate.</param>
/// <returns><see langword="true"/> if the baseline contains <paramref name="error"/>; otherwise, <see langword="false"/>.</returns>
public bool Contains(VerificationError error)
{
return _errors.Contains(error);
}

/// <summary>Filters out the errors that are contained in the baseline.</summary>
/// <param name="errors">The errors to filter.</param>
/// <returns>The errors that are not contained in the baseline.</returns>
public IEnumerable<VerificationError> Apply(IEnumerable<VerificationError> errors)
{
return Count == 0 ? errors : errors.Where(e => !_errors.Contains(e));
}

/// <summary>Serializes the baseline as JSON to the specified stream.</summary>
/// <param name="stream">The stream to write the JSON representation to.</param>
public void ToJson(Stream stream)
{
JsonSerializer.Serialize(stream, new JsonVerificationBaseline(this), ModVerifyJsonSettings.JsonSettings);
}

/// <summary>Asynchronously serializes the baseline as JSON to the specified stream.</summary>
/// <param name="stream">The stream to write the JSON representation to.</param>
/// <returns>A task that represents the asynchronous serialization operation.</returns>
public Task ToJsonAsync(Stream stream)
{
return JsonSerializer.SerializeAsync(stream, new JsonVerificationBaseline(this), ModVerifyJsonSettings.JsonSettings);
}

/// <summary>Deserializes a baseline from its JSON representation.</summary>
/// <param name="stream">The stream to read the JSON representation from.</param>
/// <returns>The deserialized baseline.</returns>
/// <exception cref="InvalidBaselineException">The JSON representation is invalid or cannot be parsed.</exception>
public static VerificationBaseline FromJson(Stream stream)
{
return JsonBaselineParser.Parse(stream);
Expand All @@ -82,6 +114,7 @@ IEnumerator IEnumerable.GetEnumerator()
return GetEnumerator();
}

/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder($"Baseline [Version={Version}, MinSeverity={MinimumSeverity}, NumErrors={Count}");
Expand Down
2 changes: 1 addition & 1 deletion src/ModVerify/Reporting/Engine/EngineErrorReporterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal abstract class EngineErrorReporterBase<T> : IGameVerifierInfo

public IReadOnlyList<IGameVerifierInfo> VerifierChain { get; }

public string Name => GetType().FullName;
public string Name => GetType().FullName!;

public abstract string FriendlyName { get; }

Expand Down
12 changes: 8 additions & 4 deletions src/ModVerify/Reporting/Engine/GameEngineErrorCollection.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using PG.StarWarsGame.Engine.ErrorReporting;
using PG.StarWarsGame.Files.XML.ErrorHandling;

namespace AET.ModVerify.Reporting.Engine;

/// <summary>Collects the XML, initialization, and assertion errors reported by the game engine during verification.</summary>
public sealed class GameEngineErrorCollection : IGameEngineErrorCollection, IGameEngineErrorReporter
{
private readonly ConcurrentBag<XmlError> _xmlErrors = new();
private readonly ConcurrentBag<InitializationError> _initializationErrors = new();
private readonly ConcurrentBag<EngineAssert> _asserts = new();
private readonly ConcurrentBag<XmlError> _xmlErrors = [];
private readonly ConcurrentBag<InitializationError> _initializationErrors = [];
private readonly ConcurrentBag<EngineAssert> _asserts = [];

/// <inheritdoc />
public IEnumerable<XmlError> XmlErrors => _xmlErrors.ToList();

/// <inheritdoc />
public IEnumerable<InitializationError> InitializationErrors => _initializationErrors.ToList();

/// <inheritdoc />
public IEnumerable<EngineAssert> Asserts => _asserts.ToList();

void IXmlParserErrorReporter.Report(XmlError error)
Expand Down
8 changes: 7 additions & 1 deletion src/ModVerify/Reporting/Engine/IGameEngineErrorCollection.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>Provides the errors and assertions collected from the game engine during verification.</summary>
public interface IGameEngineErrorCollection
{
/// <summary>Gets the XML parser errors reported by the engine.</summary>
IEnumerable<XmlError> XmlErrors { get; }

/// <summary>Gets the initialization errors reported by the engine.</summary>
IEnumerable<InitializationError> InitializationErrors { get; }

/// <summary>Gets the assertions raised by the engine.</summary>
IEnumerable<EngineAssert> Asserts { get; }
}
7 changes: 4 additions & 3 deletions src/ModVerify/Reporting/Reporters/Console/ConsoleReporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
namespace AET.ModVerify.Reporting.Reporters;
namespace AET.ModVerify.Reporting.Reporters;

/// <summary>Provides the settings for the console verification reporter.</summary>
public sealed record ConsoleReporterSettings : ReporterSettings
{
/// <summary>Gets or sets a value that indicates whether only a summary is written to the console instead of individual findings.</summary>
/// <value>
/// <see langword="true"/> to write only a summary; otherwise, <see langword="false"/> to write individual findings.
/// The default is <see langword="false"/>.
/// </value>
public bool SummaryOnly { get; init; }
}
Loading
Loading