using System; using System.Windows; using Moq; using XP.Common.Logging.Interfaces; using Xunit; using Xunit.Abstractions; using XP.Hardware.RaySource.Services; using XplorePlane.Models; using XplorePlane.Services.AppState; namespace XplorePlane.Tests.Services { /// /// AppStateService 单元测试。 /// 验证默认状态值、Dispose 后行为、null 参数校验、CalibrationMatrix 缺失时的错误处理。 /// public class AppStateServiceTests : IDisposable { private readonly AppStateService _service; private readonly Mock _mockRaySource; private readonly Mock _mockLogger; private readonly ITestOutputHelper _output; public AppStateServiceTests(ITestOutputHelper output) { _output = output; // Ensure WPF Application exists for Dispatcher if (Application.Current == null) { new Application(); } _mockRaySource = new Mock(); _mockLogger = new Mock(); _mockLogger.Setup(l => l.ForModule()).Returns(_mockLogger.Object); _service = new AppStateService(_mockRaySource.Object, _mockLogger.Object); } public void Dispose() { _service.Dispose(); } // ── 默认状态值验证 ── [Fact] public void DefaultState_MotionState_IsDefault() { _output.WriteLine($"MotionState == MotionState.Default: {ReferenceEquals(MotionState.Default, _service.MotionState)}"); Assert.Same(MotionState.Default, _service.MotionState); } [Fact] public void DefaultState_RaySourceState_IsDefault() { _output.WriteLine($"RaySourceState == RaySourceState.Default: {ReferenceEquals(RaySourceState.Default, _service.RaySourceState)}"); Assert.Same(RaySourceState.Default, _service.RaySourceState); } [Fact] public void DefaultState_SystemState_IsDefault() { _output.WriteLine($"SystemState == SystemState.Default: {ReferenceEquals(SystemState.Default, _service.SystemState)}"); Assert.Same(SystemState.Default, _service.SystemState); } [Fact] public void DefaultState_CalibrationMatrix_IsNull() { _output.WriteLine($"CalibrationMatrix: {_service.CalibrationMatrix?.ToString() ?? "null"}"); Assert.Null(_service.CalibrationMatrix); } // ── null 参数抛出 ArgumentNullException ── [Fact] public void UpdateMotionState_NullArgument_ThrowsArgumentNullException() { var ex = Assert.Throws(() => _service.UpdateMotionState(null!)); _output.WriteLine($"UpdateMotionState(null) threw: {ex.GetType().Name}, Param={ex.ParamName}"); } [Fact] public void UpdateRaySourceState_NullArgument_ThrowsArgumentNullException() { var ex = Assert.Throws(() => _service.UpdateRaySourceState(null!)); _output.WriteLine($"UpdateRaySourceState(null) threw: {ex.GetType().Name}, Param={ex.ParamName}"); } [Fact] public void UpdateDetectorState_NullArgument_ThrowsArgumentNullException() { var ex = Assert.Throws(() => _service.UpdateDetectorState(null!)); _output.WriteLine($"UpdateDetectorState(null) threw: {ex.GetType().Name}, Param={ex.ParamName}"); } [Fact] public void UpdateSystemState_NullArgument_ThrowsArgumentNullException() { var ex = Assert.Throws(() => _service.UpdateSystemState(null!)); _output.WriteLine($"UpdateSystemState(null) threw: {ex.GetType().Name}, Param={ex.ParamName}"); } // ── Dispose 后 Update 被忽略 ── [Fact] public void Dispose_ThenUpdate_IsIgnored() { var originalState = _service.MotionState; _service.Dispose(); _output.WriteLine("Service disposed, attempting UpdateMotionState..."); // Should not throw, and state should remain unchanged var newState = new MotionState(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); _service.UpdateMotionState(newState); _output.WriteLine($"State unchanged after dispose: {ReferenceEquals(originalState, _service.MotionState)}"); Assert.Same(originalState, _service.MotionState); } // ── CalibrationMatrix 为 null 时 RequestLinkedView 设置错误状态 ── [Fact] public void RequestLinkedView_NoCalibrationMatrix_SetsErrorState() { // CalibrationMatrix is null by default Assert.Null(_service.CalibrationMatrix); _service.RequestLinkedView(100.0, 200.0); _output.WriteLine($"RequestLinkedView(100, 200) without CalibrationMatrix: HasError={_service.SystemState.HasError}, ErrorMessage='{_service.SystemState.ErrorMessage}'"); Assert.True(_service.SystemState.HasError); Assert.NotEmpty(_service.SystemState.ErrorMessage); } } }