- Created `StellaOps.TestKit.Tests` project for unit tests related to determinism. - Implemented `DeterminismManifestTests` to validate deterministic output for canonical bytes and strings, file read/write operations, and error handling for invalid schema versions. - Added `SbomDeterminismTests` to ensure identical inputs produce consistent SBOMs across SPDX 3.0.1 and CycloneDX 1.6/1.7 formats, including parallel execution tests. - Updated project references in `StellaOps.Integration.Determinism` to include the new determinism testing library.
127 lines
3.8 KiB
C#
127 lines
3.8 KiB
C#
namespace StellaOps.TestKit.Deterministic;
|
|
|
|
/// <summary>
|
|
/// Provides deterministic random number generation for testing.
|
|
/// Uses a fixed seed to ensure reproducible random sequences.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Usage:
|
|
/// <code>
|
|
/// var random = new DeterministicRandom(seed: 42);
|
|
/// var value1 = random.Next(); // Same value every time with seed 42
|
|
/// var value2 = random.NextDouble(); // Deterministic sequence
|
|
///
|
|
/// // For property-based testing with FsCheck
|
|
/// var gen = DeterministicRandom.CreateGen(seed: 42);
|
|
/// </code>
|
|
/// </remarks>
|
|
public sealed class DeterministicRandom
|
|
{
|
|
private readonly System.Random _random;
|
|
private readonly int _seed;
|
|
|
|
/// <summary>
|
|
/// Creates a new deterministic random number generator with the specified seed.
|
|
/// </summary>
|
|
/// <param name="seed">The seed value. Same seed always produces same sequence.</param>
|
|
public DeterministicRandom(int seed)
|
|
{
|
|
_seed = seed;
|
|
_random = new System.Random(seed);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the seed used for this random number generator.
|
|
/// </summary>
|
|
public int Seed => _seed;
|
|
|
|
/// <summary>
|
|
/// Returns a non-negative random integer.
|
|
/// </summary>
|
|
public int Next() => _random.Next();
|
|
|
|
/// <summary>
|
|
/// Returns a non-negative random integer less than the specified maximum.
|
|
/// </summary>
|
|
public int Next(int maxValue) => _random.Next(maxValue);
|
|
|
|
/// <summary>
|
|
/// Returns a random integer within the specified range.
|
|
/// </summary>
|
|
public int Next(int minValue, int maxValue) => _random.Next(minValue, maxValue);
|
|
|
|
/// <summary>
|
|
/// Returns a random floating-point number between 0.0 and 1.0.
|
|
/// </summary>
|
|
public double NextDouble() => _random.NextDouble();
|
|
|
|
/// <summary>
|
|
/// Fills the elements of the specified array with random bytes.
|
|
/// </summary>
|
|
public void NextBytes(byte[] buffer) => _random.NextBytes(buffer);
|
|
|
|
/// <summary>
|
|
/// Fills the elements of the specified span with random bytes.
|
|
/// </summary>
|
|
public void NextBytes(Span<byte> buffer) => _random.NextBytes(buffer);
|
|
|
|
/// <summary>
|
|
/// Creates a new deterministic Random instance with the specified seed.
|
|
/// Useful for integration with code that expects System.Random.
|
|
/// </summary>
|
|
public static System.Random CreateRandom(int seed) => new(seed);
|
|
|
|
/// <summary>
|
|
/// Generates a deterministic GUID based on the seed.
|
|
/// </summary>
|
|
public Guid NextGuid()
|
|
{
|
|
var bytes = new byte[16];
|
|
_random.NextBytes(bytes);
|
|
return new Guid(bytes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates a deterministic string of the specified length using alphanumeric characters.
|
|
/// </summary>
|
|
public string NextString(int length)
|
|
{
|
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
var result = new char[length];
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
result[i] = chars[_random.Next(chars.Length)];
|
|
}
|
|
return new string(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Selects a random element from the specified array.
|
|
/// </summary>
|
|
public T NextElement<T>(T[] array)
|
|
{
|
|
if (array == null || array.Length == 0)
|
|
{
|
|
throw new ArgumentException("Array cannot be null or empty", nameof(array));
|
|
}
|
|
return array[_random.Next(array.Length)];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shuffles an array in-place using the Fisher-Yates algorithm (deterministic).
|
|
/// </summary>
|
|
public void Shuffle<T>(T[] array)
|
|
{
|
|
if (array == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(array));
|
|
}
|
|
|
|
for (int i = array.Length - 1; i > 0; i--)
|
|
{
|
|
int j = _random.Next(i + 1);
|
|
(array[i], array[j]) = (array[j], array[i]);
|
|
}
|
|
}
|
|
}
|