修复测试用例错误
This commit is contained in:
@@ -194,101 +194,101 @@ namespace XplorePlane.Tests.ViewModels
|
||||
// ─── Event frequency statistics ──────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 触发状态变更事件后,对应指标的 EventsPerSecond 应在计时器触发后更新
|
||||
/// After raising a state-change event, EventsPerSecond should update on the next timer tick
|
||||
/// 触发状态变更事件后,事件应被记录到内部队列
|
||||
/// After raising a state-change event, it should be recorded in the internal queue
|
||||
/// Validates: Requirement 9.1
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task StateChangeEvent_UpdatesEventsPerSecond_OnTimerTick()
|
||||
public void StateChangeEvent_RecordsEventInQueue()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
vm.Initialize();
|
||||
|
||||
// Raise a MotionState event
|
||||
RaiseMotionStateEvent();
|
||||
|
||||
// Wait for the 1-second timer to fire (with buffer)
|
||||
await Task.Delay(1500);
|
||||
DoEvents();
|
||||
// Verify the event was enqueued (check via reflection)
|
||||
var queueField = typeof(PerformanceMonitorViewModel)
|
||||
.GetField("_eventQueue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
var queue = queueField?.GetValue(vm) as System.Collections.Concurrent.ConcurrentQueue<(string Category, DateTime Timestamp)>;
|
||||
|
||||
var motionMetric = vm.Metrics.FirstOrDefault(m => m.StateType == "MotionState");
|
||||
Assert.NotNull(motionMetric);
|
||||
// At least one event should have been counted in the last second
|
||||
Assert.True(motionMetric.EventsPerSecond >= 0,
|
||||
"EventsPerSecond should be non-negative after a state change event");
|
||||
Assert.NotNull(queue);
|
||||
Assert.False(queue.IsEmpty, "Event queue should contain at least one entry after raising a state-change event");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 多次触发同一类型事件后,频率应反映事件数量
|
||||
/// Multiple events of the same type should be reflected in the frequency metric
|
||||
/// 多次触发同一类型事件后,队列中应有对应数量的记录
|
||||
/// Multiple events of the same type should all be recorded in the queue
|
||||
/// Validates: Requirement 9.1
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task MultipleStateChangeEvents_AreCountedInFrequency()
|
||||
public void MultipleStateChangeEvents_AreAllRecordedInQueue()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
vm.Initialize();
|
||||
|
||||
// Raise 5 MotionState events in quick succession
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
RaiseMotionStateEvent();
|
||||
}
|
||||
|
||||
// Wait for the timer tick
|
||||
await Task.Delay(1500);
|
||||
DoEvents();
|
||||
var queueField = typeof(PerformanceMonitorViewModel)
|
||||
.GetField("_eventQueue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
var queue = queueField?.GetValue(vm) as System.Collections.Concurrent.ConcurrentQueue<(string Category, DateTime Timestamp)>;
|
||||
|
||||
var motionMetric = vm.Metrics.FirstOrDefault(m => m.StateType == "MotionState");
|
||||
Assert.NotNull(motionMetric);
|
||||
Assert.True(motionMetric.EventsPerSecond >= 0);
|
||||
Assert.NotNull(queue);
|
||||
Assert.True(queue.Count >= 5, $"Queue should have at least 5 entries, but has {queue.Count}");
|
||||
}
|
||||
|
||||
// ─── Latency monitoring ───────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 触发状态变更事件后,AverageLatency 应被更新为非负值
|
||||
/// After a state-change event, AverageLatency should be updated to a non-negative value
|
||||
/// 触发状态变更事件后,延迟历史应被更新(通过反射验证)
|
||||
/// After a state-change event, latency history should be updated (verified via reflection)
|
||||
/// Validates: Requirement 9.4
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task StateChangeEvent_UpdatesAverageLatency()
|
||||
public void StateChangeEvent_RecordsTimestampForLatencyCalculation()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
vm.Initialize();
|
||||
|
||||
var before = DateTime.UtcNow;
|
||||
RaiseMotionStateEvent();
|
||||
var after = DateTime.UtcNow;
|
||||
|
||||
// Allow the dispatcher BeginInvoke to run
|
||||
await Task.Delay(200);
|
||||
DoEvents();
|
||||
// Verify the event was enqueued with a timestamp in the expected range
|
||||
var queueField = typeof(PerformanceMonitorViewModel)
|
||||
.GetField("_eventQueue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
var queue = queueField?.GetValue(vm) as System.Collections.Concurrent.ConcurrentQueue<(string Category, DateTime Timestamp)>;
|
||||
|
||||
var motionMetric = vm.Metrics.FirstOrDefault(m => m.StateType == "MotionState");
|
||||
Assert.NotNull(motionMetric);
|
||||
Assert.True(motionMetric.AverageLatency >= 0,
|
||||
"AverageLatency should be non-negative after a state change event");
|
||||
Assert.NotNull(queue);
|
||||
Assert.False(queue.IsEmpty);
|
||||
queue.TryPeek(out var entry);
|
||||
Assert.Equal("MotionState", entry.Category);
|
||||
Assert.True(entry.Timestamp >= before && entry.Timestamp <= after.AddMilliseconds(50),
|
||||
"Event timestamp should be within the expected range");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MaxLatency 应反映所有指标中的最大延迟
|
||||
/// MaxLatency should reflect the highest latency across all metrics
|
||||
/// MaxLatency 应反映所有指标中的最大延迟(通过直接注入延迟值验证)
|
||||
/// MaxLatency should reflect the highest latency across all metrics (verified by direct injection)
|
||||
/// Validates: Requirement 9.4
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task MaxLatency_ReflectsHighestMetricLatency()
|
||||
public void MaxLatency_ReflectsHighestMetricLatency_AfterDirectInjection()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
vm.Initialize();
|
||||
|
||||
// Raise events for multiple state types
|
||||
RaiseMotionStateEvent();
|
||||
RaiseRaySourceStateEvent();
|
||||
// Inject known latency values directly via the private UpdateLatencyMetric method
|
||||
var method = typeof(PerformanceMonitorViewModel)
|
||||
.GetMethod("UpdateLatencyMetric", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
await Task.Delay(200);
|
||||
DoEvents();
|
||||
method?.Invoke(vm, new object[] { "MotionState", 100.0 });
|
||||
method?.Invoke(vm, new object[] { "RaySourceState", 200.0 });
|
||||
|
||||
var expectedMax = vm.Metrics.Count > 0 ? vm.Metrics.Max(m => m.AverageLatency) : 0;
|
||||
Assert.Equal(expectedMax, vm.MaxLatency);
|
||||
Assert.Equal(200.0, vm.MaxLatency, precision: 1);
|
||||
Assert.False(vm.IsLatencyWarning, "200ms is below the 500ms threshold");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -306,59 +306,46 @@ namespace XplorePlane.Tests.ViewModels
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射注入高延迟值,验证 IsLatencyWarning 变为 true
|
||||
/// Inject a high latency value via reflection to verify IsLatencyWarning becomes true
|
||||
/// 通过直接注入高延迟值,验证 IsLatencyWarning 变为 true
|
||||
/// Inject a high latency value directly to verify IsLatencyWarning becomes true
|
||||
/// Validates: Requirement 9.5
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task IsLatencyWarning_IsTrue_WhenMaxLatencyExceeds500ms()
|
||||
public void IsLatencyWarning_IsTrue_WhenMaxLatencyExceeds500ms()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
vm.Initialize();
|
||||
|
||||
// Inject a high-latency event by manipulating the latency history via reflection
|
||||
var latencyHistoryField = typeof(PerformanceMonitorViewModel)
|
||||
.GetField("_latencyHistory", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
var latencyHistory = latencyHistoryField?.GetValue(vm) as System.Collections.Generic.Dictionary<string, System.Collections.Generic.Queue<double>>;
|
||||
var method = typeof(PerformanceMonitorViewModel)
|
||||
.GetMethod("UpdateLatencyMetric", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (latencyHistory != null && latencyHistory.ContainsKey("MotionState"))
|
||||
{
|
||||
latencyHistory["MotionState"].Enqueue(600.0); // 600ms > 500ms threshold
|
||||
}
|
||||
// Inject a latency value above the 500ms threshold
|
||||
method?.Invoke(vm, new object[] { "MotionState", 600.0 });
|
||||
|
||||
// Trigger latency update by raising an event
|
||||
RaiseMotionStateEvent();
|
||||
|
||||
await Task.Delay(200);
|
||||
DoEvents();
|
||||
|
||||
// MaxLatency should now be >= 600ms (average of 600 + new measurement)
|
||||
// IsLatencyWarning depends on whether MaxLatency > 500
|
||||
// We verify the property logic is wired correctly
|
||||
Assert.Equal(vm.MaxLatency > 500, vm.IsLatencyWarning);
|
||||
Assert.True(vm.MaxLatency > 500, $"MaxLatency should be > 500ms, was {vm.MaxLatency}");
|
||||
Assert.True(vm.IsLatencyWarning, "IsLatencyWarning should be true when MaxLatency > 500ms");
|
||||
}
|
||||
|
||||
// ─── Trend data management ────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 计时器触发后,TrendData 应增加一个数据点
|
||||
/// TrendData should gain one data point on each timer tick
|
||||
/// 计时器触发后,TrendData 应增加一个数据点(通过直接调用私有方法验证)
|
||||
/// TrendData should gain one data point when UpdateTrendData is called directly
|
||||
/// Validates: Requirement 9.2
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task TimerTick_AddsTrendDataPoint()
|
||||
public void UpdateTrendData_AddsTrendDataPoint()
|
||||
{
|
||||
var vm = CreateViewModel();
|
||||
vm.Initialize();
|
||||
|
||||
var initialCount = vm.TrendData.Count;
|
||||
|
||||
// Wait for at least one timer tick (1 second + buffer)
|
||||
await Task.Delay(1500);
|
||||
DoEvents();
|
||||
var method = typeof(PerformanceMonitorViewModel)
|
||||
.GetMethod("UpdateTrendData", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
method?.Invoke(vm, null);
|
||||
|
||||
Assert.True(vm.TrendData.Count > initialCount,
|
||||
"TrendData should have grown after a timer tick");
|
||||
Assert.Equal(initialCount + 1, vm.TrendData.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user