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 { public class SnapshotManagerViewModelTests { private readonly Mock _mockAppStateService; private readonly Mock _mockLoggerService; private readonly Mock _mockLogger; private readonly Dispatcher _dispatcher; public SnapshotManagerViewModelTests() { _mockAppStateService = new Mock(); _mockLoggerService = new Mock(); _mockLogger = new Mock(); _mockLoggerService.Setup(x => x.ForModule()).Returns(_mockLogger.Object); // Create a dispatcher for the current thread _dispatcher = Dispatcher.CurrentDispatcher; } [Fact] public void UpdateSnapshotDetails_WhenSnapshotSelected_ShouldPopulateSnapshotDetails() { // Arrange var viewModel = new SnapshotManagerViewModel(_mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); var snapshot = new StateSnapshot { Id = Guid.NewGuid(), Timestamp = DateTime.Now, MotionState = new MotionState( StageX: 100.5, StageY: 200.3, SourceZ: 300.1, DetectorZ: 400.2, DetectorSwing: 45.0, FDD: 1000.0, StageXSpeed: 10.0, StageYSpeed: 20.0, SourceZSpeed: 30.0, DetectorZSpeed: 40.0, DetectorSwingSpeed: 5.0, FDDSpeed: 50.0, StageRotation: 0.0, FixtureRotation: 0.0, FOD: 500.0, Magnification: 2.0, StageRotationSpeed: 0.0, FixtureRotationSpeed: 0.0 ), RaySourceState = new RaySourceState(IsOn: true, Voltage: 160.0, Power: 8.0), DetectorState = new DetectorState(IsConnected: true, IsAcquiring: false, FrameRate: 30.0, Resolution: "1920x1080"), SystemState = new SystemState(OperationMode: OperationMode.Idle, HasError: false, ErrorMessage: ""), CameraState = new CameraState(IsConnected: true, IsStreaming: false, CurrentFrame: null, Width: 1920, Height: 1080, FrameRate: 30.0), LinkedViewState = new LinkedViewState(TargetPosition: new PhysicalPosition(0, 0, 0), IsExecuting: false, LastRequestTime: DateTime.Now), RecipeExecutionState = new RecipeExecutionState(CurrentStepIndex: 0, TotalSteps: 10, Status: RecipeExecutionStatus.Idle, CurrentRecipeName: "Test Recipe"), CalibrationMatrix = new CalibrationMatrix(1, 0, 0, 0, 1, 0, 0, 0, 1) }; var snapshotViewModel = new SnapshotViewModel { Snapshot = snapshot }; // Act viewModel.SelectedSnapshot = snapshotViewModel; // Assert Assert.NotEmpty(viewModel.SnapshotDetails); Assert.Equal(8, viewModel.SnapshotDetails.Count); // Should have 8 root nodes (one for each state type) // Verify MotionState node exists and has children var motionStateNode = viewModel.SnapshotDetails.FirstOrDefault(n => n.Name == "MotionState"); Assert.NotNull(motionStateNode); Assert.NotEmpty(motionStateNode.Children); // Verify specific field values var stageXNode = motionStateNode.Children.FirstOrDefault(c => c.Name == "StageX"); Assert.NotNull(stageXNode); Assert.Equal("100.50", stageXNode.Value); // Verify RaySourceState node exists var raySourceNode = viewModel.SnapshotDetails.FirstOrDefault(n => n.Name == "RaySourceState"); Assert.NotNull(raySourceNode); Assert.NotEmpty(raySourceNode.Children); } [Fact] public void UpdateSnapshotDetails_WhenNoSnapshotSelected_ShouldClearSnapshotDetails() { // Arrange var viewModel = new SnapshotManagerViewModel(_mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); // Add a snapshot first var snapshot = new StateSnapshot { Id = Guid.NewGuid(), Timestamp = DateTime.Now, MotionState = new MotionState(100, 200, 300, 400, 45, 1000, 10, 20, 30, 40, 5, 50, 0, 0, 500, 2, 0, 0), RaySourceState = new RaySourceState(true, 160, 8), DetectorState = new DetectorState(true, false, 30, "1920x1080"), SystemState = new SystemState(OperationMode.Idle, false, ""), CameraState = new CameraState(true, false, null, 1920, 1080, 30), LinkedViewState = new LinkedViewState(new PhysicalPosition(0, 0, 0), false, DateTime.Now), RecipeExecutionState = new RecipeExecutionState(0, 10, RecipeExecutionStatus.Idle, "Test"), CalibrationMatrix = new CalibrationMatrix(1, 0, 0, 0, 1, 0, 0, 0, 1) }; viewModel.SelectedSnapshot = new SnapshotViewModel { Snapshot = snapshot }; Assert.NotEmpty(viewModel.SnapshotDetails); // Act - Deselect snapshot viewModel.SelectedSnapshot = null; // Assert Assert.Empty(viewModel.SnapshotDetails); } [Fact] public void CaptureSnapshot_ShouldCreateSnapshotAndSelectIt() { // Arrange var motionState = new MotionState(100, 200, 300, 400, 45, 1000, 10, 20, 30, 40, 5, 50, 0, 0, 500, 2, 0, 0); var raySourceState = new RaySourceState(true, 160, 8); var detectorState = new DetectorState(true, false, 30, "1920x1080"); var systemState = new SystemState(OperationMode.Idle, false, ""); var cameraState = new CameraState(true, false, null, 1920, 1080, 30); var linkedViewState = new LinkedViewState(new PhysicalPosition(0, 0, 0), false, DateTime.Now); var recipeState = new RecipeExecutionState(0, 10, RecipeExecutionStatus.Idle, "Test"); var calibMatrix = new CalibrationMatrix(1, 0, 0, 0, 1, 0, 0, 0, 1); _mockAppStateService.Setup(x => x.MotionState).Returns(motionState); _mockAppStateService.Setup(x => x.RaySourceState).Returns(raySourceState); _mockAppStateService.Setup(x => x.DetectorState).Returns(detectorState); _mockAppStateService.Setup(x => x.SystemState).Returns(systemState); _mockAppStateService.Setup(x => x.CameraState).Returns(cameraState); _mockAppStateService.Setup(x => x.LinkedViewState).Returns(linkedViewState); _mockAppStateService.Setup(x => x.RecipeExecutionState).Returns(recipeState); _mockAppStateService.Setup(x => x.CalibrationMatrix).Returns(calibMatrix); var viewModel = new SnapshotManagerViewModel(_mockAppStateService.Object, _mockLoggerService.Object, _dispatcher); // Act viewModel.CaptureSnapshot(); // Assert Assert.Single(viewModel.Snapshots); Assert.NotNull(viewModel.SelectedSnapshot); Assert.Equal(viewModel.Snapshots[0], viewModel.SelectedSnapshot); Assert.NotEmpty(viewModel.SnapshotDetails); // Details should be populated for selected snapshot } } }