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 const bool showOps = true; int Seed = 2135974; // Environment.TickCount; // ORACLES var reference = new SortedDictionary(); var subject = BaseOrderedMap.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 if (showOps)Console.WriteLine("insert"); reference[key] = val; subject.Set(key, val); } else { // ACTION: REMOVE if (reference.ContainsKey(key)) { if (showOps)Console.WriteLine("remove"); 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(); var subject = BaseOrderedMap.CreateTransient(_strategy); var random = new Random(Seed); // Fill Data for(int i=0; i= 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 expected, TransientMap actual) { // 1. Count if (expected.Count != actual.Count) { Console.WriteLine("BP"); 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) { Console.WriteLine("BP"); 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!"); } } }