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
}