Files
git.stella-ops.org/src/__Libraries/StellaOps.TestKit/Deterministic/DeterministicRandom.cs
master bc4318ef97 Add tests for SBOM generation determinism across multiple formats
- 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.
2025-12-23 18:56:12 +02:00

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]);
}
}
}