First
This commit is contained in:
commit
79b5ab98aa
13 changed files with 2016 additions and 0 deletions
160
TestProject1/FuzzTest.cs
Normal file
160
TestProject1/FuzzTest.cs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using PersistentMap;
|
||||
|
||||
public class BTreeFuzzTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
private readonly IntStrategy _strategy = new();
|
||||
|
||||
public BTreeFuzzTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Fuzz_Insert_And_Remove_consistency()
|
||||
{
|
||||
// CONFIGURATION
|
||||
const int Iterations = 100_000; // High enough to trigger all splits/merges
|
||||
const int KeyRange = 5000; // Small enough to cause frequent collisions
|
||||
int Seed = Environment.TickCount;
|
||||
|
||||
// ORACLES
|
||||
var reference = new SortedDictionary<int, int>();
|
||||
var subject = BaseOrderedMap<int, int, IntStrategy>.CreateTransient(_strategy);
|
||||
|
||||
var random = new Random(Seed);
|
||||
_output.WriteLine($"Starting Fuzz Test with Seed: {Seed}");
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < Iterations; i++)
|
||||
{
|
||||
// 1. Pick an Action: 70% Insert/Update, 30% Remove
|
||||
bool isInsert = random.NextDouble() < 0.7;
|
||||
int key = random.Next(KeyRange);
|
||||
int val = key * 100;
|
||||
|
||||
if (isInsert)
|
||||
{
|
||||
// ACTION: INSERT
|
||||
reference[key] = val;
|
||||
subject.Set(key, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ACTION: REMOVE
|
||||
if (reference.ContainsKey(key))
|
||||
{
|
||||
reference.Remove(key);
|
||||
subject.Remove(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try removing non-existent key (should be safe)
|
||||
subject.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. VERIFY CONSISTENCY (Expensive but necessary)
|
||||
// We check consistency every 1000 ops or if the tree is small,
|
||||
// to keep the test fast enough.
|
||||
if (i % 1000 == 0 || reference.Count < 100)
|
||||
{
|
||||
AssertConsistency(reference, subject);
|
||||
}
|
||||
}
|
||||
|
||||
// Final check
|
||||
AssertConsistency(reference, subject);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_output.WriteLine($"FAILED at iteration with SEED: {Seed}");
|
||||
throw; // Re-throw to fail the test
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Fuzz_Range_Queries()
|
||||
{
|
||||
// Validates that your Range Enumerator matches LINQ on the reference
|
||||
const int Iterations = 1000;
|
||||
const int KeyRange = 2000;
|
||||
int Seed = Environment.TickCount;
|
||||
|
||||
var reference = new SortedDictionary<int, int>();
|
||||
var subject = BaseOrderedMap<int, int, IntStrategy>.CreateTransient(_strategy);
|
||||
var random = new Random(Seed);
|
||||
|
||||
// Fill Data
|
||||
for(int i=0; i<KeyRange; i++)
|
||||
{
|
||||
int k = random.Next(KeyRange);
|
||||
reference[k] = k;
|
||||
subject.Set(k, k);
|
||||
}
|
||||
|
||||
var persistent = subject.ToPersistent();
|
||||
|
||||
for (int i = 0; i < Iterations; i++)
|
||||
{
|
||||
int min = random.Next(KeyRange);
|
||||
int max = min + random.Next(KeyRange - min); // Ensure max >= min
|
||||
|
||||
// 1. Reference Result (LINQ)
|
||||
// Note: SortedDictionary doesn't have a direct Range query, so we filter memory.
|
||||
var expected = reference
|
||||
.Where(kv => kv.Key >= min && kv.Key <= max)
|
||||
.Select(kv => kv.Key)
|
||||
.ToList();
|
||||
|
||||
// 2. Subject Result
|
||||
var actual = persistent.Range(min, max)
|
||||
.Select(kv => kv.Key)
|
||||
.ToList();
|
||||
|
||||
// 3. Compare
|
||||
if (!expected.SequenceEqual(actual))
|
||||
{
|
||||
_output.WriteLine($"Range Mismatch! Range: [{min}, {max}]");
|
||||
_output.WriteLine($"Expected: {string.Join(",", expected)}");
|
||||
_output.WriteLine($"Actual: {string.Join(",", actual)}");
|
||||
Assert.Fail("Range query results differ.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertConsistency(SortedDictionary<int, int> expected, TransientMap<int, int, IntStrategy> actual)
|
||||
{
|
||||
// 1. Count
|
||||
// if (expected.Count != actual.Count)
|
||||
// {
|
||||
// throw new Exception($"Count Mismatch! Expected {expected.Count}, Got {actual.Count}");
|
||||
// }
|
||||
|
||||
// 2. Full Scan Verification
|
||||
using var enumerator = actual.GetEnumerator();
|
||||
foreach (var kvp in expected)
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
throw new Exception("Enumerator ended too early!");
|
||||
}
|
||||
|
||||
if (enumerator.Current.Key != kvp.Key || enumerator.Current.Value != kvp.Value)
|
||||
{
|
||||
throw new Exception($"Content Mismatch! Expected [{kvp.Key}:{kvp.Value}], Got [{enumerator.Current.Key}:{enumerator.Current.Value}]");
|
||||
}
|
||||
}
|
||||
|
||||
if (enumerator.MoveNext())
|
||||
{
|
||||
throw new Exception("Enumerator has extra items!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue