mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-07-28 17:57:45 +00:00
060649192f
- Remove 'C# Implementation Considerations' sections from 34 indicator .md files - Delete 29 temp PowerShell scripts (_fix_mojibake.ps1, _hex_scan.ps1, etc.) - Move test files into tests/ subdirectories for consistent project structure - Add trader-focused bullet points to indicator documentation
894 lines
21 KiB
C#
894 lines
21 KiB
C#
|
|
namespace QuanTAlib.Tests;
|
|
|
|
public class RingBufferTests
|
|
{
|
|
[Fact]
|
|
public void Constructor_ValidCapacity_CreatesBuffer()
|
|
{
|
|
var buffer = new RingBuffer(10);
|
|
|
|
Assert.Equal(10, buffer.Capacity);
|
|
Assert.Equal(0, buffer.Count);
|
|
Assert.False(buffer.IsFull);
|
|
Assert.Equal(0, buffer.Sum);
|
|
Assert.True(double.IsNaN(buffer.Average));
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_ZeroCapacity_ThrowsArgumentException()
|
|
{
|
|
Assert.Throws<ArgumentException>(() => new RingBuffer(0));
|
|
}
|
|
|
|
[Fact]
|
|
public void Constructor_NegativeCapacity_ThrowsArgumentException()
|
|
{
|
|
Assert.Throws<ArgumentException>(() => new RingBuffer(-1));
|
|
}
|
|
|
|
[Fact]
|
|
public void Add_SingleValue_UpdatesState()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
|
|
Assert.Equal(1, buffer.Count);
|
|
Assert.Equal(10.0, buffer.Sum);
|
|
Assert.Equal(10.0, buffer.Average);
|
|
Assert.Equal(10.0, buffer.Newest);
|
|
Assert.Equal(10.0, buffer.Oldest);
|
|
Assert.False(buffer.IsFull);
|
|
}
|
|
|
|
[Fact]
|
|
public void Add_MultipleValues_UpdatesState()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
Assert.Equal(3, buffer.Count);
|
|
Assert.Equal(60.0, buffer.Sum);
|
|
Assert.Equal(20.0, buffer.Average);
|
|
Assert.Equal(30.0, buffer.Newest);
|
|
Assert.Equal(10.0, buffer.Oldest);
|
|
Assert.False(buffer.IsFull);
|
|
}
|
|
|
|
[Fact]
|
|
public void Add_FillBuffer_BecomesFullAndWraps()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
Assert.Equal(3, buffer.Count);
|
|
Assert.True(buffer.IsFull);
|
|
Assert.Equal(60.0, buffer.Sum);
|
|
Assert.Equal(20.0, buffer.Average);
|
|
|
|
// Add one more - should remove 10.0
|
|
double removed = buffer.Add(40.0);
|
|
|
|
Assert.Equal(10.0, removed);
|
|
Assert.Equal(3, buffer.Count);
|
|
Assert.True(buffer.IsFull);
|
|
Assert.Equal(90.0, buffer.Sum); // 20 + 30 + 40
|
|
Assert.Equal(30.0, buffer.Average);
|
|
Assert.Equal(40.0, buffer.Newest);
|
|
Assert.Equal(20.0, buffer.Oldest);
|
|
}
|
|
|
|
[Fact]
|
|
public void Add_MultipleWraps_MaintainsCorrectState()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
// Fill and wrap multiple times
|
|
for (int i = 1; i <= 10; i++)
|
|
{
|
|
buffer.Add(i * 10.0);
|
|
}
|
|
|
|
// Should contain: 80, 90, 100
|
|
Assert.Equal(3, buffer.Count);
|
|
Assert.True(buffer.IsFull);
|
|
Assert.Equal(270.0, buffer.Sum); // 80 + 90 + 100
|
|
Assert.Equal(90.0, buffer.Average);
|
|
Assert.Equal(100.0, buffer.Newest);
|
|
Assert.Equal(80.0, buffer.Oldest);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateNewest_ModifiesLastValue()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
Assert.Equal(60.0, buffer.Sum);
|
|
|
|
buffer.UpdateNewest(35.0);
|
|
|
|
Assert.Equal(65.0, buffer.Sum); // 10 + 20 + 35
|
|
Assert.Equal(35.0, buffer.Newest);
|
|
Assert.Equal(3, buffer.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void UpdateNewest_EmptyBuffer_DoesNothing()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.UpdateNewest(100.0); // Should not throw
|
|
|
|
Assert.Equal(0, buffer.Count);
|
|
Assert.Equal(0, buffer.Sum);
|
|
}
|
|
|
|
[Fact]
|
|
public void Indexer_AccessesCorrectValues()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
// Index 0 = oldest, Index 2 = newest
|
|
Assert.Equal(10.0, buffer[0]);
|
|
Assert.Equal(20.0, buffer[1]);
|
|
Assert.Equal(30.0, buffer[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Indexer_AfterWrap_AccessesCorrectValues()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
buffer.Add(40.0); // Wraps, removes 10
|
|
|
|
// Should contain: 20, 30, 40
|
|
Assert.Equal(20.0, buffer[0]);
|
|
Assert.Equal(30.0, buffer[1]);
|
|
Assert.Equal(40.0, buffer[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Indexer_OutOfRange_ThrowsException()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
|
|
// Valid indices are 0 and 1 (2 elements)
|
|
// Index 2 should throw ArgumentOutOfRangeException
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => _ = buffer[2]);
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => _ = buffer[10]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Clear_ResetsState()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
buffer.Clear();
|
|
|
|
Assert.Equal(0, buffer.Count);
|
|
Assert.Equal(0, buffer.Sum);
|
|
Assert.True(double.IsNaN(buffer.Average));
|
|
Assert.False(buffer.IsFull);
|
|
}
|
|
|
|
[Fact]
|
|
public void Clear_AllowsReuse()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
buffer.Clear();
|
|
|
|
buffer.Add(100.0);
|
|
|
|
Assert.Equal(1, buffer.Count);
|
|
Assert.Equal(100.0, buffer.Sum);
|
|
Assert.Equal(100.0, buffer.Newest);
|
|
}
|
|
|
|
[Fact]
|
|
public void Clone_CreatesIndependentCopy()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
var clone = buffer.Clone();
|
|
|
|
// Verify clone has same state
|
|
Assert.Equal(buffer.Count, clone.Count);
|
|
Assert.Equal(buffer.Sum, clone.Sum);
|
|
Assert.Equal(buffer.Newest, clone.Newest);
|
|
Assert.Equal(buffer.Oldest, clone.Oldest);
|
|
|
|
// Modify original - clone should be unaffected
|
|
buffer.Add(40.0);
|
|
|
|
Assert.Equal(4, buffer.Count);
|
|
Assert.Equal(3, clone.Count);
|
|
Assert.Equal(100.0, buffer.Sum);
|
|
Assert.Equal(60.0, clone.Sum);
|
|
}
|
|
|
|
[Fact]
|
|
public void CopyFrom_CopiesState()
|
|
{
|
|
var source = new RingBuffer(5);
|
|
var target = new RingBuffer(5);
|
|
|
|
source.Add(10.0);
|
|
source.Add(20.0);
|
|
source.Add(30.0);
|
|
|
|
target.Add(100.0); // Different initial state
|
|
|
|
target.CopyFrom(source);
|
|
|
|
Assert.Equal(source.Count, target.Count);
|
|
Assert.Equal(source.Sum, target.Sum);
|
|
Assert.Equal(source.Newest, target.Newest);
|
|
Assert.Equal(source.Oldest, target.Oldest);
|
|
|
|
// Verify independence after copy
|
|
source.Add(40.0);
|
|
Assert.NotEqual(source.Sum, target.Sum);
|
|
}
|
|
|
|
[Fact]
|
|
public void CopyFrom_DifferentCapacity_ThrowsException()
|
|
{
|
|
var source = new RingBuffer(5);
|
|
var target = new RingBuffer(10);
|
|
|
|
Assert.Throws<ArgumentException>(() => target.CopyFrom(source));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetSpan_ReturnsChronologicalOrder()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
var span = buffer.GetSpan();
|
|
|
|
Assert.Equal(3, span.Length);
|
|
Assert.Equal(10.0, span[0]);
|
|
Assert.Equal(20.0, span[1]);
|
|
Assert.Equal(30.0, span[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetSpan_AfterWrap_ReturnsChronologicalOrder()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
buffer.Add(40.0);
|
|
buffer.Add(50.0);
|
|
|
|
var span = buffer.GetSpan();
|
|
|
|
// Should be: 30, 40, 50
|
|
Assert.Equal(3, span.Length);
|
|
Assert.Equal(30.0, span[0]);
|
|
Assert.Equal(40.0, span[1]);
|
|
Assert.Equal(50.0, span[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetSpan_EmptyBuffer_ReturnsEmpty()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
var span = buffer.GetSpan();
|
|
|
|
Assert.True(span.IsEmpty);
|
|
}
|
|
|
|
[Fact]
|
|
public void Min_ReturnsMinimumValue()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(30.0);
|
|
buffer.Add(10.0);
|
|
buffer.Add(50.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(40.0);
|
|
|
|
Assert.Equal(10.0, buffer.Min());
|
|
}
|
|
|
|
[Fact]
|
|
public void Max_ReturnsMaximumValue()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(30.0);
|
|
buffer.Add(10.0);
|
|
buffer.Add(50.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(40.0);
|
|
|
|
Assert.Equal(50.0, buffer.Max());
|
|
}
|
|
|
|
[Fact]
|
|
public void Min_EmptyBuffer_ReturnsNaN()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
Assert.True(double.IsNaN(buffer.Min()));
|
|
}
|
|
|
|
[Fact]
|
|
public void Max_EmptyBuffer_ReturnsNaN()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
Assert.True(double.IsNaN(buffer.Max()));
|
|
}
|
|
|
|
[Fact]
|
|
public void Enumerator_IteratesInChronologicalOrder()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
List<double> values = [];
|
|
foreach (var v in buffer)
|
|
{
|
|
values.Add(v);
|
|
}
|
|
|
|
Assert.Equal(3, values.Count);
|
|
Assert.Equal(10.0, values[0]);
|
|
Assert.Equal(20.0, values[1]);
|
|
Assert.Equal(30.0, values[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Enumerator_AfterWrap_IteratesInChronologicalOrder()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
buffer.Add(40.0);
|
|
buffer.Add(50.0);
|
|
|
|
List<double> values = [];
|
|
foreach (var v in buffer)
|
|
{
|
|
values.Add(v);
|
|
}
|
|
|
|
// Should be: 30, 40, 50
|
|
Assert.Equal(3, values.Count);
|
|
Assert.Equal(30.0, values[0]);
|
|
Assert.Equal(40.0, values[1]);
|
|
Assert.Equal(50.0, values[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Add_WithIsNew_WorksCorrectly()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0, isNew: true);
|
|
buffer.Add(20.0, isNew: true);
|
|
buffer.Add(25.0, isNew: false); // Should update 20.0 to 25.0
|
|
|
|
Assert.Equal(2, buffer.Count);
|
|
Assert.Equal(25.0, buffer.Newest);
|
|
Assert.Equal(35.0, buffer.Sum); // 10 + 25
|
|
}
|
|
|
|
[Fact]
|
|
public void Indexer_WithIndexType_SupportsFromEnd()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
Assert.Equal(30.0, buffer[^1]); // Newest
|
|
Assert.Equal(20.0, buffer[^2]);
|
|
Assert.Equal(10.0, buffer[^3]); // Oldest
|
|
}
|
|
|
|
[Fact]
|
|
public void Newest_EmptyBuffer_ReturnsNaN()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
Assert.True(double.IsNaN(buffer.Newest));
|
|
}
|
|
|
|
[Fact]
|
|
public void Oldest_EmptyBuffer_ReturnsNaN()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
Assert.True(double.IsNaN(buffer.Oldest));
|
|
}
|
|
|
|
[Fact]
|
|
public void Average_EmptyBuffer_ReturnsNaN()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
Assert.True(double.IsNaN(buffer.Average));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetInternalSpan_ReturnsFullBuffer()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
|
|
var span = buffer.GetInternalSpan();
|
|
|
|
Assert.Equal(5, span.Length); // Full capacity, not count
|
|
}
|
|
|
|
[Fact]
|
|
public void ToArray_AfterWrap_ReturnsChronologicalOrder()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
buffer.Add(40.0); // Wraps
|
|
|
|
var arr = buffer.ToArray();
|
|
|
|
Assert.Equal(3, arr.Length);
|
|
Assert.Equal(20.0, arr[0]);
|
|
Assert.Equal(30.0, arr[1]);
|
|
Assert.Equal(40.0, arr[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void CopyTo_AfterWrap_CopiesInChronologicalOrder()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
buffer.Add(40.0); // Wraps
|
|
|
|
var dest = new double[5];
|
|
buffer.CopyTo(dest, 1);
|
|
|
|
Assert.Equal(0, dest[0]); // Untouched
|
|
Assert.Equal(20.0, dest[1]);
|
|
Assert.Equal(30.0, dest[2]);
|
|
Assert.Equal(40.0, dest[3]);
|
|
Assert.Equal(0, dest[4]); // Untouched
|
|
}
|
|
|
|
[Fact]
|
|
public void Indexer_Set_UpdatesValueAndSum()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
Assert.Equal(60.0, buffer.Sum);
|
|
|
|
buffer[1] = 25.0; // Change 20.0 to 25.0
|
|
|
|
Assert.Equal(65.0, buffer.Sum);
|
|
Assert.Equal(25.0, buffer[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Indexer_SetFromEnd_UpdatesValueAndSum()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
buffer[^1] = 35.0; // Change newest (30.0) to 35.0
|
|
|
|
Assert.Equal(65.0, buffer.Sum);
|
|
Assert.Equal(35.0, buffer[^1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Min_LargeBuffer_UsesSimd()
|
|
{
|
|
var buffer = new RingBuffer(100);
|
|
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
buffer.Add(i + 1); // 1 to 100
|
|
}
|
|
|
|
Assert.Equal(1.0, buffer.Min());
|
|
}
|
|
|
|
[Fact]
|
|
public void Max_LargeBuffer_UsesSimd()
|
|
{
|
|
var buffer = new RingBuffer(100);
|
|
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
buffer.Add(i + 1); // 1 to 100
|
|
}
|
|
|
|
Assert.Equal(100.0, buffer.Max());
|
|
}
|
|
|
|
[Fact]
|
|
public void ToArray_EmptyBuffer_ReturnsEmpty()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
var arr = buffer.ToArray();
|
|
|
|
Assert.Empty(arr);
|
|
}
|
|
|
|
[Fact]
|
|
public void CopyTo_EmptyBuffer_DoesNothing()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
double[] dest = [1.0, 2.0, 3.0];
|
|
|
|
buffer.CopyTo(dest, 0);
|
|
|
|
Assert.Equal(1.0, dest[0]);
|
|
Assert.Equal(2.0, dest[1]);
|
|
Assert.Equal(3.0, dest[2]);
|
|
}
|
|
|
|
[Fact]
|
|
public void InternalBuffer_ReturnsSpan()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
|
|
var span = buffer.InternalBuffer;
|
|
|
|
Assert.Equal(5, span.Length);
|
|
Assert.Equal(10.0, span[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Enumerator_Reset_AllowsReIteration()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
|
|
var enumerator = buffer.GetEnumerator();
|
|
|
|
// First iteration
|
|
Assert.True(enumerator.MoveNext());
|
|
Assert.Equal(10.0, enumerator.Current);
|
|
Assert.True(enumerator.MoveNext());
|
|
Assert.Equal(20.0, enumerator.Current);
|
|
Assert.False(enumerator.MoveNext());
|
|
|
|
// Reset and iterate again
|
|
enumerator.Reset();
|
|
Assert.True(enumerator.MoveNext());
|
|
Assert.Equal(10.0, enumerator.Current);
|
|
|
|
enumerator.Dispose(); // Coverage for Dispose
|
|
}
|
|
|
|
[Fact]
|
|
public void IEnumerable_GetEnumerator_Works()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
|
|
IEnumerable<double> enumerable = buffer;
|
|
List<double> values = [];
|
|
foreach (var v in enumerable)
|
|
{
|
|
values.Add(v);
|
|
}
|
|
|
|
Assert.Equal(2, values.Count);
|
|
Assert.Equal(10.0, values[0]);
|
|
Assert.Equal(20.0, values[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void IEnumerable_NonGeneric_GetEnumerator_Works()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
|
|
IEnumerable enumerable = buffer;
|
|
List<double> values = [];
|
|
foreach (var v in enumerable)
|
|
{
|
|
values.Add((double)v);
|
|
}
|
|
|
|
Assert.Equal(2, values.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void Indexer_Set_AfterWrap_UpdatesCorrectly()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
buffer.Add(40.0); // Wraps - now has 20, 30, 40
|
|
|
|
buffer[0] = 25.0; // Change oldest (20.0) to 25.0
|
|
|
|
Assert.Equal(95.0, buffer.Sum); // 25 + 30 + 40
|
|
Assert.Equal(25.0, buffer[0]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Add_WithIsNew_EmptyBuffer_AddsValue()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0, isNew: false); // isNew=false but buffer empty, should still add
|
|
|
|
Assert.Equal(1, buffer.Count);
|
|
Assert.Equal(10.0, buffer.Newest);
|
|
}
|
|
|
|
[Fact]
|
|
public void BarCorrection_Workflow()
|
|
{
|
|
// Simulate bar correction (isNew=false) workflow
|
|
var buffer = new RingBuffer(3);
|
|
var backup = new RingBuffer(3);
|
|
|
|
// Add values as new bars
|
|
buffer.Add(10.0);
|
|
backup.CopyFrom(buffer);
|
|
|
|
buffer.Add(20.0);
|
|
backup.CopyFrom(buffer);
|
|
|
|
buffer.Add(30.0);
|
|
backup.CopyFrom(buffer);
|
|
|
|
double avgBeforeCorrection = buffer.Average;
|
|
|
|
// Simulate correction (isNew=false)
|
|
buffer.CopyFrom(backup); // Restore previous state
|
|
buffer.UpdateNewest(35.0); // Update with corrected value
|
|
|
|
// Average should reflect the correction
|
|
Assert.Equal(21.666666666666668, buffer.Average, 1e-10);
|
|
Assert.NotEqual(avgBeforeCorrection, buffer.Average);
|
|
}
|
|
|
|
[Fact]
|
|
public void Snapshot_CapturesCurrentState()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
buffer.Snapshot();
|
|
|
|
// Modify buffer after snapshot
|
|
buffer.Add(40.0);
|
|
|
|
Assert.Equal(4, buffer.Count);
|
|
Assert.Equal(100.0, buffer.Sum); // 10 + 20 + 30 + 40
|
|
}
|
|
|
|
[Fact]
|
|
public void Restore_ReturnsToSnapshotState()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
buffer.Snapshot();
|
|
double sumBeforeModification = buffer.Sum;
|
|
int countBeforeModification = buffer.Count;
|
|
|
|
// Modify buffer after snapshot
|
|
buffer.Add(40.0);
|
|
Assert.Equal(4, buffer.Count);
|
|
|
|
// Restore to snapshot state
|
|
buffer.Restore();
|
|
|
|
Assert.Equal(countBeforeModification, buffer.Count);
|
|
Assert.Equal(sumBeforeModification, buffer.Sum);
|
|
}
|
|
|
|
[Fact]
|
|
public void Snapshot_Restore_WithWrapping()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
buffer.Snapshot();
|
|
|
|
// Add value that causes wrap
|
|
buffer.Add(40.0);
|
|
Assert.Equal(90.0, buffer.Sum); // 20 + 30 + 40
|
|
|
|
buffer.Restore();
|
|
|
|
Assert.Equal(60.0, buffer.Sum); // 10 + 20 + 30
|
|
Assert.Equal(30.0, buffer.Newest);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecalculateSum_CorrectsDrift()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
double recalculated = buffer.RecalculateSum();
|
|
|
|
Assert.Equal(60.0, recalculated);
|
|
Assert.Equal(60.0, buffer.Sum);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecalculateSum_AfterMultipleOperations()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
// Simulate many operations that could accumulate floating-point drift
|
|
for (int i = 0; i < 100; i++)
|
|
{
|
|
buffer.Add(i * 0.1);
|
|
}
|
|
|
|
double recalculated = buffer.RecalculateSum();
|
|
|
|
// Should be equal (or very close) since we're using exact values
|
|
Assert.Equal(recalculated, buffer.Sum);
|
|
}
|
|
|
|
[Fact]
|
|
public void StartIndex_EmptyBuffer_ReturnsZero()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
Assert.Equal(0, buffer.StartIndex);
|
|
}
|
|
|
|
[Fact]
|
|
public void StartIndex_PartiallyFilled_ReturnsZero()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
|
|
Assert.Equal(0, buffer.StartIndex);
|
|
}
|
|
|
|
[Fact]
|
|
public void StartIndex_FullBuffer_ReturnsHead()
|
|
{
|
|
var buffer = new RingBuffer(3);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
buffer.Add(40.0); // Wraps
|
|
|
|
// StartIndex should point to oldest element
|
|
Assert.True(buffer.StartIndex >= 0 && buffer.StartIndex < buffer.Capacity);
|
|
Assert.Equal(20.0, buffer.Oldest);
|
|
}
|
|
|
|
[Fact]
|
|
public void Indexer_NegativeIndexViaFromEnd_ThrowsWhenOutOfBounds()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
// ^4 when count=3 should throw
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => _ = buffer[^4]);
|
|
}
|
|
|
|
[Fact]
|
|
public void CopyTo_InsufficientDestinationBuffer_Behavior()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
buffer.Add(30.0);
|
|
|
|
var dest = new double[2]; // Too small
|
|
|
|
// This will throw IndexOutOfRangeException since we're copying 3 elements to size-2 array
|
|
Assert.Throws<ArgumentException>(() => buffer.CopyTo(dest, 0));
|
|
}
|
|
|
|
[Fact]
|
|
public void CopyTo_StartIndexOutOfRange_Behavior()
|
|
{
|
|
var buffer = new RingBuffer(5);
|
|
|
|
buffer.Add(10.0);
|
|
buffer.Add(20.0);
|
|
|
|
var dest = new double[5];
|
|
|
|
// Starting at index 4 with 2 elements should fail
|
|
Assert.Throws<ArgumentException>(() => buffer.CopyTo(dest, 4));
|
|
}
|
|
}
|