本页内容

本页内容

测试和CI

测试运行器内部结构

共享测试状态和进程辅助工具

TypeScript 工具使用本机 typescript 包。 pnpm tsgo 通道直接解析其可执行文件;语法和语义工具使用其不稳定的 API 和明确的进程生命周期。包根导出版本元数据,而 AST、文件系统和编译器 API 位于 typescript/unstable/* 下。打包声明构建运行相同的本机编译器,并进行语义检查,并在捆绑之前验证其完整源代码和包清单收据。 tsdown 包装器默认声明一次构建为一种配置,将本机编译器进程与现有的 Node 堆预算结合起来。仅运行时构建保持其并行性,并且显式 --concurrency 值保留 tsdown 自己的行为。 Code Mode直接执行JavaScript,不使用该编译器;其 TypeScript-style 工具声明是面向模型的文档。

build-all、独立 tsdown 构建、tsgo、SDK 声明准备、包边界检查和依赖 lint 使用 .artifacts/dist-artifacts.lock 的签出本地所有权。所有权涵盖清理、生成、缓存恢复以及使用这些输出的检查;独立结帐保持独立。竞争命令打印等待消息并等待实时所有者,而没有获取截止日期。编译器和构建执行超时不变。独立的 tsgo 运行序列化,包括仅源检查;核心测试分片运行程序在一个所有者内部保留其显式并发性。当这些命令运行时,请勿手动删除 dist。突然的所有者或嵌套包装器退出,或未经验证的子清理,会保留锁。丢失或无法验证的所有者 PID,或者记录的子清理失败,会立即导致获取失败而不会回收任何内容。 PID的死亡并不能证明超然的后代就此停止。在手动删除废弃的锁定目录之前,请检查其 owner.json 并验证所有关联的构建、编译器和 lint 进程(包括分离的后代)已停止;然后重试该命令。

运行时耗时的测试通过显式构建所有者来准备签出工件,而不是通过使用 --version 启动 CLI 来准备。准备工作重用源运行程序新鲜度检查和签出工件所有权,无需更新程序服务或数据库维护托管。当前的工件不需要可写的签出或服务检查。 build-all 指纹制作投入于现有邮票中。测试准备可以在源刷新或仅测试更正之后重用一致的运行时,包括新的私有传输提交。普通 CLI 和不可变部署 HEAD 检查仍然严格; UI E2E 准备也保留其当前的头部检查。更改的生产输入、构建配置、依赖项、编译器标识或所需的缺失输出仍然需要准备。跳过静态资源的部分后期构建无法满足需要这些资源的读者。 E2E 检查之前的完整构建应使用 OPENCLAW_BUILD_PRIVATE_QA=1 pnpm build;标记到构建命令的范围,以便其工件满足最强的测试先决条件。

在编写之前,自动准备需要经过验证的工件分离或观察到的离线管理网关。在 Linux 上,它使用现有的本机管理器绑定来读取加载的命令位置,而不读取服务环境文件。本机 GetUnit 未加载结果表示未加载运行时,而不是缺少其保存的定义。卸载的保存单元不会使原本可写的源签出变得不可变;这是准入时检查,而不是服务启动排除或沙箱。物理共享的 dist 路径、不可读的工件路径、不完整的发现和未知的服务状态永远不会授予重建权限。不可变的部署和已知的实时重叠仍然被拒绝。普通单独的工作树不需要新的 CLI 标志或配置;当无法建立本机分离时,使用 现有的隔离转轮 。此检查不是沙箱。显式 pnpm build、自动源 CLI 重建和实际更新发布保留其现有准入策略。

在子加入和工件所有权解决后,Lint 在 stderr 上报告其最终失败,包括在清理不确定时保留所有权。独立的Oxlint及其分片CLI以[oxlint] FAILED (exit N)结尾; pnpm lint 拥有整个管道,并以一个 [lint] FAILED (exit N) 结束。分片进度将 passed 与 failed (exit N) 区分开来,并且 stdout 仍然可用于机器可读的工具输出。成功的运行没有失败预告片。在子执行期间转发的信号和分片超时会使命令失败;整个主机丢失或SIGKILL的报告过程可以防止最终线路。

本地插件 lint 使用 packages/plugin-sdk/dist 中的本机 SDK 声明。专用包边界编译器还使用 .artifacts/extension-package-boundary/plugins 中的七个插件 API 树。每个声明和编译所有者都会验证其使用的源内容、继承的配置、选定的编译器和完整的输出清单。不相关的现有源或测试编辑保留缓存命中。解析拓扑变化保守地无效,包括声明的根之外的新候选模块。过时的声明在清除其私有输入收据后会获得完整的本机发射;成功发出的清单会驱动过时的声明修剪。输出丢失或被篡改会使所有者失效。内容记录位于 .artifacts/extension-package-boundary 下,在打包构建清理之外。私有 .inputs.json 收据包含规范化的结账相关源和清单路径,而不是本机 .tsbuildinfo 状态。热运行会验证记录而不发出声明。

打包声明构建和包边界记录仅接受签出拥有的输入真实路径,包括编译器库、继承的配置、依赖项链接和包清单。这些路径共享 compileNativeProject,它使用固定本机编译器的异步 API 进行检查和内存中声明发射。它的文件系统回调使结账之外的候选者在本机分辨率可以读取它们之前显得丢失。直接捕获源和包清单读取;准入不依赖于解析解析痕迹。编译器版本固定在 package.json 中,因为此 API 不稳定。配置和请求的语义检查在发射之前运行。声明错误来自内存中的发出结果,避免了仅用于诊断的单独声明转换。任何错误都会阻止工件发布。

即使祖先目录包含 node_modules,嵌套物理工作树也受其自己的 pnpm install --frozen-lockfile 支持。祖先依赖项无法满足缺少的本地输入或更改发出的声明。当本地 pnpm 链接的目标停留在结帐内时,仍受支持。解析外部的本地链接仍然会失败并显示 Declaration input escapes checkout,而不会发布成功记录或修剪过时的声明。外部候选者仍然无法访问,即使它是返回结账的符号链接。温暖记录使用相同的输入检查。不要过滤编译器收据或移植声明来绕过检查。

声明的结帐联结和平台路径别名映射到同一本机根以进行验证和实际快照读取。本地声明准备还将编译器的 PWD 与其工作目录对齐,因此 shell 别名不会更改发出的清单路径。来自子目录的调用仍然使用包含签出作为所有权边界。其他pnpm tsgo通道继续使用原生CLI;它的包装器不会创建或重用共享的外部安装。

Canvas A2UI 资产构建使用共享构建工件缓存,包括当连续更新候选者使用不同签出时的 BUILD_ALL_CACHE_ROOT。缓存会一起验证并恢复两个方言包。 Canvas 源、其包清单、锁定文件、工作区依赖项设置、补丁、构建脚本、Node 版本和构建环境使重用无效;不相关的根包脚本则不会。 OPENCLAW_BUILD_CACHE=0 强制编译。显式 OPENCLAW_A2UI_BUNDLE_OUT 目标绕过共享缓存。稀疏结帐保留其现有的完整预构建捆绑行为。 Cold A2UI 默认将本机 Rayon 和 Rolldown 工作池构建为四个线程,保留显式的 RAYON_NUM_THREADS 和 ROLLDOWN_WORKER_THREADS 覆盖。

打包的 SDK 声明属于由完整版本、包版本和 ciArtifacts 版本共享的一个分阶段所有者。它在未命中时序列化两个标准 tsdown SDK 组,并缓存其完整的分阶段生成。每个成功的编译器都通过私人分阶段收据提供其源代码和包清单成员资格;缺少收据或在编译过程中更改输入会导致无法发布。共享输入快照策略可验证消耗的字节、继承的配置、生成器和清单输入以及解析拓扑,而无需在命中时启动编译器。缓存命中恢复到新的暂存中,并在发布之前通过相同的条目和相关声明关闭检查。所有 tsdown 声明构建(八个 SDK/unified 组、工作区包和 AI 包)使用相同的有界编译器作为本地声明准备和包边界检查。成功的构建需要签出拥有的编译器库和声明依赖项;不支持共享外部安装。编译器收据保留完整的源代码和包清单成员身份,包括 JSON 输入。默认类型根保留在签出范围内,显式类型根必须是本地的,并且有效的根 package.json 限制源包范围查找。

共享快照策略仍然验证消耗的字节和解析拓扑。编译期间源和命名空间的更改会阻止接受。新的本地候选模块使缓存记录无效,祖先安装的出现和删除也是如此。外部检查总是会发现丢失的文件,因此以后对祖先包内容的更改无法进入编译器的文件系统视图。每个发出的声明必须在成功的编译器成员资格中拥有一个源映射所有者。捆绑器在其原始源路径下使用这些声明;仅当其子级稳定后,私有编译器阶段才会被删除。编译器配置、本机可执行文件和 API 文件、输入字节和解析拓扑参与缓存失效。运行时模块分辨率不变。

本地准备永远不会覆盖打包的声明或写入工作区转发桥。

插件 SDK 声明准备和 scripts/run-tsgo.mjs 要求子工作在报告成功之前完成。在 POSIX 上,每个进程组都会验证自己的托管进程组:剩余的子进程将被终止,并且命令会失败,而不是允许工件标记或下游检查继续进行。 POSIX 进程组不会检测故意离开该组的后代。

在 Windows 上,托管命令和网关测试实例使用保留的内核作业。仅当请求 Windows 命令时才加载平台代码; planner 导入独立于已安装的应用程序包。加载在生成之前完成,因此侦听器注册与子级创建保持同步。工具使用 Node 内置函数解析其工作线程 URL,并重用核心中的本机作业绑定。启动器在启动命令之前加入该作业。 Cleanup等待空Job、leader退出、输出关闭;仅领导者退出并不能证明后代完成。失败的终止会报告观察到的幸存 PID,并在作业仍未解决时保留资源声明。正常的领导者退出也会终止剩余的工作成员。最终确定在关闭作业句柄之前记录其结果,包括失败时;单独关闭句柄不会验证终止或释放资源声明。拥有自己的 IPC 频道的现有调用者保留直接启动合同;即使领导者及其管道已关闭,没有所属作业的失败的任务终止仍不确定。

run-vitest(包括项目分片)、插件批次、test-live(包括实时分片)、run-vitest-profile 和 TUI PTY 观察程序通过 TMPDIR、TMP 为每个 Vitest 调用提供一个拥有的临时命名空间,和 TEMP。在 Vitest 启动之前,隔离调用还会接收该命名空间内的本机 HOME 和 USERPROFILE。这可以保护工作线程使用的主回退、命名内置导入和导入时捕获;仅更改工作线程的 JavaScript process.env 不会更改本机线程主查找。每个工人和每个测试固定装置的家仍然是分开的。安装的 Corepack 和 Playwright 浏览器缓存保留其调用者选择的位置。

网关端口声明保留在所有封闭的 Vitest 命名空间之外的公共临时目录中,通过其显式资源所有者找到。因此,并行调用共享端口所有权,而固定装置将其保留的套接字交给子进程;删除一个调用的文件无法删除另一装置的端口声明。

在共享池端口上绑定进程内或生成的网关的固定装置将保留该端口的选择声明,直到网关关闭。网关在启动时会重试繁忙的端口,因此无人认领的设备可以在切换期间占用另一个设备的端口。 getDeterministicFreePortBlock 是支票,不是租赁;进程内网关 E2E 夹具使用 acquireGatewayE2ePortBlock 和 startClaimedGateway。

实时感知设置仍会加载原始配置文件并在请求时暂存实时状态。有界调用工件承载着该设置的原始位置;它不授予实时访问权限,并且密封设置从不咨询它。已知的密封选择会忽略环境现场和真实家庭标志。已知的完全实时感知的选择保留了明确的 OPENCLAW_LIVE_USE_REAL_HOME 行为。如果其选择混合了家庭策略或无法分类(包括自定义配置和不明确的项目选择器),则在配置加载之前会拒绝显式的真实家庭实时调用。使用 node scripts/run-vitest.mjs <test-path> 在没有 LIVE、OPENCLAW_LIVE_TEST、OPENCLAW_LIVE_GATEWAY 和 OPENCLAW_LIVE_USE_REAL_HOME 的情况下运行密封测试,然后使用 node scripts/test-live.mts -- <live-test-path> 单独运行预期的实时选择。启动器不会拆分运行或更改监视、过滤器或报告语义。

命名空间包含隔离的 home、它们的 JIT 缓存、SDK/shared-home 分配根和后备 SQLite 状态;它的生命周期跨越共享工作文件和模块重置。在 POSIX 分离启动中,父进程仅在子进程组停止、输出管道关闭并且嵌套资源所有者释放其待处理声明(包括通过和失败运行、子进程崩溃、捕获 SIGINT/SIGTERM 信号以及支持的看门狗终止)后删除该命名空间。命名空间之外的显式状态、配置文件输出和镜像工件保持不变。失败或未经验证的组加入或未解决的嵌套声明保留命名空间并报告手动恢复的确切路径。嵌套命名空间、设备生命周期和托管命令在承认工作之前注册临时文件系统所有权。发布需要积极的完成证据;捕获的清理失败、模块重置、工作程序退出或中间运行程序崩溃无法释放待处理的声明或其祖先。托管命令保留其现有的输出消耗合同; Windows Job命令也加入内核Job完成。失败的最终确定永远不会释放所有权。在手动删除报告的确切目录之前,停止所有剩余的编写器。 Windows 和非分离启动分配相同的隔离本机主目录,但在子退出和管道关闭后保留其命名空间并包含带有诊断的声明,因为无法验证后代完成。原始外部调用不会获得此边界。强迫父母或主管死亡(例如SIGKILL)可以阻止清理;故意逃离所属集团的未登记后代仍不属于本合同范围。包装器不会清除旧目录或根据名称、年龄或 PID 推断所有权。在每个承认的组加入后,CI 分片运行程序还会删除其默认的包含文件和转换缓存临时目录。调用者提供的暂存和持久缓存根仍归调用者所有。未经验证的后代完成会保留分片暂存目录并报告其确切路径。这是家庭隔离,而不是文件系统沙箱:显式绝对路径、os.userInfo() 帐户查找、具有剥离或替换家庭变量的子项以及有意的真实家庭实时执行仍处于其保护之外。

Codex 应用程序服务器固定装置等待代理和共享状态 SQLite 案例之间的引流。他们的文件拆卸耗尽了共享磁盘预算扫描工作人员的资源,在文件期间保留了重用性,并在隔离分叉关闭之前将其释放。

  • src/test-utils/openclaw-test-state.ts:当测试需要隔离的 HOME、OPENCLAW_STATE_DIR、OPENCLAW_CONFIG_PATH、配置 fixture、工作区、代理目录或身份验证配置文件存储时,可在 Vitest 中使用。
  • pnpm test:env-mutations:report:tests/harnesses 的非阻塞报告,直接改变 HOME、OPENCLAW_STATE_DIR、OPENCLAW_CONFIG_PATH、OPENCLAW_WORKSPACE_DIR 或相关环境密钥。使用它来查找共享测试状态帮助程序的迁移候选者。
  • test/helpers/openclaw-test-instance.ts:用于需要运行中的网关、CLI 环境、日志捕获以及统一清理的进程级 E2E 测试。
  • 源 scripts/lib/docker-e2e-image.sh 的 Docker/Bash E2E 通道可以将 docker_e2e_test_state_shell_b64 <label> <scenario> 传递到容器中并用 scripts/lib/openclaw-e2e-instance.sh 对其进行解码;多主脚本可以传递 docker_e2e_test_state_function_b64 并在每个流中调用 openclaw_test_state_create <label> <scenario>。 node --import tsx scripts/lib/openclaw-test-state.mts -- create --label <name> --scenario <name> --env-file <path> --json 写入可获取的主机环境文件(create 之前的 -- 使较新的 Node 运行时不会将 --env-file 视为 Node 标志)。启动网关的通道可以获取 scripts/lib/openclaw-e2e-instance.sh 进行入口点解析、模拟 OpenAI 启动、foreground/background 启动、准备情况检查、状态环境导出、日志转储和进程清理。

createOpenClawTestState选择并拥有临时路径和处理环境选择器。它不是文件系统沙箱,也不会阻止外部生产者。等待其异步restoreEnv();在恢复选择器或删除状态之前停止并加入所需的生产者。状态选择泄漏的运行时复制需要强制存储隔离,例如无法访问操作员存储的 VM 或容器,而不仅仅是临时 HOME 或状态目录覆盖。

公共测试诊断

共享的 Vitest 记者工厂会编辑断言消息、差异、expected/received 值、堆栈、源代码摘录和注释中的凭证形状字段,然后将其转发给选定的记者。键保持可见,值变为 <redacted len=N>。这也适用于显式 --reporter 选择、UI/browser 配置和 JSON/JUnit 报告。托管日志是公开的,运行者发行的令牌可能不会注册 GitHub 屏蔽。

不带引号的环境记录每行使用一个赋值:右侧的空格和标点符号属于该值。由 Node 的检查器分割成带引号的片段的多行字符串将被编辑为一个值。

编辑是无条件的,会影响诊断输出,而不是断言行为。测试控制台捕获在此边界之外;测试仍然必须避免直接记录凭据。

配置的扩展分叉项目在 Vitest 的本机分叉传输周围使用 openclaw-forks 诊断适配器。如果现有停止期限失败而子进程仍处于活动状态,则适配器在本机终止和管道清理之前最多再花费两秒时间收集 Node 报告。超时仍然是测试失败。该报告将丢失的停止确认与确认后的停顿区分开来,并包括本机堆栈、libuv 句柄和工作线程报告。环境变量、命令参数和套接字端点被省略。阻塞的事件循环可以阻止信号报告;该案例明确报告称没有捕获完整的报告。

成功关闭仍然保持安静。显式 --pool=forks 选择 V​​itest 的内置池并绕过此适配器。

JSON 跨本机进程的报告

对于多项目或分块运行,请使用输出文件显式请求本机 JSON,例如:

bash
pnpm test test/vitest/vitest.unit-fast-isolated.config.ts test/vitest/vitest.agents-embedded-agent.config.ts --reporter=verbose --reporter=json --outputFile=.artifacts/test-results.json

项目运行程序和插件批处理运行程序为每次尝试提供单独的本机 JSON 和 blob 文件,然后从 Vitest 的本机报告合并发布请求的 JSON。他们打印了一个配套的 <output>.reports-<unique> 目录。保留该目录:它包含原始报告、启用覆盖时的每次尝试覆盖文件,以及包含子退出代码、信号、超时和未启动工作的 index.json。每个调用运行一次。无输出超时会使命令失败并导致报告集不完整;它永远不会开始替换尝试。 Blob 报告是精确版本的工件。在合并另一个版本生成的工件之前,使用当前 Vitest 版本重新运行子报告。

聚合保留了已接受的箱子库存,但不是原件的无损替代品。本机合并不会恢复快照摘要或 JSON coverageMap,其 startTime 是合并时间。通过快照测试仍然成功。请阅读原始原文以了解这些详细信息以及过程结果的索引:JSON success 不会对每个包装器或未处理的错误故障进行编码。每次尝试都会在配套目录中保留单独的内置覆盖率报告。自定义覆盖率 providers/reporters 和覆盖率报告器元组选项需要具有唯一目标的单独调用。

完整的失败测试聚合会随着失败的命令退出而保留。证据缺失或无效、取消、未开始所需工作或发布失败未发布完整的汇总;现有的输出文件并不是新运行的证明。诊断打印保留的报告集位置。报告集不会自动扫描。

重叠选择可以共享本机任务 ID,因此即使案例计数匹配,合并它们也可以替换独立的故障详细信息。此类报告集保留其原件并且无法发布。选择每个配置一次,或使用不同的输出文件分别运行重叠选择。

此所有权适用于具有命名的、基于文件的 Node 项目和本机控制台报告器的显式 CLI JSON 文件请求。标量 --outputFile 和 --outputFile.json 都可以工作。配置拥有的报告器选项、其他文件格式、自定义报告器和 inline/browser 项目组合需要具有唯一输出目标的单独本机调用。不要假设这些输出是汇总的。单进程和仅控制台运行保留其现有的本机行为。本机帮助和其他非测试控件保留在子 CLI 中,并且不分配报告集。 run --version 仍然运行测试,就像在本机 Vitest 中一样。仅配置报告器不会被拦截:多个子级仍然可以覆盖相同的配置文件。使用不同的路径单独运行这些配置;单独添加 --reporter=json 不会覆盖报告者元组自己的 outputFile。

本页有帮助吗?