关于 flaky-smoke-tests
flaky-smoke-tests 是用于调查 Azure DevOps Flaky Smoke Tests 流水线中 VS Code 测试间歇性失败的技能,面向 CI 维护者。它指导从任务日志和运行产物定位失败迭代、分析 UI 事件链,并针对相关平台安排验证。
主要功能
- 01定位失败的测试迭代
- 02下载并关联任务日志与产物
- 03分析输入、请求和界面渲染链
- 04查找引入问题的提交
- 05安排定向流水线验证
适用场景
- 01调查定时流水线中的冒烟测试失败
- 02区分请求未发送与结果未呈现
- 03验证修复是否消除间歇性故障
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ vscode --skill flaky-smoke-tests安装后在 Claude Code 中输入/flaky-smoke-tests确认已加载。
详细介绍
诊断不稳定的冒烟测试
遇到 Azure DevOps Flaky Smoke Tests 流水线失败时,请使用此技能:
- 组织:
https://dev.azure.com/monacotools - 项目:
Monaco - 定义 ID:
700 - 流水线 YAML:
build/azure-pipelines/product-smoke-flaky.yml - 定时运行:每天在
main上运行两次
该流水线会从源代码构建 VS Code,并针对 iterations 参数中的每一项运行一次完整的 Electron 冒烟测试套件。每个平台会在一个作业中按顺序运行各次迭代。例外情况是 Windows 单元测试和集成测试:二者分别拆分为两个并行作业:WindowsSmokeFlakyUnit_odd / WindowsSmokeFlakyIntegration_odd 运行奇数次迭代,WindowsSmokeFlakyUnit_even / WindowsSmokeFlakyIntegration_even 运行偶数次迭代。每个分片都有自己的构件(logs-windows-x64-<unit|integration>-<odd|even>-<attempt>)和独立的 Slack 统计行。
重要的流水线行为
- 每次迭代都是一个独立的时间线任务,名称为
Smoke test iteration <N>/<total> (Electron)。 - 迭代任务使用
continueOnError: true,因此失败的迭代通常结果为succeededWithIssues,后续迭代仍会继续运行。 - 不要根据整个构建或作业的结果来判断冒烟测试是否正常。检查每个迭代任务。
- 每个平台作业只会从共享的
.build/logs目录发布一次日志。构件的作用范围是作业,而不是迭代:smoke-test-runner.log可能包含多次迭代的内容,套件目录也不会按迭代区分。 - 调查期间,如果仍需要该平台作业的日志构件,请勿取消该作业。即使一个或多个失败迭代任务的日志已可用,取消也可能导致完全跳过发布步骤。如果验证运行已经确定会失败,可以让当前作业运行完成,或者排入一次诊断迭代,并让该作业完成发布后再继续调查。
- 后续迭代可能会覆盖或截断共享日志目录中的文件。因此,最终构件可能包含最后一次迭代的套件目录或运行器日志,却不包含失败迭代的详细诊断信息。请保留精确的任务日志;如果需要失败尝试中的扩展宿主、渲染器、模拟服务器、屏幕截图或跟踪证据,请运行一次迭代的诊断运行。
- 精确的迭代任务日志是其失败摘要和时间范围的权威来源。将该时间范围与作业级构件进行关联。
- 迭代可能会读取之前迭代遗留的冒烟测试状态,例如历史会话行。切勿假定看起来最新的行或匹配的提示词属于当前对话,除非已确认当前视图。
平台定义和构件:
| 平台 | 作业 | 日志构件 | 崩溃构件 |
|---|---|---|---|
| macOS arm64 | macOSSmokeFlaky | logs-macos-arm64-smoke-<attempt> | crash-dump-macos-arm64-smoke-<attempt> |
| Windows x64 | WindowsSmokeFlaky | logs-windows-x64-smoke-<attempt> | crash-dump-windows-x64-smoke-<attempt> |
| Linux x64 | LinuxSmokeFlaky | logs-linux-x64-smoke-<attempt> | crash-dump-linux-x64-smoke-<attempt> |
先决条件
使用 Azure CLI,而不是浏览器:
az --version
az extension show --name azure-devops
az devops configure --defaults \
organization=https://dev.azure.com/monacotools \
project=Monaco
如果身份验证失败,请运行 az login。
1. 确认构建和源代码修订版本
az pipelines build show \
--id <BUILD_ID> \
--org https://dev.azure.com/monacotools \
--project Monaco \
--query "{id:id,status:status,result:result,sourceBranch:sourceBranch,sourceVersion:sourceVersion,templateParameters:templateParameters}" \
--output json
记录 sourceVersion。调查流水线实际构建的代码,而不是当前工作树。
2. 查找失败的迭代
下载构建时间线:
az devops invoke \
--org https://dev.azure.com/monacotools \
--area build \
--resource timeline \
--route-parameters project=Monaco buildId=<BUILD_ID> \
--output json
筛选名称包含 Smoke test iteration 的任务记录。将结果为 succeededWithIssues、failed 或包含错误问题的任务视为失败迭代。记录:
- 任务
id - 父作业
id - 任务
name startTime和finishTimelog.idissues
如果用户提供了 Azure 日志 URL,其中的 j= 值是作业 ID,t= 值是任务 ID。直接查询这些记录:
az devops invoke \
--org https://dev.azure.com/monacotools \
--area build \
--resource timeline \
--route-parameters project=Monaco buildId=<BUILD_ID> \
--query "records[?id=='<JOB_OR_TASK_ID>'].{id:id,parentId:parentId,name:name,type:type,state:state,result:result,logId:log.id,startTime:startTime,finishTime:finishTime,issues:issues}" \
--output json
3. 下载确切迭代任务日志
使用时间线记录中的 log.id。正确的 REST 资源是 logs,而不是 buildLog:
az devops invoke \
--org https://dev.azure.com/monacotools \
--area build \
--resource logs \
--route-parameters project=Monaco buildId=<BUILD_ID> logId=<LOG_ID> \
--out-file task-log.json
下载的响应是 JSON,其中包含一个 value 数组,每行日志对应一个字符串。将其转换为纯文本。
PowerShell:
(Get-Content -Raw task-log.json | ConvertFrom-Json).value |
Set-Content task-log.txt
Bash:
jq -r '.value[]' task-log.json > task-log.txt
即使平台产物尚未发布,任务日志也会提供简明的 Mocha 失败信息、堆栈和该迭代的确切时间范围。通过 smoke runner 的 Logger 写入的诊断信息(包括 dumpFailureDiagnostics)可能只存在于 smoke-test-runner.log 中,因此不要假设它们会出现在 Azure 任务日志中。
4. 下载平台日志产物
先列出产物;名称中包含作业尝试次数:
az pipelines runs artifact list \
--run-id <BUILD_ID> \
--org https://dev.azure.com/monacotools \
--project Monaco \
--output table
下载相关平台的产物:
az pipelines runs artifact download \
--run-id <BUILD_ID> \
--artifact-name <LOGS_ARTIFACT> \
--path <DESTINATION> \
--org https://dev.azure.com/monacotools \
--project Monaco
平台作业发布其输出后,产物才可用。运行被取消后缺少平台产物是正常情况,并不表示产物名称有误。
5. 关联失败信息
从 <DESTINATION>/smoke-tests-electron/smoke-test-runner.log 开始。使用失败任务的时间戳和测试标题,找出匹配的 Test start / Test end 时间段。不要使用测试标题首次出现的位置,因为 runner 日志中可能包含多次迭代。
在该时间段内,确认以下链路:
- 手势:是否点击了预期的编辑器并输入了提示词?
- 分发:发送操作是否触发,新会话视图是否关闭?
- 请求:mock 服务是否收到了预期的场景标签?
- 工具循环:对于 shell 测试,第二次模型请求是否包含预期的工具结果?
- 渲染:助手响应是否已渲染?在哪个会话视图中?
- 路由:活动会话是否自动切换到新的编辑框,同时已完成的响应仍留在另一个会话中?
对于 mock-LLM 套件,请搜索:
- 场景 ID
request body:- 预期的响应标记
model turn 1/2和model turn 2/2
请求日志可用于区分:
- 未分发
- 提供方或场景错误
- 工具调用从未执行
- 工具结果已返回,但 UI 渲染或路由未能呈现
然后检查套件目录:
smoke-tests-electron/<N>_suite_<Suite_Name>/
有用的文件包括:
window*/exthost/<extension>/<extension>.logmain.log、renderer.log和agenthost.logcopilot-runtime-logs/process-*.log— Copilot 运行时(@github/copilotCLI)进程日志;Copilot 运行时会话失败时,dumpFailureDiagnostics会捕获这些日志。遇到**卡住或“等待响应超时”**时,先检查这些日志:它们是 SDK/CLI 自身的记录(启动、身份验证、模型请求、轮次生命周期、崩溃、乱序或协议错误),可以解释仅凭测试错误无法说明的超时。日志尾部也会镜像到smoke-test-runner.log。Agent Host 会话(Agents Window / 本地 AgentHost)会以trace级别写入完整的日志运行记录;Chat Sessions 编辑器(Copilot CLI / Claude)和本地会话在此处只写入最简启动日志(说明运行时是否已启动),详细诊断信息则在GitHub Copilot Chat.log中。(Claude / Codex 会话使用不同的运行时,不会记录在此处。)playwright-screenshot-*.png- Playwright 跟踪存档
对于原生退出或渲染器崩溃,还要下载平台崩溃产物。
6. 谨慎解读 UI 诊断信息
会话列表文本和当前视图文本回答的是不同问题:
- 列表行可能包含当前测试中的提示词,但命令或标题可能继承自先前的请求。
- 当前视图可能已经是一个全新的未命名编辑器,而响应属于一个已完成且未激活的会话。
- 范围过广的响应选择器可能匹配到较早会话或迭代中的过期 DOM。
- 重复使用固定的预热场景标记,可能导致后续预热等待匹配到较早的响应,从而放弃实际仍在运行的预热。
- 通过
installAllHandlers创建的标准冒烟测试套件会使用不同的随机用户数据目录。除非比较日志中实际的vscode-userdata:或--user-data-dir路径,否则不要将前一个套件中的设置归因于失败的套件。各次迭代会共享更广泛的作业状态,但同级套件通常不会共用同一配置文件。 - 设置变更可能改变症状,而非修复问题。每次验证运行都要重新检查确切的堆栈,以及手势、调度、请求和渲染链路。例如,将扩展启用操作移到启动前,可能消除编辑器打开超时,但也会导致扩展在写入测试设置前就被激活,从而引发后续请求或响应超时。
优先使用能够确认身份的断言:
- 每次尝试使用唯一的场景 ID 或响应标记
- 当前聊天/会话的资源属性
- 后续输入前,当前会话中已有预期响应
- 手势触发后,模拟服务器的请求计数或捕获到的请求内容
不要只通过延长超时时间来“修复”这些竞态。应等待实际状态转换,或移除重复或正在进行中的工作。
7. 找出引入问题的提交
使用构建的 sourceVersion,并根据堆栈确定源文件:
git show <SOURCE_VERSION>:test/smoke/src/areas/<area>/<test>.test.ts
git log --oneline <KNOWN_GOOD>..<SOURCE_VERSION> -- <relevant paths>
git blame -L <start>,<end> <file>
git show <SUSPECT_COMMIT> -- <relevant paths>
追踪完整的因果过程,而不只是失败的断言。例如,检查设置钩子、预热、会话选择、响应等待和拆卸。
区分:
- 引入竞态行为的提交
- 后续无关的提交,而该提交之后的时序恰好暴露了问题
- 仅添加诊断信息或使间歇性故障显现的提交
只有当差异中包含导致问题的行为,且流水线证据与之吻合时,才指出引入问题的提交。
8. 排队运行针对性验证
分支和提交必须已推送到 microsoft/vscode。排队前,检查并取消同一分支上过时的 definition-700 运行。
由于 iterations 是一个对象参数,请直接使用 az pipelines run:
az pipelines run \
--id 700 \
--branch <BRANCH> \
--commit-id <COMMIT_SHA> \
--parameters "iterations=[1,2,3,4,5,6]" \
VSCODE_BUILD_MACOS=true \
VSCODE_BUILD_LINUX=false \
VSCODE_BUILD_WIN32=true \
--org https://dev.azure.com/monacotools \
--project Monaco \
--output json
只启用与故障相关的平台。使用 az pipelines build show 确认已排队运行的 sourceVersion 和 templateParameters。
进行 6 次迭代是快速验证的实用样本。如果故障较少见,或要在宣布某个反复出现的间歇性故障已解决之前进行验证,请使用默认值 20。
9. 验证本地更改
对于冒烟测试中的 TypeScript 更改:
npm run compile --prefix test/smoke
node --experimental-strip-types build/hygiene.ts <changed-file>
如果本地依赖缺失或过时,请明确说明,并依靠针对性的 definition-700 运行进行完整的编译验证;不要悄悄跳过验证,也不要安装无关工具。
预期报告
总结:
- 构建 ID 和源修订版本
- 失败的平台和迭代次数
- 失败测试的确切名称和症状
- 调度、请求和渲染证据
- 根本原因
- 引入问题的提交,以及该提交导致问题的原因
- 修复提交
- 针对性验证运行 ID、平台、迭代次数和当前结果