using Moq; using System; using System.Linq; using System.Windows.Threading; using Xunit; using XP.Common.Logging.Interfaces; using XplorePlane.Models; using XplorePlane.Services.AppState; using XplorePlane.ViewModels.Debug; namespace XplorePlane.Tests.ViewModels { /// /// EventLogViewModel 单元测试 /// Unit tests for EventLogViewModel /// public class EventLogViewModelTests { private readonly Mock _mockAppStateService; private readonly Mock _mockLoggerService; private readonly Dispatcher _dispatcher; public EventLogViewModelTests() { _mockAppStateService = new Mock(); _mockLoggerService = new Mock(); // 创建 Dispatcher(需要在 STA 线程上) // Create Dispatcher (needs to be on STA thread) _dispatcher = Dispatcher.CurrentDispatcher; // 设置 logger mock 返回自身(链式调用) // Setup logger mock to return itself (method chaining) _mockLoggerService.Setup(l => l.ForModule()) .Returns(_mockLoggerService.Object); } [Fact] public void Constructor_InitializesFilterOptionsWithAllStatesEnabled() { // Arrange & Act var viewModel = new EventLogViewModel( _mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); // Assert Assert.Equal(8, viewModel.FilterOptions.Count); Assert.True(viewModel.FilterOptions["MotionState"]); Assert.True(viewModel.FilterOptions["RaySourceState"]); Assert.True(viewModel.FilterOptions["DetectorState"]); Assert.True(viewModel.FilterOptions["SystemState"]); Assert.True(viewModel.FilterOptions["CameraState"]); Assert.True(viewModel.FilterOptions["LinkedViewState"]); Assert.True(viewModel.FilterOptions["RecipeExecutionState"]); Assert.True(viewModel.FilterOptions["CalibrationMatrix"]); } [Fact] public void ToggleFilterCommand_TogglesFilterState() { // Arrange var viewModel = new EventLogViewModel( _mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); var initialState = viewModel.FilterOptions["MotionState"]; // Act viewModel.ToggleFilterCommand.Execute("MotionState"); // Assert Assert.Equal(!initialState, viewModel.FilterOptions["MotionState"]); } [Fact] public void SelectAllFiltersCommand_EnablesAllFilters() { // Arrange var viewModel = new EventLogViewModel( _mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); // 先禁用所有过滤器 // First disable all filters viewModel.ClearAllFiltersCommand.Execute(); // Act viewModel.SelectAllFiltersCommand.Execute(); // Assert Assert.All(viewModel.FilterOptions.Values, value => Assert.True(value)); } [Fact] public void ClearAllFiltersCommand_DisablesAllFilters() { // Arrange var viewModel = new EventLogViewModel( _mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); // Act viewModel.ClearAllFiltersCommand.Execute(); // Assert Assert.All(viewModel.FilterOptions.Values, value => Assert.False(value)); } [Fact] public void ApplyFilter_FiltersEventLogCorrectly() { // Arrange var viewModel = new EventLogViewModel( _mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); // 添加测试数据 // Add test data viewModel.EventLog.Add(new EventLogEntry { Timestamp = DateTime.Now, EventType = "MotionStateChanged", FieldName = "StageX", OldValue = "0", NewValue = "100", Category = "MotionState" }); viewModel.EventLog.Add(new EventLogEntry { Timestamp = DateTime.Now, EventType = "RaySourceStateChanged", FieldName = "IsOn", OldValue = "False", NewValue = "True", Category = "RaySourceState" }); // 禁用 RaySourceState 过滤器 // Disable RaySourceState filter viewModel.FilterOptions["RaySourceState"] = false; // Act viewModel.ToggleFilterCommand.Execute("MotionState"); // 触发过滤器应用 viewModel.ToggleFilterCommand.Execute("MotionState"); // 恢复状态 // Assert Assert.Single(viewModel.FilteredEventLog); Assert.Equal("MotionState", viewModel.FilteredEventLog[0].Category); } [Fact] public void EventLog_LimitsTo1000Records() { // Arrange var viewModel = new EventLogViewModel( _mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); // Act - 添加 1001 条记录 // Act - Add 1001 records for (int i = 0; i < 1001; i++) { viewModel.EventLog.Add(new EventLogEntry { Timestamp = DateTime.Now, EventType = "MotionStateChanged", FieldName = "StageX", OldValue = i.ToString(), NewValue = (i + 1).ToString(), Category = "MotionState" }); } // Assert Assert.Equal(1000, viewModel.EventLog.Count); } [Fact] public void Dispose_ClearsCollections() { // Arrange var viewModel = new EventLogViewModel( _mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); viewModel.EventLog.Add(new EventLogEntry { Timestamp = DateTime.Now, EventType = "MotionStateChanged", FieldName = "StageX", OldValue = "0", NewValue = "100", Category = "MotionState" }); // Act viewModel.Dispose(); // Assert Assert.Empty(viewModel.EventLog); Assert.Empty(viewModel.FilteredEventLog); } } }