namespace NavisworksTransport.UnitTests.Commands { /// /// CommandBase抽象类的纯逻辑测试 /// 通过创建测试实现类来测试基础功能,不依赖Navisworks环境 /// [TestClass] public class CommandBaseTests { private TestCommand _testCommand; [TestInitialize] public void SetUp() { _testCommand = new TestCommand(); } [TestCleanup] public void TearDown() { _testCommand?.Cancel(); } #region 构造函数测试 [TestMethod] public void Constructor_Default_SetsCorrectDefaults() { // Arrange & Act var command = new TestCommand(); // Assert Assert.IsNotNull(command.CommandId, "CommandId不应该为空"); Assert.IsTrue(Guid.TryParse(command.CommandId, out _), "CommandId应该是有效的GUID"); Assert.AreEqual("TestCommand", command.DisplayName, "DisplayName应该默认为类名"); Assert.AreEqual("路径规划命令", command.Description, "Description应该有默认值"); Assert.AreEqual(CommandExecutionStatus.NotStarted, command.Status, "初始状态应该为NotStarted"); Assert.AreEqual(0, command.Progress, "初始进度应该为0"); } [TestMethod] public void Constructor_WithParameters_SetsCorrectValues() { // Arrange string expectedCommandId = "test-command-123"; string expectedDisplayName = "测试命令"; string expectedDescription = "这是一个测试命令"; // Act var command = new TestCommand(expectedCommandId, expectedDisplayName, expectedDescription); // Assert Assert.AreEqual(expectedCommandId, command.CommandId, "CommandId应该设置为指定值"); Assert.AreEqual(expectedDisplayName, command.DisplayName, "DisplayName应该设置为指定值"); Assert.AreEqual(expectedDescription, command.Description, "Description应该设置为指定值"); } [TestMethod] public void Constructor_WithNullParameters_SetsDefaults() { // Arrange & Act var command = new TestCommand(null, null, null); // Assert Assert.IsNotNull(command.CommandId, "CommandId不应该为空"); Assert.IsTrue(Guid.TryParse(command.CommandId, out _), "CommandId应该是有效的GUID"); Assert.AreEqual("TestCommand", command.DisplayName, "DisplayName应该默认为类名"); Assert.AreEqual("路径规划命令", command.Description, "Description应该有默认值"); } #endregion #region 状态管理测试 [TestMethod] public void Status_MultipleThreadsAccess_ThreadSafe() { // Arrange var command = new TestCommand(); var exceptions = new List(); var tasks = new List(); // Act - 多线程并发访问Status for (int i = 0; i < 10; i++) { tasks.Add(Task.Run(() => { try { for (int j = 0; j < 100; j++) { var status = command.Status; // 读取状态 } } catch (Exception ex) { lock (exceptions) { exceptions.Add(ex); } } })); } Task.WaitAll(tasks.ToArray()); // Assert Assert.AreEqual(0, exceptions.Count, "多线程访问Status不应该抛出异常"); } [TestMethod] public void Progress_MultipleThreadsAccess_ThreadSafe() { // Arrange var command = new TestCommand(); var exceptions = new List(); var tasks = new List(); // Act - 多线程并发访问Progress for (int i = 0; i < 10; i++) { tasks.Add(Task.Run(() => { try { for (int j = 0; j < 100; j++) { var progress = command.Progress; // 读取进度 } } catch (Exception ex) { lock (exceptions) { exceptions.Add(ex); } } })); } Task.WaitAll(tasks.ToArray()); // Assert Assert.AreEqual(0, exceptions.Count, "多线程访问Progress不应该抛出异常"); } #endregion #region 事件测试 [TestMethod] public void StatusChanged_EventRaised_WhenStatusChanges() { // Arrange var command = new TestCommand(); CommandStatusChangedEventArgs receivedEventArgs = null; command.StatusChanged += (sender, args) => receivedEventArgs = args; // Act command.SimulateStatusChangeEvent(CommandExecutionStatus.NotStarted, CommandExecutionStatus.Executing); // Assert Assert.IsNotNull(receivedEventArgs, "StatusChanged事件应该被触发"); Assert.AreEqual(CommandExecutionStatus.NotStarted, receivedEventArgs.OldStatus, "旧状态应该正确"); Assert.AreEqual(CommandExecutionStatus.Executing, receivedEventArgs.NewStatus, "新状态应该正确"); } [TestMethod] public void ProgressChanged_EventRaised_WhenProgressChanges() { // Arrange var command = new TestCommand(); CommandProgressChangedEventArgs receivedEventArgs = null; command.ProgressChanged += (sender, args) => receivedEventArgs = args; // Act command.SimulateProgressChange(50, "进度50%"); // Assert Assert.IsNotNull(receivedEventArgs, "ProgressChanged事件应该被触发"); Assert.AreEqual(50, receivedEventArgs.Progress, "进度值应该正确"); Assert.AreEqual("进度50%", receivedEventArgs.StatusMessage, "状态消息应该正确"); } #endregion #region 执行测试 [TestMethod] public async Task ExecuteAsync_Success_ReturnsSuccessResult() { // Arrange var command = new TestCommand(); command.SetShouldSucceed(true); // Act var result = await command.ExecuteAsync(); // Assert Assert.IsTrue(result.IsSuccess, "执行应该成功"); Assert.AreEqual(CommandExecutionStatus.Completed, command.Status, "状态应该为Completed"); Assert.AreEqual(100, command.Progress, "进度应该为100"); } [TestMethod] public async Task ExecuteAsync_Failure_ReturnsFailureResult() { // Arrange var command = new TestCommand(); command.SetShouldSucceed(false); // Act var result = await command.ExecuteAsync(); // Assert Assert.IsFalse(result.IsSuccess, "执行应该失败"); Assert.AreEqual(CommandExecutionStatus.Failed, command.Status, "状态应该为Failed"); } [TestMethod] public async Task ExecuteAsync_ValidationFailure_ReturnsValidationFailure() { // Arrange var command = new TestCommand(); command.SetValidationShouldFail(true); // Act var result = await command.ExecuteAsync(); // Assert Assert.IsFalse(result.IsSuccess, "执行应该失败"); Assert.AreEqual(CommandExecutionStatus.Failed, command.Status, "状态应该为Failed"); Assert.IsTrue(result.ErrorMessage.Contains("验证失败"), "错误消息应该包含验证失败信息"); } [TestMethod] public async Task ExecuteAsync_CancellationRequested_ReturnsCancelledResult() { // Arrange var command = new TestCommand(); command.SetExecutionDelay(TimeSpan.FromSeconds(2)); // 设置较长的执行时间 var cts = new CancellationTokenSource(); // Act var executeTask = command.ExecuteAsync(cts.Token); cts.CancelAfter(100); // 100ms后取消 var result = await executeTask; // Assert Assert.IsFalse(result.IsSuccess, "执行应该失败"); Assert.AreEqual(CommandExecutionStatus.Cancelled, command.Status, "状态应该为Cancelled"); Assert.IsTrue(result.ErrorMessage.Contains("取消"), "错误消息应该包含取消信息"); } [TestMethod] public async Task ExecuteAsync_AlreadyExecuting_ReturnsFailure() { // Arrange var command = new TestCommand(); command.SetExecutionDelay(TimeSpan.FromSeconds(1)); // Act var task1 = command.ExecuteAsync(); await Task.Delay(50); // 确保第一个任务开始执行 var result2 = await command.ExecuteAsync(); // 尝试重复执行 // Assert Assert.IsFalse(result2.IsSuccess, "重复执行应该失败"); Assert.IsTrue(result2.ErrorMessage.Contains("正在执行中"), "错误消息应该指示命令正在执行"); // 等待第一个任务完成 await task1; } [TestMethod] public void Cancel_WhileExecuting_CancelsExecution() { // Arrange var command = new TestCommand(); command.SetExecutionDelay(TimeSpan.FromSeconds(2)); // Act var executeTask = command.ExecuteAsync(); command.Cancel(); // 取消执行 // Assert - 由于是异步操作,我们只验证Cancel方法不抛出异常 Assert.IsTrue(true, "Cancel方法应该能够正常调用"); } #endregion #region CanExecute测试 [TestMethod] public void CanExecute_DefaultImplementation_ReturnsSuccess() { // Arrange var command = new TestCommand(); // Act var result = command.CanExecute(); // Assert Assert.IsTrue(result.IsSuccess, "默认的CanExecute应该返回成功"); } [TestMethod] public void CanExecute_WhileExecuting_ReturnsFailure() { // Arrange var command = new TestCommand(); command.SetExecutionDelay(TimeSpan.FromSeconds(1)); // Act var executeTask = command.ExecuteAsync(); var canExecuteResult = command.CanExecute(); // Assert Assert.IsFalse(canExecuteResult.IsSuccess, "执行中的命令CanExecute应该返回失败"); // 清理 command.Cancel(); } #endregion #region 性能测试 [TestMethod] public async Task ExecuteAsync_TracksElapsedTime() { // Arrange var command = new TestCommand(); command.SetExecutionDelay(TimeSpan.FromMilliseconds(100)); // Act var result = await command.ExecuteAsync(); // Assert Assert.IsTrue(result.ElapsedMilliseconds >= 90, "执行时间应该被正确记录"); // 允许一些时间误差 Assert.IsTrue(result.ElapsedMilliseconds < 500, "执行时间不应该过长"); } #endregion } #region 测试用Command实现 /// /// 用于测试的Command实现类 /// internal class TestCommand : CommandBase { private bool _shouldSucceed = true; private bool _validationShouldFail = false; private TimeSpan _executionDelay = TimeSpan.Zero; public TestCommand() : base() { } public TestCommand(string commandId, string displayName, string description) : base(commandId, displayName, description) { } public void SetShouldSucceed(bool shouldSucceed) { _shouldSucceed = shouldSucceed; } public void SetValidationShouldFail(bool shouldFail) { _validationShouldFail = shouldFail; } public void SetExecutionDelay(TimeSpan delay) { _executionDelay = delay; } // 暴露受保护的方法用于测试 public void SimulateProgressChange(int progress, string message) { UpdateProgress(progress, message); } public void SimulateStatusChangeEvent(CommandExecutionStatus oldStatus, CommandExecutionStatus newStatus) { OnStatusChanged(oldStatus, newStatus, "测试状态变化"); } protected override async Task ValidateAsync(CancellationToken cancellationToken) { if (_validationShouldFail) { return PathPlanningResult.ValidationFailure("测试验证失败"); } return PathPlanningResult.Success("验证成功"); } protected override async Task ExecuteInternalAsync(CancellationToken cancellationToken) { // 模拟执行延迟 if (_executionDelay > TimeSpan.Zero) { await Task.Delay(_executionDelay, cancellationToken); } // 模拟进度更新 for (int i = 10; i <= 90; i += 20) { if (cancellationToken.IsCancellationRequested) break; UpdateProgress(i, $"执行进度 {i}%"); await Task.Delay(10, cancellationToken); // 短暂延迟以模拟工作 } if (_shouldSucceed) { return PathPlanningResult.Success("测试执行成功"); } else { return PathPlanningResult.Failure("测试执行失败"); } } } #endregion }