Files
XplorePlane/XplorePlane.Tests/Services/AppStateServiceTests.cs
T
2026-05-06 16:48:09 +08:00

205 lines
8.3 KiB
C#

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
{
/// <summary>
/// AppStateService unit tests.
/// Verifies default values, null guards, dispose behavior, and hardware-driven motion-state sync.
/// </summary>
public class AppStateServiceTests : IDisposable
{
private readonly AppStateService _service;
private readonly Mock<IRaySourceService> _mockRaySource;
private readonly Mock<IMotionSystem> _mockMotionSystem;
private readonly Mock<IMotionControlService> _mockMotionControlService;
private readonly Mock<ILinearAxis> _mockStageX;
private readonly Mock<ILinearAxis> _mockStageY;
private readonly Mock<ILinearAxis> _mockSourceZ;
private readonly Mock<ILinearAxis> _mockDetectorZ;
private readonly Mock<IRotaryAxis> _mockDetectorSwing;
private readonly Mock<ILoggerService> _mockLogger;
private readonly EventAggregator _eventAggregator;
private readonly ITestOutputHelper _output;
public AppStateServiceTests(ITestOutputHelper output)
{
_output = output;
if (Application.Current == null)
{
new Application();
}
_mockRaySource = new Mock<IRaySourceService>();
_mockMotionSystem = new Mock<IMotionSystem>();
_mockMotionControlService = new Mock<IMotionControlService>();
_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);
_mockLogger = new Mock<ILoggerService>();
_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);
_mockMotionControlService
.Setup(x => x.GetCurrentGeometry())
.Returns((0d, 0d, 1d));
_mockLogger.Setup(l => l.ForModule<AppStateService>()).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.XM);
Assert.Equal(0, _service.MotionState.YM);
Assert.Equal(0, _service.MotionState.ZT);
Assert.Equal(0, _service.MotionState.ZD);
Assert.Equal(0, _service.MotionState.TiltD);
Assert.Equal(0, _service.MotionState.Dist);
}
[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<ArgumentNullException>(() => _service.UpdateMotionState(null!));
_output.WriteLine($"UpdateMotionState(null) threw: {ex.GetType().Name}, Param={ex.ParamName}");
}
[Fact]
public void UpdateRaySourceState_NullArgument_ThrowsArgumentNullException()
{
var ex = Assert.Throws<ArgumentNullException>(() => _service.UpdateRaySourceState(null!));
_output.WriteLine($"UpdateRaySourceState(null) threw: {ex.GetType().Name}, Param={ex.ParamName}");
}
[Fact]
public void UpdateDetectorState_NullArgument_ThrowsArgumentNullException()
{
var ex = Assert.Throws<ArgumentNullException>(() => _service.UpdateDetectorState(null!));
_output.WriteLine($"UpdateDetectorState(null) threw: {ex.GetType().Name}, Param={ex.ParamName}");
}
[Fact]
public void UpdateSystemState_NullArgument_ThrowsArgumentNullException()
{
var ex = Assert.Throws<ArgumentNullException>(() => _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<GeometryUpdatedEvent>()
.Publish(new GeometryData(100, 222.2, 2.22));
Assert.Equal(12.5, _service.MotionState.XM);
Assert.Equal(34.5, _service.MotionState.YM);
Assert.Equal(56.5, _service.MotionState.ZT);
Assert.Equal(78.5, _service.MotionState.ZD);
Assert.Equal(9.5, _service.MotionState.TiltD);
Assert.Equal(222.2, _service.MotionState.Dist);
}
private static Mock<ILinearAxis> CreateLinearAxis(AxisId axisId, double position)
{
var axis = new Mock<ILinearAxis>();
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<IRotaryAxis> CreateRotaryAxis(RotaryAxisId axisId, double angle)
{
var axis = new Mock<IRotaryAxis>();
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;
}
}
}