using Moq; using Prism.Events; using System; using System.Windows; using XP.Common.Logging.Interfaces; using XP.Hardware.MotionControl.Abstractions; using XP.Hardware.MotionControl.Abstractions.Enums; using XP.Hardware.MotionControl.Abstractions.Events; using XP.Hardware.MotionControl.Services; using XP.Hardware.RaySource.Services; using XplorePlane.Models; using XplorePlane.Services.AppState; using Xunit; using Xunit.Abstractions; namespace XplorePlane.Tests.Services { /// /// AppStateService unit tests. /// Verifies default values, null guards, dispose behavior, and hardware-driven motion-state sync. /// public class AppStateServiceTests : IDisposable { private readonly AppStateService _service; private readonly Mock _mockRaySource; private readonly Mock _mockMotionSystem; private readonly Mock _mockMotionControlService; private readonly Mock _mockStageX; private readonly Mock _mockStageY; private readonly Mock _mockSourceZ; private readonly Mock _mockDetectorZ; private readonly Mock _mockDetectorSwing; private readonly Mock _mockStageRotation; private readonly Mock _mockFixtureRotation; private readonly Mock _mockLogger; private readonly EventAggregator _eventAggregator; private readonly ITestOutputHelper _output; public AppStateServiceTests(ITestOutputHelper output) { _output = output; if (Application.Current == null) { new Application(); } _mockRaySource = new Mock(); _mockMotionSystem = new Mock(); _mockMotionControlService = new Mock(); _mockStageX = CreateLinearAxis(AxisId.StageX, 0); _mockStageY = CreateLinearAxis(AxisId.StageY, 0); _mockSourceZ = CreateLinearAxis(AxisId.SourceZ, 0); _mockDetectorZ = CreateLinearAxis(AxisId.DetectorZ, 0); _mockDetectorSwing = CreateRotaryAxis(RotaryAxisId.DetectorSwing, 0); _mockStageRotation = CreateRotaryAxis(RotaryAxisId.StageRotation, 0); _mockFixtureRotation = CreateRotaryAxis(RotaryAxisId.FixtureRotation, 0); _mockLogger = new Mock(); _eventAggregator = new EventAggregator(); _mockMotionSystem.Setup(x => x.GetLinearAxis(AxisId.StageX)).Returns(_mockStageX.Object); _mockMotionSystem.Setup(x => x.GetLinearAxis(AxisId.StageY)).Returns(_mockStageY.Object); _mockMotionSystem.Setup(x => x.GetLinearAxis(AxisId.SourceZ)).Returns(_mockSourceZ.Object); _mockMotionSystem.Setup(x => x.GetLinearAxis(AxisId.DetectorZ)).Returns(_mockDetectorZ.Object); _mockMotionSystem.Setup(x => x.GetRotaryAxis(RotaryAxisId.DetectorSwing)).Returns(_mockDetectorSwing.Object); _mockMotionSystem.Setup(x => x.GetRotaryAxis(RotaryAxisId.StageRotation)).Returns(_mockStageRotation.Object); _mockMotionSystem.Setup(x => x.GetRotaryAxis(RotaryAxisId.FixtureRotation)).Returns(_mockFixtureRotation.Object); _mockMotionControlService .Setup(x => x.GetCurrentGeometry()) .Returns((0d, 0d, 1d)); _mockLogger.Setup(l => l.ForModule()).Returns(_mockLogger.Object); _service = new AppStateService( _mockRaySource.Object, _mockMotionSystem.Object, _mockMotionControlService.Object, _eventAggregator, _mockLogger.Object); } public void Dispose() { _service.Dispose(); } [Fact] public void DefaultState_MotionState_IsHardwareSnapshot() { Assert.Equal(0, _service.MotionState.StageX); Assert.Equal(0, _service.MotionState.StageY); Assert.Equal(0, _service.MotionState.SourceZ); Assert.Equal(0, _service.MotionState.DetectorZ); Assert.Equal(0, _service.MotionState.DetectorSwing); Assert.Equal(0, _service.MotionState.FDD); } [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); } [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}"); } [Fact] public void Dispose_ThenUpdate_IsIgnored() { var originalState = _service.MotionState; _service.Dispose(); var newState = new MotionState(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); _service.UpdateMotionState(newState); Assert.Same(originalState, _service.MotionState); } [Fact] public void RequestLinkedView_NoCalibrationMatrix_SetsErrorState() { Assert.Null(_service.CalibrationMatrix); _service.RequestLinkedView(100.0, 200.0); Assert.True(_service.SystemState.HasError); Assert.NotEmpty(_service.SystemState.ErrorMessage); } [Fact] public void GeometryUpdatedEvent_RefreshesMotionStateFromHardware() { _mockStageX.SetupGet(x => x.ActualPosition).Returns(12.5); _mockStageY.SetupGet(x => x.ActualPosition).Returns(34.5); _mockSourceZ.SetupGet(x => x.ActualPosition).Returns(56.5); _mockDetectorZ.SetupGet(x => x.ActualPosition).Returns(78.5); _mockDetectorSwing.SetupGet(x => x.ActualAngle).Returns(9.5); _eventAggregator.GetEvent() .Publish(new GeometryData(100, 222.2, 2.22)); Assert.Equal(12.5, _service.MotionState.StageX); Assert.Equal(34.5, _service.MotionState.StageY); Assert.Equal(56.5, _service.MotionState.SourceZ); Assert.Equal(78.5, _service.MotionState.DetectorZ); Assert.Equal(9.5, _service.MotionState.DetectorSwing); Assert.Equal(222.2, _service.MotionState.FDD); } private static Mock CreateLinearAxis(AxisId axisId, double position) { var axis = new Mock(); axis.SetupGet(x => x.Id).Returns(axisId); axis.SetupGet(x => x.ActualPosition).Returns(position); axis.SetupGet(x => x.Status).Returns(AxisStatus.Idle); return axis; } private static Mock CreateRotaryAxis(RotaryAxisId axisId, double angle) { var axis = new Mock(); axis.SetupGet(x => x.Id).Returns(axisId); axis.SetupGet(x => x.ActualAngle).Returns(angle); axis.SetupGet(x => x.Status).Returns(AxisStatus.Idle); axis.SetupGet(x => x.Enabled).Returns(true); return axis; } } }