From 186eec4e75b983288ffc16462a342a74a2eb54c0 Mon Sep 17 00:00:00 2001 From: hsc Date: Mon, 14 Sep 2026 17:08:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9ECommandRunningTime?= =?UTF-8?q?=E6=AD=A5=E9=AA=A4=E8=80=97=E6=97=B6=E7=BB=9F=E8=AE=A1=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=EF=BC=88=E5=BA=8F=E5=8F=B7=E5=8C=BA=E9=97=B4RunTime?= =?UTF-8?q?=E6=B1=82=E5=92=8C=EF=BC=8C=E4=BE=9D=E8=B5=96=E5=80=92=E7=BD=AE?= =?UTF-8?q?+AsyncLocal=E4=BD=9C=E7=94=A8=E5=9F=9F=E9=9A=94=E7=A6=BB?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Command/CommandRunningTime.cs | 47 +++++++++++++++++++++++++++ Common/Tool/StepRuntimeContext.cs | 27 +++++++++++++++ UIShare/GlobalVariable/StepRunning.cs | 28 ++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 Command/CommandRunningTime.cs create mode 100644 Common/Tool/StepRuntimeContext.cs diff --git a/Command/CommandRunningTime.cs b/Command/CommandRunningTime.cs new file mode 100644 index 0000000..c0650e2 --- /dev/null +++ b/Command/CommandRunningTime.cs @@ -0,0 +1,47 @@ +using Common.Attributes; +using Common.Tools; +using System; + +namespace Command +{ + /// + /// 步骤运行耗时统计命令。 + /// + /// 命令库为纯 .NET 类库,不直接引用宿主的步骤管理器类型; + /// 实际的步骤数据查询能力由宿主(StepRunning)在执行测试步骤前通过 + /// 注入(依赖倒置), + /// 多台架并发运行时各作用域的查询器随异步流隔离,互不串扰。 + /// + /// + [ADPCommand] + public static class CommandRunningTime + { + /// + /// 计算测试步骤运行时间之和:统计步骤管理器主程序中, + /// 序号从起始序号到结束序号(含两端)范围内所有步骤的运行耗时总和。 + /// + /// 起始步骤序号(从 1 开始) + /// 结束步骤序号(含本身) + /// 范围内步骤运行时间之和(毫秒);序号超界、范围内无步骤或查询异常时输出 0 + public static double 获取步骤运行时间之和(int 起始序号, int 结束序号) + { + // 读取当前异步执行流注入的步骤耗时查询器(由 StepRunning 设置) + var query = StepRuntimeContext.StepTimeQuery.Value; + if (query == null) + { + Console.WriteLine($"[步骤耗时] 宿主未注入步骤耗时查询器,无法计算序号 {起始序号}~{结束序号} 的运行时间之和"); + return 0; + } + + try + { + return query(起始序号, 结束序号); + } + catch (Exception ex) + { + Console.WriteLine($"[步骤耗时] 计算序号 {起始序号}~{结束序号} 的运行时间之和失败: {ex.Message}"); + return 0; + } + } + } +} diff --git a/Common/Tool/StepRuntimeContext.cs b/Common/Tool/StepRuntimeContext.cs new file mode 100644 index 0000000..7dc618a --- /dev/null +++ b/Common/Tool/StepRuntimeContext.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel; + +namespace Common.Tools +{ + /// + /// 步骤运行时上下文桥接器(命令库与宿主之间的依赖倒置通道)。 + /// + /// 命令库(Command 项目)不引用宿主类型,无法直接访问步骤管理器的数据; + /// 宿主(StepRunning)在执行测试步骤前,将当前作用域的步骤耗时查询委托写入 + /// ;命令库中的耗时统计命令在执行时读取该委托完成查询。 + /// + /// + /// 使用 承载:委托随异步执行流(ExecutionContext)流动, + /// 多台架并发运行时各自持有自己作用域的查询器,天然隔离互不串扰。 + /// + /// + public static class StepRuntimeContext + { + /// + /// 当前异步执行流的步骤耗时查询委托:(起始序号, 结束序号) → 该范围内步骤运行时间之和(毫秒)。 + /// 由宿主(StepRunning)在每次执行测试步骤方法前设置;未设置时命令侧按未注入处理。 + /// + [Browsable(false)] + public static readonly AsyncLocal?> StepTimeQuery = new(); + } +} diff --git a/UIShare/GlobalVariable/StepRunning.cs b/UIShare/GlobalVariable/StepRunning.cs index c9ade2a..9dbe7bf 100644 --- a/UIShare/GlobalVariable/StepRunning.cs +++ b/UIShare/GlobalVariable/StepRunning.cs @@ -73,6 +73,8 @@ namespace UIShare ResetAllStepStatus(program.ErrorStepCollection); tmpParameters.Clear(); TestRoundID = Guid.NewGuid(); + // 注入步骤耗时查询器:AsyncLocal 随异步执行流流动,命令库(CommandRunningTime)据此查询当前作用域主程序的步骤耗时 + StepRuntimeContext.StepTimeQuery.Value = QueryStepRunTimeSum; } foreach (var item in program.Parameters) { @@ -261,6 +263,8 @@ namespace UIShare tmpParameters.Clear(); testItemStack.Clear(); TestRoundID = Guid.NewGuid(); + // 注入步骤耗时查询器:AsyncLocal 随异步执行流流动,命令库(CommandRunningTime)据此查询当前作用域主程序的步骤耗时 + StepRuntimeContext.StepTimeQuery.Value = QueryStepRunTimeSum; } int initialLoopStackCount = loopStack.Count; foreach (var item in program.Parameters) @@ -800,6 +804,30 @@ namespace UIShare } } + /// + /// 查询当前作用域主程序步骤集合中,序号位于 [起始序号, 结束序号](含两端)范围内步骤的运行时间之和(毫秒)。 + /// 由 ExecuteSteps / ExecuteErrorSteps 顶层启动时经 StepRuntimeContext 注入(AsyncLocal 随异步流流动), + /// 供命令库 CommandRunningTime.获取步骤运行时间之和 调用(依赖倒置:命令库不引用宿主类型)。 + /// 序号倒置、超界(范围内不存在步骤)时记录错误日志并返回 0;未执行步骤的耗时按 0 计入。 + /// + private double QueryStepRunTimeSum(int startIndex, int endIndex) + { + var steps = _scopedContext.Program.StepCollection; + if (startIndex > endIndex) + { + LoggerHelper.Error($"[步骤耗时] 查询失败:起始序号 {startIndex} 大于结束序号 {endIndex},返回 0"); + return 0; + } + var matched = steps.Where(s => s.Index >= startIndex && s.Index <= endIndex).ToList(); + if (matched.Count == 0) + { + LoggerHelper.Error($"[步骤耗时] 查询失败:序号 {startIndex}~{endIndex} 范围内不存在测试步骤(主程序共 {steps.Count} 步),返回 0"); + return 0; + } + // 尚未执行或未启用的步骤 RunTime 为 null,按 0 计入 + return matched.Sum(s => s.RunTime ?? 0); + } + private void UpdateCurrentStepResult(StepVM step, bool paraResult = true, bool stepResult = true, int depth = 0) { if (stepResult && paraResult)