|
@@ -276,7 +276,16 @@ namespace Bird_tool
|
|
// 重试次数
|
|
// 重试次数
|
|
public int RetryCount { get; set; }
|
|
public int RetryCount { get; set; }
|
|
// 当前测试项的状态
|
|
// 当前测试项的状态
|
|
- public TestStepConfig CurrentStep => Steps?[CurrentStepIndex];
|
|
|
|
|
|
+ public TestStepConfig CurrentStep
|
|
|
|
+ {
|
|
|
|
+ get
|
|
|
|
+ {
|
|
|
|
+ if (Steps == null || CurrentStepIndex < 0 || CurrentStepIndex >= Steps.Count)
|
|
|
|
+ return null;
|
|
|
|
+ return Steps[CurrentStepIndex];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
public List<TestStepConfig> Steps { get; set; }
|
|
public List<TestStepConfig> Steps { get; set; }
|
|
public List<TestStepResult> StepResults { get; } = new List<TestStepResult>();
|
|
public List<TestStepResult> StepResults { get; } = new List<TestStepResult>();
|
|
public TestReport Report { get; set; } = new TestReport();
|
|
public TestReport Report { get; set; } = new TestReport();
|
|
@@ -306,8 +315,10 @@ namespace Bird_tool
|
|
public delegate void StepChangedHandler(TestStepConfig step, TestContext context, bool isStarting);
|
|
public delegate void StepChangedHandler(TestStepConfig step, TestContext context, bool isStarting);
|
|
public delegate void TestFailHandler(TestStepConfig step, TestContext context);
|
|
public delegate void TestFailHandler(TestStepConfig step, TestContext context);
|
|
public delegate void TestSuccessHandler(TestContext context);
|
|
public delegate void TestSuccessHandler(TestContext context);
|
|
|
|
+ public delegate void TestEndHandler(bool isFail);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
public event LogHandler OnLog;
|
|
public event LogHandler OnLog;
|
|
@@ -316,6 +327,7 @@ namespace Bird_tool
|
|
public event StepChangedHandler OnStepChanged;
|
|
public event StepChangedHandler OnStepChanged;
|
|
public event TestFailHandler OnFailed;
|
|
public event TestFailHandler OnFailed;
|
|
public event TestSuccessHandler OnSuccess;
|
|
public event TestSuccessHandler OnSuccess;
|
|
|
|
+ public event TestEndHandler OnTestEnd;
|
|
|
|
|
|
private readonly SerialManager _serialManager;
|
|
private readonly SerialManager _serialManager;
|
|
private TestContext _context;
|
|
private TestContext _context;
|
|
@@ -388,25 +400,21 @@ namespace Bird_tool
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- public async Task StopTestAsync(bool waitForCompletion = false)
|
|
|
|
|
|
+ public async Task StopTestAsync(bool waitForCompletion = false, bool testEnd = false)
|
|
{
|
|
{
|
|
try
|
|
try
|
|
{
|
|
{
|
|
// 取消所有任务
|
|
// 取消所有任务
|
|
_cts.Cancel();
|
|
_cts.Cancel();
|
|
-
|
|
|
|
|
|
+ // 清空任务队列
|
|
|
|
+ OnLog?.Invoke($"_cts.Cancel();", LogLevel.error);
|
|
// 等待队列处理完成(如果需要)
|
|
// 等待队列处理完成(如果需要)
|
|
if (waitForCompletion && _queueProcessor != null && !_queueProcessor.IsCompleted)
|
|
if (waitForCompletion && _queueProcessor != null && !_queueProcessor.IsCompleted)
|
|
{
|
|
{
|
|
- try
|
|
|
|
- {
|
|
|
|
- await _queueProcessor;
|
|
|
|
- }
|
|
|
|
- catch (OperationCanceledException)
|
|
|
|
- {
|
|
|
|
- // 正常取消
|
|
|
|
- }
|
|
|
|
|
|
+ OnLog?.Invoke($"_queueProcessor", LogLevel.error);
|
|
|
|
+ await _queueProcessor;
|
|
}
|
|
}
|
|
|
|
+ OnLog?.Invoke($"_queueProcessor ok", LogLevel.error);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
catch (Exception ex)
|
|
{
|
|
{
|
|
@@ -420,17 +428,31 @@ namespace Bird_tool
|
|
_serialManager.Disconnect();
|
|
_serialManager.Disconnect();
|
|
_serialManager.OnLineReceived -= HandleResponse;
|
|
_serialManager.OnLineReceived -= HandleResponse;
|
|
}
|
|
}
|
|
|
|
+ OnLog?.Invoke($"_serialManager", LogLevel.error);
|
|
|
|
|
|
- // 重置状态
|
|
|
|
- _context = null;
|
|
|
|
- isStart = false;
|
|
|
|
|
|
+ // 重置上下文但保留配置
|
|
|
|
+ if (_context != null)
|
|
|
|
+ {
|
|
|
|
+ _context.IsRunning = false;
|
|
|
|
+ _context.CurrentStepIndex = -1;
|
|
|
|
+ _context.Report = new TestReport();
|
|
|
|
+ }
|
|
|
|
|
|
- // 重新创建 CTS 以便下次使用
|
|
|
|
- _cts.Dispose();
|
|
|
|
|
|
+ OnLog?.Invoke($"Dispose", LogLevel.debug);
|
|
|
|
+ // 重新创建资源
|
|
|
|
+ _cts?.Dispose();
|
|
_cts = new CancellationTokenSource();
|
|
_cts = new CancellationTokenSource();
|
|
-
|
|
|
|
|
|
+ OnLog?.Invoke($"StartQueueProcessor", LogLevel.debug);
|
|
// 重新启动队列处理器
|
|
// 重新启动队列处理器
|
|
|
|
+
|
|
|
|
+ OnLog?.Invoke($"停止任务完成", LogLevel.debug);
|
|
|
|
+ if (testEnd && isStart)
|
|
|
|
+ {
|
|
|
|
+ OnTestEnd?.Invoke(false);
|
|
|
|
+ }
|
|
|
|
+ isStart = false;
|
|
StartQueueProcessor();
|
|
StartQueueProcessor();
|
|
|
|
+
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -442,23 +464,31 @@ namespace Bird_tool
|
|
OnLogShow?.Invoke("串口打开失败");
|
|
OnLogShow?.Invoke("串口打开失败");
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
+ _serialManager.OnLineReceived += HandleResponse;
|
|
|
|
+
|
|
lock (_lock)
|
|
lock (_lock)
|
|
{
|
|
{
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ ResetAllSteps();
|
|
if (steps == null || steps.Count == 0)
|
|
if (steps == null || steps.Count == 0)
|
|
{
|
|
{
|
|
OnLog?.Invoke("步骤列表为空,无法启动测试", LogLevel.error);
|
|
OnLog?.Invoke("步骤列表为空,无法启动测试", LogLevel.error);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
- foreach (var step in steps)
|
|
|
|
- {
|
|
|
|
- step.JumpCount = 0;
|
|
|
|
- step.stepStatus = StepStatus.NotRun;
|
|
|
|
- step.RetryCount = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- _serialManager.OnLineReceived += HandleResponse;
|
|
|
|
|
|
+
|
|
EnqueueTask(async ct =>
|
|
EnqueueTask(async ct =>
|
|
{
|
|
{
|
|
|
|
+ if (isStart)
|
|
|
|
+ {
|
|
|
|
+ OnLog?.Invoke("测试正在进行中,请先停止当前测试", LogLevel.error);
|
|
|
|
+ StopTestAsync(true, false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (_context != null && _context.IsRunning)
|
|
|
|
+ {
|
|
|
|
+ await StopTestAsync(true, false);
|
|
|
|
+ }
|
|
// 初始化上下文
|
|
// 初始化上下文
|
|
_context = new TestContext
|
|
_context = new TestContext
|
|
{
|
|
{
|
|
@@ -475,7 +505,17 @@ namespace Bird_tool
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ private void ResetAllSteps()
|
|
|
|
+ {
|
|
|
|
+ if (_step_map == null) return;
|
|
|
|
|
|
|
|
+ foreach (var step in _step_map)
|
|
|
|
+ {
|
|
|
|
+ step.stepStatus = StepStatus.NotRun;
|
|
|
|
+ step.RetryCount = 0;
|
|
|
|
+ step.JumpCount = 0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
public bool StartTest()
|
|
public bool StartTest()
|
|
{
|
|
{
|
|
lock (_lock)
|
|
lock (_lock)
|
|
@@ -504,7 +544,8 @@ namespace Bird_tool
|
|
OnLog?.Invoke($"无法找到分组 {groupId} 的起始步骤", LogLevel.error);
|
|
OnLog?.Invoke($"无法找到分组 {groupId} 的起始步骤", LogLevel.error);
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
- // todo 从第一个id出现的位置提取分组后面的所有内容
|
|
|
|
|
|
+ OnLog?.Invoke($"找到分组ID为 {groupId} 的起始位置 {firstIndex}", LogLevel.info);
|
|
|
|
+ // 从第一个id出现的位置提取分组后面的所有内容
|
|
var stepsFromGroup = _step_map.Skip(firstIndex).ToList();
|
|
var stepsFromGroup = _step_map.Skip(firstIndex).ToList();
|
|
foreach (var step in stepsFromGroup)
|
|
foreach (var step in stepsFromGroup)
|
|
{
|
|
{
|
|
@@ -512,6 +553,7 @@ namespace Bird_tool
|
|
step.stepStatus = StepStatus.NotRun;
|
|
step.stepStatus = StepStatus.NotRun;
|
|
step.RetryCount = 0;
|
|
step.RetryCount = 0;
|
|
}
|
|
}
|
|
|
|
+
|
|
enableMakeResult = false;
|
|
enableMakeResult = false;
|
|
return StartTestWithSteps(stepsFromGroup);
|
|
return StartTestWithSteps(stepsFromGroup);
|
|
}
|
|
}
|
|
@@ -763,6 +805,11 @@ namespace Bird_tool
|
|
|
|
|
|
private async Task ExecuteCurrentStepAsync(CancellationToken ct)
|
|
private async Task ExecuteCurrentStepAsync(CancellationToken ct)
|
|
{
|
|
{
|
|
|
|
+ if (_context == null || _context.CurrentStep == null)
|
|
|
|
+ {
|
|
|
|
+ OnLog?.Invoke("无法执行步骤:上下文或步骤无效", LogLevel.error);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
var step = _context.CurrentStep;
|
|
var step = _context.CurrentStep;
|
|
RecordStepResult(step, TestStatus.Running, "步骤开始执行");
|
|
RecordStepResult(step, TestStatus.Running, "步骤开始执行");
|
|
// 记录步骤开始
|
|
// 记录步骤开始
|
|
@@ -835,7 +882,6 @@ namespace Bird_tool
|
|
// 重构响应等待逻辑
|
|
// 重构响应等待逻辑
|
|
var timeoutCts = new CancellationTokenSource(step.Timeout);
|
|
var timeoutCts = new CancellationTokenSource(step.Timeout);
|
|
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCts.Token) ;
|
|
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCts.Token) ;
|
|
- OnLog?.Invoke($"发送命令 test", LogLevel.info);
|
|
|
|
try
|
|
try
|
|
{
|
|
{
|
|
if (step.RequiresUserPrompt &&
|
|
if (step.RequiresUserPrompt &&
|
|
@@ -846,34 +892,71 @@ namespace Bird_tool
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
- OnLog?.Invoke($"等待响应(发送后延迟)", LogLevel.debug);
|
|
|
|
|
|
+ OnLog?.Invoke($"等待响应(命令已发送)", LogLevel.debug);
|
|
var response = await WaitForResponseAsync(step, linkedCts.Token);
|
|
var response = await WaitForResponseAsync(step, linkedCts.Token);
|
|
- ProcessResponse(response, step, linkedCts.Token);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested)
|
|
catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested)
|
|
{
|
|
{
|
|
HandleStepFailure("操作超时", false);
|
|
HandleStepFailure("操作超时", false);
|
|
}
|
|
}
|
|
-
|
|
|
|
- // 如果没有SuccessPattern 与 SuccessText则直接进行询问
|
|
|
|
- if (step.RequiresUserPrompt && string.IsNullOrEmpty(step.SuccessPattern) && string.IsNullOrEmpty(step.SuccessText))
|
|
|
|
- {
|
|
|
|
- await PromptQuestionAsync(step, linkedCts.Token);
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
private async Task<string> WaitForResponseAsync(TestStepConfig step, CancellationToken ct)
|
|
private async Task<string> WaitForResponseAsync(TestStepConfig step, CancellationToken ct)
|
|
{
|
|
{
|
|
var tcs = new TaskCompletionSource<string>();
|
|
var tcs = new TaskCompletionSource<string>();
|
|
- Action<string> responseHandler = (data) =>
|
|
|
|
|
|
+ Action<string> responseHandler = async (data) =>
|
|
{
|
|
{
|
|
|
|
+ if (_context == null || _context.CurrentStep != step)
|
|
|
|
+ {
|
|
|
|
+ OnLog?.Invoke($"<忽略响应> 步骤已变更", LogLevel.debug);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
if ((!string.IsNullOrEmpty(step.SuccessPattern) && Regex.IsMatch(data, step.SuccessPattern)) ||
|
|
if ((!string.IsNullOrEmpty(step.SuccessPattern) && Regex.IsMatch(data, step.SuccessPattern)) ||
|
|
- (!string.IsNullOrEmpty(step.SuccessText) && data.Contains(step.SuccessText)) ||
|
|
|
|
- (!string.IsNullOrEmpty(step.FailurePattern) && Regex.IsMatch(data, step.FailurePattern)) ||
|
|
|
|
- (!string.IsNullOrEmpty(step.FailureText) && data.Contains(step.FailureText)))
|
|
|
|
|
|
+ (!string.IsNullOrEmpty(step.SuccessText) && data.Contains(step.SuccessText)))
|
|
|
|
+ {
|
|
|
|
+ if (!string.IsNullOrEmpty(step.ExtractPattern) && !ExtractVariables(data, step))
|
|
|
|
+ {
|
|
|
|
+ if (!ExtractVariables(data, step))
|
|
|
|
+ {
|
|
|
|
+ OnLog?.Invoke($"匹配成功, 但是无法提取变量 {data} ", LogLevel.error);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 自定义验证逻辑
|
|
|
|
+ bool shouldRetry = false;
|
|
|
|
+ if (step.Validator != null)
|
|
|
|
+ {
|
|
|
|
+ shouldRetry = step.Validator(data, _context);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (shouldRetry)
|
|
|
|
+ {
|
|
|
|
+ OnLog?.Invoke("验证失败,需要重试", LogLevel.info);
|
|
|
|
+ HandleStepFailure("自定义验证失败", false);
|
|
|
|
+ tcs.TrySetResult(data);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ // 判断是否需要进行提问. 如果需要提问则先进行提问
|
|
|
|
+ if (step.RequiresUserPrompt)
|
|
|
|
+ {
|
|
|
|
+ tcs.TrySetResult(data);
|
|
|
|
+ await PromptQuestionAsync(step, ct);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ HandleSetpSuccess("匹配到成功关键词");
|
|
|
|
+ tcs.TrySetResult(data);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else if ((!string.IsNullOrEmpty(step.FailurePattern) && Regex.IsMatch(data, step.FailurePattern)) ||
|
|
|
|
+ (!string.IsNullOrEmpty(step.FailureText) && data.Contains(step.FailureText)))
|
|
{
|
|
{
|
|
|
|
+ HandleStepFailure("匹配到失败关键词", false);
|
|
tcs.TrySetResult(data);
|
|
tcs.TrySetResult(data);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
@@ -975,13 +1058,13 @@ namespace Bird_tool
|
|
{
|
|
{
|
|
HandleTestEnd(false);
|
|
HandleTestEnd(false);
|
|
OnFailed?.Invoke(_context.CurrentStep, _context);
|
|
OnFailed?.Invoke(_context.CurrentStep, _context);
|
|
- StopTestAsync();
|
|
|
|
|
|
+ StopTestAsync(false, true);
|
|
}
|
|
}
|
|
private void HandleTestSuccess()
|
|
private void HandleTestSuccess()
|
|
{
|
|
{
|
|
HandleTestEnd(true);
|
|
HandleTestEnd(true);
|
|
OnSuccess?.Invoke(_context);
|
|
OnSuccess?.Invoke(_context);
|
|
- StopTestAsync();
|
|
|
|
|
|
+ StopTestAsync(false, true);
|
|
}
|
|
}
|
|
|
|
|
|
// 替换现有的 PromptQuestion 方法
|
|
// 替换现有的 PromptQuestion 方法
|
|
@@ -1021,6 +1104,7 @@ namespace Bird_tool
|
|
|
|
|
|
private void HandleSetpSuccess(string message)
|
|
private void HandleSetpSuccess(string message)
|
|
{
|
|
{
|
|
|
|
+
|
|
_timeoutTimer?.Dispose();
|
|
_timeoutTimer?.Dispose();
|
|
_timeoutTimer = null;
|
|
_timeoutTimer = null;
|
|
var step = _context.CurrentStep;
|
|
var step = _context.CurrentStep;
|