Skip to content
Open
26 changes: 22 additions & 4 deletions Px.Utils.TestingApp/Commands/Benchmark.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Diagnostics;
using System.Reflection;
using Px.Utils.Validation;

namespace Px.Utils.TestingApp.Commands
{
Expand Down Expand Up @@ -77,12 +78,18 @@ internal BenchmarkResult(string name, IReadOnlyList<double> iterationTimesMs)
/// </summary>
protected Func<Task>[] BenchmarkFunctionsAsync { get; set; } = [];

/// <summary>
/// Feedback retention options used by validation benchmark commands.
/// </summary>
protected ValidationOptions ValidationOptions { get; private set; } = new();

private static readonly string[] iterFlags = ["-i", "-iter"];
private static readonly string[] feedbackLimitFlags = ["-l", "-limit"];

/// <summary>
/// List of flags that can be used to provide parameters to the benchmark command.
/// </summary>
protected List<string[]> ParameterFlags { get; } = [iterFlags];
protected List<string[]> ParameterFlags { get; } = [iterFlags, feedbackLimitFlags];

internal List<BenchmarkResult> Results { get; } = [];
private int processesCompleted;
Expand Down Expand Up @@ -137,7 +144,8 @@ internal override async Task Run(bool batchMode, List<string>? inputs = null)

protected virtual void SetRunParameters()
{
if (Parameters.Keys.Count == ParameterFlags.Count)
ValidationOptions = new();
if (Parameters.Keys.All(key => Array.Exists(ParameterFlags.ToArray(), flag => flag.Contains(key))))
{
foreach (string key in Parameters.Keys)
{
Expand All @@ -146,9 +154,9 @@ protected virtual void SetRunParameters()
{
Iterations = iterations;
}
else if(!Array.Exists(ParameterFlags.ToArray(), flag => flag.Contains(key)))
else if (feedbackLimitFlags.Contains(key) && Parameters[key].Count == 1)
{
throw new ArgumentException($"Invalid argument {key} {string.Join(' ', Parameters[key])}");
ValidationOptions = ParseValidationOptions(Parameters[key][0]);
}
}
}
Expand Down Expand Up @@ -176,6 +184,16 @@ protected virtual void StartInteractiveMode()
Iterations = value;
}

private static ValidationOptions ParseValidationOptions(string value)
{
if (!int.TryParse(value, out int limit) || limit <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), "Feedback limit must be a positive integer.");
}

return new ValidationOptions { MaxFeedbackItemsPerSignature = limit };
}
Comment thread
sakari-malkki marked this conversation as resolved.

/// <summary>
/// Setup method for the benchmark. Called before running the benchmarks. Marked as virtual to allow for custom setup in derived classes.
/// </summary>
Expand Down
19 changes: 8 additions & 11 deletions Px.Utils.TestingApp/Commands/DataValidationBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,14 @@ internal sealed class DataValidationBenchmark : FileBenchmark
internal override string Help =>
"Validates the px file data." + Environment.NewLine +
"\t-r, -rows: How many data rows the validator should expect." + Environment.NewLine +
"\t-c, -cols: How many data colums the validator should expect.";
"\t-c, -cols: How many data colums the validator should expect." + Environment.NewLine +
"\t-l, -limit: Feedback items retained per file, level, and rule; use a positive number. Defaults to 100.";

internal override string Description => "Benchmarks the data validation capabilities of the DataValidator.";

private long start;
private const string dataKeyword = "DATA";

private Encoding encoding;

private const int readStartOffset = 3;

internal DataValidationBenchmark()
{
BenchmarkFunctions = [ValidateDataBenchmarks];
Expand All @@ -44,28 +41,28 @@ protected override async Task OneTimeBenchmarkSetupAsync()
using Stream stream = new FileStream(TestFilePath, FileMode.Open, FileAccess.Read);
PxFileMetadataReader reader = new();
encoding = reader.GetEncoding(stream);
start = StreamUtilities.FindKeywordPosition(stream, dataKeyword, PxFileConfiguration.Default);
start = StreamUtilities.FindDataStartPosition(stream, PxFileConfiguration.Default);
if (start == -1)
{
throw new ArgumentException($"Could not find data keyword '{dataKeyword}'");
throw new ArgumentException("Could not find the first data value after 'DATA='");
}
}

private void ValidateDataBenchmarks()
{
using Stream stream = new FileStream(TestFilePath, FileMode.Open, FileAccess.Read);
stream.Position = start + dataKeyword.Length + readStartOffset; // skip the '=' and linechange
stream.Position = start;
DataValidator validator = new(expectedCols, expectedRows, 0);
validator.Validate(stream, TestFilePath, encoding);
validator.Validate(stream, TestFilePath, encoding, null, ValidationOptions);
}

private async Task ValidateDataBenchmarksAsync()
{
using Stream stream = new FileStream(TestFilePath, FileMode.Open, FileAccess.Read);
stream.Position = start + dataKeyword.Length + readStartOffset; // skip the '=' and linechange
stream.Position = start;
DataValidator validator = new(expectedCols, expectedRows, 0);

await validator.ValidateAsync(stream, TestFilePath, encoding);
await validator.ValidateAsync(stream, TestFilePath, encoding, null, ValidationOptions);
}

protected override void SetRunParameters()
Expand Down
8 changes: 5 additions & 3 deletions Px.Utils.TestingApp/Commands/DatabaseValidationBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ namespace Px.Utils.TestingApp.Commands
{
internal sealed class DatabaseValidationBenchmark : Benchmark
{
internal override string Help => "Validates a px path database.";
internal override string Help =>
"Validates a px path database." + Environment.NewLine +
"\t-l, -limit: Feedback items retained per file, level, and rule; use a positive number. Defaults to 100.";

internal override string Description => "Validates a px path database.";
private static readonly string[] directoryFlags = ["-d", "-directory"];
Expand Down Expand Up @@ -59,13 +61,13 @@ protected override async Task OneTimeBenchmarkSetupAsync()
private void ValidationBenchmark()
{
if(validator is null) throw new InvalidOperationException("Validator not initialized.");
validator.Validate();
validator.Validate(ValidationOptions);
}

private async Task ValidationBenchmarkAsync()
{
if(validator is null) throw new InvalidOperationException("Validator not initialized.");
await validator.ValidateAsync();
await validator.ValidateAsync(ValidationOptions);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ internal sealed class MetadataContentValidationBenchmark : FileBenchmark
internal override string Help =>
"Validates the contents of the Px file metadata given amount of times." + Environment.NewLine +
"\t-f, -file: The path to the px file to read." + Environment.NewLine +
"\t-i, -iter: The number of iterations to run.";
"\t-i, -iter: The number of iterations to run." + Environment.NewLine +
"\t-l, -limit: Feedback items retained per file, level, and rule; use a positive number. Defaults to 100.";

internal override string Description => "Benchmarks the metadata content validation of Px.Utils/Validation/SyntaxValidator.";

Expand All @@ -34,7 +35,7 @@ protected override async Task OneTimeBenchmarkSetupAsync()
private void ValidateContentBenchmark()
{
ContentValidator validator = new(TestFilePath, Encoding.Default, [.. _entries]);
validator.Validate();
validator.Validate(ValidationOptions);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ internal sealed class MetadataSyntaxValidationBenchmark : FileBenchmark
internal override string Help =>
"Validates the syntax of the Px file metadata given amount of times." + Environment.NewLine +
"\t-f, -file: The path to the px file to read." + Environment.NewLine +
"\t-i, -iter: The number of iterations to run.";
"\t-i, -iter: The number of iterations to run." + Environment.NewLine +
"\t-l, -limit: Feedback items retained per file, level, and rule; use a positive number. Defaults to 100.";

internal override string Description => "Benchmarks the metadata syntax validation of Px.Utils/Validation/SyntaxValidator.";

Expand All @@ -34,15 +35,15 @@ private void SyntaxValidationBenchmark()
{
using Stream stream = new FileStream(TestFilePath, FileMode.Open, FileAccess.Read);
SyntaxValidator validator = new();
validator.Validate(stream, TestFilePath, encoding);
validator.Validate(stream, TestFilePath, encoding, null, ValidationOptions);
stream.Close();
}

private async Task SyntaxValidationBenchmarkAsync()
{
using Stream stream = new FileStream(TestFilePath, FileMode.Open, FileAccess.Read);
SyntaxValidator validator = new();
await validator.ValidateAsync(stream, TestFilePath, encoding);
await validator.ValidateAsync(stream, TestFilePath, encoding, null, ValidationOptions);
stream.Close();
}
}
Expand Down
7 changes: 4 additions & 3 deletions Px.Utils.TestingApp/Commands/PxFileValidationBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ namespace Px.Utils.TestingApp.Commands
internal sealed class PxFileValidationBenchmark : FileBenchmark
{
internal override string Help =>
"Runs through the whole px file validation process (metadata syntax- and contents-, data-) for the given file.";
"Runs through the whole px file validation process (metadata syntax- and contents-, data-) for the given file." + Environment.NewLine +
"\t-l, -limit: Feedback items retained per file, level, and rule; use a positive number. Defaults to 100.";

internal override string Description => "Benchmarks the px file validation capabilities of the PxFileValidator.";

Expand Down Expand Up @@ -42,14 +43,14 @@ private void ValidatePxFileBenchmarks()
{
using Stream stream = new FileStream(TestFilePath, FileMode.Open, FileAccess.Read);
PxFileValidator validator = new();
validator.Validate(stream, TestFilePath, encoding);
validator.Validate(stream, TestFilePath, encoding, null, ValidationOptions);
}

private async Task ValidatePxFileBenchmarksAsync()
{
using Stream stream = new FileStream(TestFilePath, FileMode.Open, FileAccess.Read);
PxFileValidator validator = new();
await validator.ValidateAsync(stream, TestFilePath, encoding);
await validator.ValidateAsync(stream, TestFilePath, encoding, null, ValidationOptions);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Px.Utils.Models.Metadata;
using Px.Utils.PxFile;
using Px.Utils.PxFile.Data;
using PxFileTests.Fixtures;
using Px.Utils.Models.Data;
Expand All @@ -12,6 +13,46 @@ public class DataReaderTests
{
private readonly double[] missingMarkers = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6];

[TestMethod]
public void ReadDoubleDataValuesQuotedDataKeywordAndWhitespaceWithSmallBufferReturnsCorrectValue()
{
// Arrange
byte[] data = Encoding.UTF8.GetBytes("TITLE=\"DATA=not-an-entry\";\nDATA=\r\n\t1;");
using Stream stream = new MemoryStream(data);
using PxFileStreamDataReader reader = new(stream, null, 2);
DoubleDataValue[] targetBuffer = new DoubleDataValue[1];
MatrixMetadata metadata = TestModelBuilder.BuildTestMetadata([1]);

// Act
reader.ReadDoubleDataValues(targetBuffer, 0, metadata, metadata);

// Assert
Assert.AreEqual(1.0, targetBuffer[0].UnsafeValue);
}

[TestMethod]
public void ReadDoubleDataValuesExplicitDataStartMatchesAutomaticPositioning()
{
// Arrange
byte[] data = Encoding.UTF8.GetBytes("TITLE=\"DATA=not-an-entry\";\nDATA= 1;");
using Stream positionStream = new MemoryStream(data);
long dataStart = StreamUtilities.FindDataStartPosition(positionStream, PxFileConfiguration.Default);
using Stream automaticStream = new MemoryStream(data);
using Stream explicitPositionStream = new MemoryStream(data);
using PxFileStreamDataReader automaticReader = new(automaticStream, null, 2);
using PxFileStreamDataReader explicitPositionReader = new(explicitPositionStream, dataStart, null, 2);
DoubleDataValue[] automaticBuffer = new DoubleDataValue[1];
DoubleDataValue[] explicitPositionBuffer = new DoubleDataValue[1];
MatrixMetadata metadata = TestModelBuilder.BuildTestMetadata([1]);

// Act
automaticReader.ReadDoubleDataValues(automaticBuffer, 0, metadata, metadata);
explicitPositionReader.ReadDoubleDataValues(explicitPositionBuffer, 0, metadata, metadata);

// Assert
CollectionAssert.AreEqual(automaticBuffer, explicitPositionBuffer);
}

[TestMethod]
public void ReadDoubleDataValuesValidIntegersReturnsCorrectDoubleDataValues()
{
Expand Down
Loading
Loading