Core 集成工具和通用测试支持
这一阶段不是系统真正“干活”的功能,而是给 core 做端到端测试的后台脚手架。all.rs 和 suite/mod.rs 像总开关,把分散测试接起来并做好开场伪装。common 里的工具箱负责临时目录、环境判断、进程等待、可信 hooks、追踪检查、上下文快照等杂活。另一些文件搭假服务器、假 Codex 会话和干净的 codex-exec,让测试不用连真实服务、不碰用户环境,也能稳定复现各种流程。
测试套件入口点
这些文件定义集成测试二进制文件和顶层套件聚合,所有共享支持代码都服务于它们。
core/tests/all.rs源码 ↗
这个文件存在的原因,是让项目的集成测试有一个统一入口。Rust 的集成测试通常会把 tests 目录下的文件当成独立测试程序来编译运行;这里用 all.rs 做一个“总测试包”,再通过 mod suite; 把真正的测试内容接进来。它还把 codex_protocol::error 重新导出出来,也就是让下面的测试模块可以更方便地使用同一套错误类型。开头的 #![allow(clippy::expect_used)] 是告诉代码检查工具 Clippy:测试里允许使用 expect。expect 是一种“如果出错就直接失败并显示说明”的写法,在测试代码里很常见,因为测试失败时直接停下来反而更清楚。整体来说,这个文件不做业务功能,只负责把测试模块组织起来,保证测试可以被一次性发现、编译和运行。
core/tests/suite/mod.rs源码 ↗
这个文件本身不写具体测试,而是像一本测试书的目录页:下面一长串 mod xxx; 就是在告诉 Rust(这个项目使用的编程语言)哪些测试文件要被编进来一起跑。它还有一个很重要的启动前准备:通过 ctor(一种让代码在测试开始前自动运行的机制)提前配置测试二进制文件的“分身”。有些测试需要调用 apply_patch、文件系统助手、Linux 沙箱等外部命令;但测试时不一定真的有这些独立程序。这里就让同一个测试程序根据启动参数或程序名,临时表现得像那些工具。可以把它想成一个演员,换个名字牌就演不同角色。部分测试只在非 Windows 或 Unix 系统启用,因为沙箱、权限、shell 行为在不同系统上不一样。没有这个文件,很多测试不会被纳入套件,或者会因为找不到这些假命令而跑不起来。
核心测试接线
这些模块提供基础共享工具、仅用于内部测试的构造器,以及用于组装隔离集成夹具的环境/配置辅助工具。
core/tests/common/lib.rs源码 ↗
做自动化测试时,最怕两件事:一是测试不稳定,今天过明天不过;二是测试不小心碰到开发者真实电脑上的配置和文件。这个文件就是为了解决这些问题。它在测试进程一启动时,先把一些行为调成可预测,比如进程编号固定、测试二进制能正确找到辅助程序、快照测试能找到项目根目录。然后它提供一批常用帮手:生成跨 Windows 和 Unix 都能用的测试路径;把临时目录转成绝对路径;创建符号链接;加载只使用临时目录的 Codex 配置;等待 Codex 后台线程吐出某个事件;等待文件系统里出现某个文件。它还提供一些“跳过测试”的宏,用来在沙盒、无网络、远程环境、Windows 等不适合的地方直接跳过。可以把它理解成测试厨房里的公共调料架:单个调料不复杂,但没有它,很多菜都会做得又慢又容易出错。
enable_deterministic_unified_exec_process_ids_for_tests46–49 ↗
fn enable_deterministic_unified_exec_process_ids_for_tests()
作用:在测试进程刚启动时,把 Codex 内部的线程管理和进程编号切到“测试模式”。这样测试结果更稳定,不会因为每次运行时编号不同而失败。
数据流:它不接收参数,而是直接修改 Codex 核心测试支持模块里的全局开关:打开线程管理测试模式,并打开确定性的进程编号。执行后,后续测试看到的相关行为会更可预测。
调用关系:这是一个启动时自动运行的初始化函数。它把活儿交给 set_thread_manager_test_mode 和 set_deterministic_process_ids,确保后面的测试在统一、稳定的环境里跑。
调用图:调用 2 个内部函数(set_deterministic_process_ids, set_thread_manager_test_mode)。
configure_arg0_dispatch_for_test_binaries52–54 ↗
fn configure_arg0_dispatch_for_test_binaries()
作用:在测试启动时设置“按程序名分发”的测试二进制查找方式。简单说,就是让测试能更容易找到同一个测试可执行文件里伪装出来的辅助命令。
数据流:它不接收参数,只会初始化一次 TEST_ARG0_PATH_ENTRY。初始化时调用 codex_arg0::arg0_dispatch,得到一份路径入口保护对象并保存起来,避免被提前释放。
调用关系:这是测试进程启动阶段自动运行的函数。后面的 find_codex_linux_sandbox_exe 可能会从这里保存的路径信息里找到 Linux 沙盒程序。
configure_insta_workspace_root_for_snapshot_tests57–74 ↗
fn configure_insta_workspace_root_for_snapshot_tests()
作用:给快照测试设置项目根目录。快照测试就是把输出结果和一份保存好的“标准答案”比较;它需要知道标准答案文件放在哪里。
数据流:它先读取环境变量 INSTA_WORKSPACE_ROOT,如果已经有人设置了就不动。否则它尝试找到仓库根目录,再拼出 codex-rs 目录,确认路径存在后写回环境变量。结果是后续快照测试能找到正确的工作区。
调用关系:这是测试启动时自动运行的准备工作。它依赖 repo_root 找仓库位置,并用 set_var 设置环境变量,为后面所有快照测试铺路。
assert_regex_match77–82 ↗
fn assert_regex_match(pattern: &str, actual: &'s str) -> regex_lite::Captures<'s>
作用:断言一段文本必须匹配某个正则表达式。正则表达式是一种用模式找文字的写法,比如检查输出里有没有某种格式的 ID。
数据流:输入是一个正则模式和实际文本。函数先把模式编译成 Regex,再拿它去匹配实际文本;匹配成功就返回捕获结果,失败就让测试直接失败。
调用关系:测试代码在需要检查字符串格式时会调用它。它把正则编译交给 Regex::new,然后用 captures 做真正匹配。
调用图:外部调用 1 个(new)。
test_path_buf_with_windows84–101 ↗
fn test_path_buf_with_windows(unix_path: &str, windows_path: Option<&str>) -> PathBuf
作用:把测试里写的 Unix 风格路径转换成当前系统能用的 PathBuf。PathBuf 可以理解成 Rust 里表示文件路径的对象。
数据流:输入是一个 Unix 路径字符串,以及可选的 Windows 专用路径。运行在 Windows 上时,如果给了 Windows 路径就直接用;没给就把 /a/b 改成类似 C:\a\b。非 Windows 上直接使用 Unix 路径。输出是 PathBuf。
调用关系:这是测试路径工具的底层函数。test_path_buf 直接调用它;test_absolute_path_with_windows 也先用它生成普通路径,再转成绝对路径。
调用图:被 2 处调用(test_absolute_path_with_windows, test_path_buf);外部调用 2 个(from, cfg!)。
test_path_buf103–105 ↗
fn test_path_buf(unix_path: &str) -> PathBuf
作用:用最简单的方式生成测试路径。测试只需要写一个 Unix 风格路径,它会自动适配当前系统。
数据流:输入是 Unix 风格路径字符串。它把这个路径交给 test_path_buf_with_windows,并且不提供 Windows 特殊版本;输出是适配当前系统的 PathBuf。
调用关系:这是给测试作者用的简化入口。真正的跨平台转换工作由 test_path_buf_with_windows 完成。
调用图:调用 1 个内部函数(test_path_buf_with_windows)。
test_absolute_path_with_windows107–113 ↗
fn test_absolute_path_with_windows(
unix_path: &str,
windows_path: Option<&str>,
) -> AbsolutePathBuf
作用:生成一个测试用的绝对路径,并兼容 Windows。绝对路径就是从磁盘根部开始的完整路径,不依赖当前所在目录。
数据流:输入是 Unix 路径和可选 Windows 路径。它先调用 test_path_buf_with_windows 得到当前系统路径,再把它包装成 AbsolutePathBuf;如果路径不是绝对路径,测试会失败。
调用关系:这是绝对路径测试工具的底层函数。test_absolute_path 和 test_tmp_path 都通过它生成可靠的绝对路径。
调用图:调用 2 个内部函数(test_path_buf_with_windows, from_absolute_path);被 2 处调用(test_absolute_path, test_tmp_path)。
test_absolute_path115–117 ↗
fn test_absolute_path(unix_path: &str) -> AbsolutePathBuf
作用:快速生成一个测试用绝对路径,不需要单独写 Windows 版本。
数据流:输入是 Unix 风格路径。它调用 test_absolute_path_with_windows,并把 Windows 专用路径留空;输出是 AbsolutePathBuf。
调用关系:这是给普通测试使用的便捷入口。具体的系统差异处理交给 test_absolute_path_with_windows。
调用图:调用 1 个内部函数(test_absolute_path_with_windows)。
create_directory_symlink127–131 ↗
fn create_directory_symlink(source: &Path, link: &Path)
作用:创建一个指向目录的符号链接。符号链接可以理解成文件系统里的“快捷方式”。
数据流:输入是源目录路径和链接路径。Unix 上调用 symlink,Windows 上调用 symlink_dir。成功后,链接路径会像一个目录快捷方式一样指向源目录;失败会让测试报错。
调用关系:测试需要模拟链接目录时会调用它。它根据操作系统选择不同的底层系统调用,因为 Windows 和 Unix 创建目录链接的接口不一样。
调用图:外部调用 2 个(symlink, symlink_dir)。
TempDir::abs138–140 ↗
fn abs(&self) -> AbsolutePathBuf
作用:给临时目录对象加一个方便方法,直接拿到它的绝对路径。
数据流:输入是一个 TempDir,也就是会自动清理的临时目录。函数读取它的 path,再转成 AbsolutePathBuf 返回,不额外创建或删除文件。
调用关系:这是 TempDirExt 这个扩展 trait 的实现。测试代码拿到 tempfile::TempDir 后,可以直接调用 abs(),不用每次手写路径转换。
test_tmp_path143–145 ↗
fn test_tmp_path() -> AbsolutePathBuf
作用:返回一个测试里代表系统临时目录的绝对路径。它用固定值,让测试输出更容易预测。
数据流:它不接收参数。非 Windows 上使用 /tmp;Windows 上使用 C:\Users\codex\AppData\Local\Temp。最后返回 AbsolutePathBuf。
调用关系:它调用 test_absolute_path_with_windows 做跨平台路径转换。test_tmp_path_buf 会进一步把它转成普通 PathBuf。
调用图:调用 1 个内部函数(test_absolute_path_with_windows);被 1 处调用(test_tmp_path_buf)。
test_tmp_path_buf147–149 ↗
fn test_tmp_path_buf() -> PathBuf
作用:返回测试临时目录的 PathBuf 版本,方便那些不需要 AbsolutePathBuf 的代码使用。
数据流:它不接收参数。先调用 test_tmp_path 得到绝对路径对象,再把它拆成普通 PathBuf 返回。
调用关系:这是 test_tmp_path 的简单包装。测试如果只想要标准路径类型,可以用这个函数。
调用图:调用 1 个内部函数(test_tmp_path)。
fetch_dotslash_file152–185 ↗
fn fetch_dotslash_file(
dotslash_file: &std::path::Path,
dotslash_cache: Option<&std::path::Path>,
) -> anyhow::Result<PathBuf>
作用:运行 dotslash 命令,把一个 DotSlash 资源下载或解析成本地可执行文件路径。DotSlash 可以理解成一种“按需拿工具文件”的机制。
数据流:输入是 dotslash 文件路径,以及可选缓存目录。函数启动外部命令 dotslash -- fetch,如果给了缓存就设置 DOTSLASH_CACHE。它检查命令是否成功、输出是否是 UTF-8、输出路径是否非空且确实是文件,最后返回这个文件路径。
调用关系:测试需要某个由 DotSlash 管理的工具或资源时会调用它。它负责和外部 dotslash 程序打交道,并把各种失败情况变成清楚的错误信息。
load_default_config_for_test190–196 ↗
async fn load_default_config_for_test(codex_home: &TempDir) -> Config
作用:为测试加载一份默认 Codex 配置,并把所有落盘状态限制在传入的临时目录里。这样不会误改开发者真实的 ~/.codex。
数据流:输入是一个临时目录。函数创建默认的云配置加载器,然后调用 load_default_config_for_test_with_cloud_config_bundle。输出是一份 Config。
调用关系:这是大多数测试加载配置时用的便捷入口。更细的配置构建工作交给 load_default_config_for_test_with_cloud_config_bundle。
调用图:调用 2 个内部函数(default, load_default_config_for_test_with_cloud_config_bundle)。
load_default_config_for_test_with_cloud_config_bundle200–212 ↗
async fn load_default_config_for_test_with_cloud_config_bundle(
codex_home: &TempDir,
cloud_config_bundle: CloudConfigBundleLoader,
) -> Config
作用:加载测试用默认配置,同时允许测试传入自己的云端配置要求。云端配置这里指企业或远程下发的配置规则。
数据流:输入是临时目录和 CloudConfigBundleLoader。函数用 ConfigBuilder 组装配置:禁用测试里不需要的托管配置,设置 codex_home 到临时目录,加入测试覆盖项,再加入云配置加载器。构建成功后返回 Config。
调用关系:load_default_config_for_test 会调用它。它还会调用 default_test_overrides,为当前平台补上测试必须的覆盖配置,比如 Linux 沙盒程序路径。
调用图:调用 2 个内部函数(without_managed_config_for_tests, default_test_overrides);被 1 处调用(load_default_config_for_test);外部调用 2 个(path, default)。
managed_network_requirements_loader214–222 ↗
fn managed_network_requirements_loader() -> CloudConfigBundleLoader
作用:生成一份测试用的企业网络要求配置,声明实验性网络功能开启,并允许本地绑定端口。
数据流:它不接收参数。函数把一小段配置文本交给 CloudConfigBundleFixture,得到一个 CloudConfigBundleLoader 返回。
调用关系:需要模拟“企业配置要求网络能力”的测试会用它。实际构造加载器的工作交给 loader_with_enterprise_requirement。
调用图:调用 1 个内部函数(loader_with_enterprise_requirement)。
default_test_overrides235–237 ↗
fn default_test_overrides() -> ConfigOverrides
作用:给测试配置准备平台相关的默认覆盖项。覆盖项就是测试时临时替换真实配置的一些设置。
数据流:Linux 上,它会尝试找到 codex-linux-sandbox 程序路径,并放进 ConfigOverrides;其他系统上返回空的默认覆盖项。输出是 ConfigOverrides。
调用关系:load_default_config_for_test_with_cloud_config_bundle 在构建配置时会调用它。Linux 版本还会调用 find_codex_linux_sandbox_exe 找到沙盒程序。
调用图:调用 1 个内部函数(find_codex_linux_sandbox_exe);被 1 处调用(load_default_config_for_test_with_cloud_config_bundle);外部调用 1 个(default)。
find_codex_linux_sandbox_exe240–254 ↗
fn find_codex_linux_sandbox_exe() -> Result<PathBuf, CargoBinError>
作用:在 Linux 测试里找到 codex-linux-sandbox 这个辅助程序。这个程序用于沙盒测试,也就是把命令限制在更安全的环境里运行。
数据流:它先看启动时 arg0 分发有没有记录 sandbox 程序路径;有就返回。没有的话,尝试返回当前正在运行的测试可执行文件路径;再不行就让 cargo_bin 查找名为 codex-linux-sandbox 的二进制。输出是路径或错误。
调用关系:default_test_overrides 会调用它,跳过宏 codex_linux_sandbox_exe_or_skip 也会依赖它。它把寻找二进制的最后一步交给 codex_utils_cargo_bin::cargo_bin。
调用图:被 1 处调用(default_test_overrides);外部调用 2 个(cargo_bin, current_exe)。
wait_for_event256–265 ↗
async fn wait_for_event(
codex: &CodexThread,
predicate: F,
) -> codex_protocol::protocol::EventMsg
作用:等待 Codex 后台线程发出一个满足条件的事件。事件可以理解成后台工作进度或结果的通知。
数据流:输入是 CodexThread 和一个判断函数。它设置默认等待时间 1 秒,然后调用 wait_for_event_with_timeout。输出是第一个满足条件的 EventMsg。
调用关系:这是等待事件的常用简化入口。wait_for_event_match 会用它先等到匹配事件,再提取更具体的数据。
调用图:调用 1 个内部函数(wait_for_event_with_timeout);被 1 处调用(wait_for_event_match);外部调用 1 个(from_secs)。
wait_for_mcp_server268–298 ↗
async fn wait_for_mcp_server(codex: &CodexThread, server_name: &str) -> anyhow::Result<()>
作用:等待指定 MCP 服务器启动完成,并确认它真的处于 ready 状态。MCP 是一种让 Codex 连接外部工具服务的协议。
数据流:输入是 CodexThread 和服务器名字。函数不断读取事件,直到看到 McpStartupComplete 汇总。然后检查这个服务器是否失败、是否被取消、是否在 ready 列表里;失败或取消就返回错误,不在 ready 里就让测试失败,成功则返回 Ok。
调用关系:配置了 MCP 服务的测试会用它确认服务已经能用了。它直接从 CodexThread 读取 next_event,并自己解释启动汇总。
调用图:调用 1 个内部函数(next_event);外部调用 2 个(bail!, assert!)。
submit_thread_settings300–323 ↗
async fn submit_thread_settings(
codex: &CodexThread,
thread_settings: codex_protocol::protocol::ThreadSettingsOverrides,
) -> anyhow::Result<()>
作用:向 Codex 后台线程提交一组线程设置,并等待系统确认这些设置已经生效。
数据流:输入是 CodexThread 和 ThreadSettingsOverrides。函数先 submit 一个 ThreadSettings 操作,拿到提交 ID;然后循环等待事件,直到看到同一个 ID 的结果。看到 Applied 就成功返回,看到 Error 或其他意外事件就让测试失败。
调用关系:测试需要临时调整线程设置时会调用它。它先把操作交给 codex.submit,再通过 codex.next_event 等回执,像寄出申请单后等盖章确认。
调用图:调用 2 个内部函数(next_event, submit);外部调用 2 个(from_secs, panic!)。
wait_for_event_match325–331 ↗
async fn wait_for_event_match(codex: &CodexThread, matcher: F) -> T
作用:等待一个事件,并从这个事件里直接提取测试关心的数据。它比只判断 true/false 更方便。
数据流:输入是 CodexThread 和 matcher 函数;matcher 如果认出目标事件,就返回 Some(data)。函数先用 wait_for_event 等到 matcher 能匹配的事件,再再次调用 matcher 取出数据并返回。
调用关系:它建立在 wait_for_event 之上。测试想“等到某类事件并拿里面字段”时,用它可以少写重复代码。
调用图:调用 1 个内部函数(wait_for_event)。
wait_for_event_with_timeout333–353 ↗
async fn wait_for_event_with_timeout(
codex: &CodexThread,
mut predicate: F,
wait_time: tokio::time::Duration,
) -> codex_protocol::protocol::EventMsg
作用:在指定时间内等待 Codex 发出满足条件的事件。如果一直等不到,就让测试失败,避免测试卡死。
数据流:输入是 CodexThread、判断函数和等待时长。函数循环读取 next_event,每次读取都包一层超时;实际等待时间至少 10 秒,以照顾异步启动这类慢操作。遇到满足条件的事件就返回 EventMsg。
调用关系:wait_for_event 会调用它。它是这个文件里等待 Codex 事件的核心实现,直接负责和 CodexThread 的事件流交互。
调用图:调用 1 个内部函数(next_event);被 1 处调用(wait_for_event);外部调用 2 个(from_secs, max)。
sandbox_env_var355–357 ↗
fn sandbox_env_var() -> &'static str
作用:返回 Codex 用来标记沙盒类型的环境变量名。环境变量就是进程启动时带着的一些文字开关。
数据流:它不接收参数,也不读取环境变量的值,只返回 codex_core 里定义好的变量名字符串。
调用关系:skip_if_sandbox 宏会用它判断当前测试是不是跑在某种沙盒里。把变量名集中在这里,可以避免测试里硬编码字符串。
sandbox_network_env_var359–361 ↗
fn sandbox_network_env_var() -> &'static str
作用:返回 Codex 用来标记“沙盒里网络被禁用”的环境变量名。
数据流:它不接收参数,只返回 codex_core 里定义好的网络禁用变量名字符串。
调用关系:skip_if_no_network 宏会用它判断是否该跳过需要网络的测试。这样测试代码不用知道底层变量名。
format_with_current_shell363–365 ↗
fn format_with_current_shell(command: &str) -> Vec<String>
作用:把一段命令文本包装成“用当前用户默认 shell 执行”的参数列表。shell 可以理解成命令行解释器,比如 bash、zsh。
数据流:输入是一段命令字符串。函数先取得默认用户 shell,再让它生成登录 shell 模式下执行这段命令所需的参数列表。输出是 Vec<String>。
调用关系:format_with_current_shell_display 会调用它,再把参数列表拼成可显示的字符串。真正理解 shell 参数格式的是 default_user_shell 返回的对象。
调用图:调用 1 个内部函数(default_user_shell);被 1 处调用(format_with_current_shell_display)。
format_with_current_shell_display367–370 ↗
fn format_with_current_shell_display(command: &str) -> String
作用:把“用当前 shell 执行某命令”的参数列表变成一行可读的命令字符串,方便断言或展示。
数据流:输入是命令字符串。函数先调用 format_with_current_shell 得到参数数组,再用 shlex::try_join 按 shell 转义规则拼成一个字符串返回。
调用关系:它是 format_with_current_shell 的展示版。测试需要比较或打印 shell 命令时会用它。
调用图:调用 1 个内部函数(format_with_current_shell);外部调用 1 个(try_join)。
format_with_current_shell_non_login372–375 ↗
fn format_with_current_shell_non_login(command: &str) -> Vec<String>
作用:生成用当前默认 shell 执行命令的参数列表,但不使用登录 shell 模式。登录 shell 通常会读取更多用户启动配置。
数据流:输入是命令字符串。函数取得默认 shell,并请求它生成非登录模式的执行参数。输出是 Vec<String>。
调用关系:format_with_current_shell_display_non_login 会调用它。它和 format_with_current_shell 的区别只在是否使用登录 shell。
调用图:调用 1 个内部函数(default_user_shell);被 1 处调用(format_with_current_shell_display_non_login)。
format_with_current_shell_display_non_login377–381 ↗
fn format_with_current_shell_display_non_login(command: &str) -> String
作用:把非登录 shell 模式下的执行参数拼成一行可读字符串。
数据流:输入是命令字符串。函数先调用 format_with_current_shell_non_login 得到参数数组,再用 shlex::try_join 进行安全拼接。输出是字符串。
调用关系:这是 format_with_current_shell_non_login 的展示版。测试需要检查非登录 shell 命令格式时会用它。
调用图:调用 1 个内部函数(format_with_current_shell_non_login);外部调用 1 个(try_join)。
stdio_server_bin383–385 ↗
fn stdio_server_bin() -> Result<String, CargoBinError>
作用:找到测试用的 test_stdio_server 二进制程序路径,并返回成字符串。
数据流:它不接收参数。函数调用 cargo_bin 查找名为 test_stdio_server 的可执行文件,把路径转成字符串;成功返回字符串,失败返回 CargoBinError。
调用关系:需要启动标准输入输出测试服务器的测试会调用它。实际查找 cargo 构建产物的工作由 codex_utils_cargo_bin::cargo_bin 完成。
调用图:外部调用 1 个(cargo_bin)。
fs_wait::wait_for_path_exists401–407 ↗
async fn wait_for_path_exists(
path: impl Into<PathBuf>,
timeout: Duration,
) -> Result<PathBuf>
作用:异步等待某个文件或目录路径出现。异步的意思是不会把整个测试运行时堵死。
数据流:输入是路径和超时时间。函数把路径转成 PathBuf,然后把真正会阻塞的等待工作丢到 spawn_blocking 里运行;最后返回出现的路径或错误。
调用关系:这是 fs_wait 模块给异步测试用的入口。它把具体等待逻辑交给 wait_for_path_exists_blocking,自己负责把阻塞操作隔离出去。
调用图:外部调用 2 个(into, spawn_blocking)。
fs_wait::wait_for_matching_file409–420 ↗
async fn wait_for_matching_file(
root: impl Into<PathBuf>,
timeout: Duration,
predicate: impl FnMut(&Path) -> bool + Send + 'static,
) -> Result<PathBuf>
作用:异步等待某个目录下出现一个符合条件的文件。条件由调用者提供,比如“文件名以 .json 结尾”。
数据流:输入是根目录、超时时间和判断函数。函数把根目录转成 PathBuf,然后在阻塞线程里调用 blocking_find_matching_file。输出是第一个匹配文件的路径,或超时错误。
调用关系:这是等待匹配文件的异步入口。它把扫描目录和监听文件变化的细节交给 blocking_find_matching_file。
调用图:外部调用 2 个(into, spawn_blocking)。
fs_wait::wait_for_path_exists_blocking422–461 ↗
fn wait_for_path_exists_blocking(path: PathBuf, timeout: Duration) -> Result<PathBuf>
作用:用阻塞方式等待某个路径出现。阻塞方式就是当前线程会停在这里等,所以外层通常把它放到专门线程里跑。
数据流:输入是目标路径和超时时间。函数先检查路径是否已经存在;如果不存在,就找到最近存在的父目录,监听这个目录及其子目录的文件变化。每次收到变化通知就再检查目标路径,直到出现、超时或监听出错。输出是目标路径或错误。
调用关系:wait_for_path_exists 会在阻塞线程里调用它;blocking_find_matching_file 也用它先确保根目录存在。它还会调用 nearest_existing_ancestor 找到能监听的起点。
调用图:外部调用 6 个(now, exists, anyhow!, nearest_existing_ancestor, channel, recommended_watcher)。
fs_wait::blocking_find_matching_file463–501 ↗
fn blocking_find_matching_file(
root: PathBuf,
timeout: Duration,
predicate: &mut impl FnMut(&Path) -> bool,
) -> Result<PathBuf>
作用:用阻塞方式等待某个目录中出现符合条件的文件。
数据流:输入是根目录、超时时间和文件判断函数。它先等待根目录存在,然后扫描一次目录;没找到就监听目录变化,每次有变化就重新扫描。超时前找到就返回文件路径,否则返回错误。
调用关系:wait_for_matching_file 会把这个函数放到阻塞线程里执行。它依赖 wait_for_path_exists_blocking 等根目录,依赖 scan_for_match 做具体扫描。
调用图:外部调用 6 个(now, anyhow!, scan_for_match, wait_for_path_exists_blocking, channel, recommended_watcher)。
fs_wait::scan_for_match503–514 ↗
fn scan_for_match(root: &Path, predicate: &mut impl FnMut(&Path) -> bool) -> Option<PathBuf>
作用:扫描一个目录树,寻找第一个符合条件的普通文件。
数据流:输入是根目录和判断函数。函数用 WalkDir 逐个走过目录里的条目,跳过非文件;对每个文件调用判断函数,命中就返回这个文件路径,全部没命中就返回 None。
调用关系:blocking_find_matching_file 会在开始时和每次文件系统变化后调用它。它只负责“找”,不负责等待。
调用图:外部调用 1 个(new)。
fs_wait::nearest_existing_ancestor516–527 ↗
fn nearest_existing_ancestor(path: &Path) -> PathBuf
作用:从一个可能还不存在的路径往上找,找到最近一个已经存在的父目录。这样文件监听器才知道该监听哪里。
数据流:输入是目标路径。函数从这个路径开始检查是否存在;不存在就看父目录,一层层往上找。找到存在的目录就返回它;如果连父目录都没有,就返回当前目录 .。
调用关系:wait_for_path_exists_blocking 在目标路径还不存在时会调用它。它为 notify 文件监听器提供一个真实存在、可以监听的起点。
调用图:外部调用 1 个(from)。
core/src/test_support.rs源码 ↗
这个文件不该被正式产品代码使用,只服务跨 crate 集成测试(crate 可以理解成 Rust 里的一个代码包)。它把很多内部测试专用能力包装成公开函数:比如创建假的 AuthManager(登录身份管理器)、创建带指定模型供应商的 ThreadManager(对话线程管理器)、启动或恢复测试线程、拿离线模型信息、生成请求元数据。它还提供一个 EmptyUserInstructionsProvider,意思是“用户没有额外指令”,避免测试被真实本地说明文件影响。文件里还有一个延迟初始化的 TEST_MODEL_PRESETS:第一次用到时才读取内置模型列表,排序并标出默认模型。整体像测试厨房里的备料台,把复杂对象提前切好配好,让各类测试只关心自己要验证的行为。
EmptyUserInstructionsProvider::load_user_instructions53–55 ↗
fn load_user_instructions(&self) -> LoadUserInstructionsFuture<'_>
作用:这个函数给测试提供一份“空的用户指令”。有人需要模拟用户没有写任何额外要求时,就用它。
数据流:进去的是这个空指令提供器本身,不读取磁盘也不读取真实配置 → 它创建一个异步任务,里面直接返回默认的 LoadedUserInstructions → 出来的是一份空结果,外部看到的效果就是“没有用户指令”。
调用关系:它实现的是 UserInstructionsProvider 这个接口,所以凡是系统在测试里需要“加载用户指令”时,都可以把这个对象塞进去。它只把结果包成异步返回,不再把工作交给真实的指令加载流程。
调用图:外部调用 2 个(pin, default)。
set_thread_manager_test_mode58–60 ↗
fn set_thread_manager_test_mode(enabled: bool)
作用:这个函数打开或关闭线程管理器的测试模式。测试模式通常会让行为更可控,避免真实运行时的一些副作用。
数据流:进去一个布尔值 enabled,表示开还是关 → 它把这个值转交给内部的 set_thread_manager_test_mode_for_tests → 出来没有返回值,但全局或内部测试开关被改掉了。
调用关系:它是给测试调用的外层开关。调用图里 enable_deterministic_unified_exec_process_ids_for_tests 会用到它,说明在开启确定性执行环境时,也会顺手让线程管理器进入测试状态。
调用图:调用 1 个内部函数(set_thread_manager_test_mode_for_tests);被 1 处调用(enable_deterministic_unified_exec_process_ids_for_tests)。
set_deterministic_process_ids62–64 ↗
fn set_deterministic_process_ids(enabled: bool)
作用:这个函数让测试里的进程编号变得固定、可预测。这样快照测试或断言不会因为每次运行编号不同而失败。
数据流:进去一个布尔值 enabled → 它交给 unified_exec 里的 set_deterministic_process_ids_for_tests → 出来没有普通返回值,但执行系统之后生成的测试进程 ID 会按可预测方式变化。
调用关系:它是统一执行子系统的测试开关。enable_deterministic_unified_exec_process_ids_for_tests 会调用它,用来把多处测试环境调整成“每次跑都一样”。
调用图:调用 1 个内部函数(set_deterministic_process_ids_for_tests);被 1 处调用(enable_deterministic_unified_exec_process_ids_for_tests)。
auth_manager_from_auth66–68 ↗
fn auth_manager_from_auth(auth: CodexAuth) -> Arc<AuthManager>
作用:这个函数用一份现成的测试登录信息,造出 AuthManager。AuthManager 可以理解成“保管登录凭证的人”。
数据流:进去一个 CodexAuth,也就是测试准备好的认证信息 → 它调用 AuthManager::from_auth_for_testing 包成可共享的 Arc;Arc 是 Rust 里让多个地方安全共用同一个对象的引用计数指针 → 出来的是 Arc<AuthManager>,测试可以到处传。
调用关系:很多测试会先用它造登录管理器,再去测试远程控制、模型请求、配置构建、权限审批等流程。它自己不验证业务,只负责把测试身份包装成系统能用的形状。
调用图:调用 1 个内部函数(from_auth_for_testing);被 25 处调用(remote_control_auth_manager, remote_control_auth_manager, rewrite_mcp_tool_arguments_for_openai_files_surfaces_upload_failures, approve_mode_skips_guardian_in_every_permission_mode, build_from_config, responses_respects_model_info_overrides_from_config, azure_responses_request_includes_store_and_reasoning_ids, prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens, websocket_harness_with_provider_options, code_mode_can_call_standalone_web_search (+15 more))。
auth_manager_from_auth_with_home70–72 ↗
fn auth_manager_from_auth_with_home(auth: CodexAuth, codex_home: PathBuf) -> Arc<AuthManager>
作用:这个函数和 auth_manager_from_auth 类似,但还能指定一个测试用的 Codex 主目录。这样测试不会碰到用户真实目录。
数据流:进去认证信息 CodexAuth 和 codex_home 路径 → 它调用 from_auth_for_testing_with_home,把身份和测试目录一起塞进 AuthManager → 出来的是可共享的 Arc<AuthManager>。
调用关系:remote_control_auth_manager_with_home 这类测试会用它,特别适合需要检查文件位置、缓存目录或本地状态的场景。它把目录选择这件事交给 AuthManager 的测试构造函数完成。
调用图:调用 1 个内部函数(from_auth_for_testing_with_home);被 1 处调用(remote_control_auth_manager_with_home)。
thread_manager_with_models_provider74–79 ↗
fn thread_manager_with_models_provider(
auth: CodexAuth,
provider: ModelProviderInfo,
) -> ThreadManager
作用:这个函数用指定登录信息和模型供应商,造一个测试用 ThreadManager。ThreadManager 可以理解成“对话线程的总调度员”。
数据流:进去 CodexAuth 和 ModelProviderInfo,也就是身份和模型来源说明 → 它调用 ThreadManager::with_models_provider_for_tests → 出来一个配置好的 ThreadManager,测试可以用它启动或恢复对话。
调用关系:一些测试用它检查恢复线程、配置警告、实验功能提示等行为。它把真正的构造细节交给 ThreadManager 内部的测试构造函数。
调用图:调用 1 个内部函数(with_models_provider_for_tests);被 3 处调用(emits_warning_when_resumed_model_differs, emits_warning_when_unstable_features_enabled_via_config, suppresses_warning_when_configured)。
thread_manager_with_models_provider_and_home81–93 ↗
fn thread_manager_with_models_provider_and_home(
auth: CodexAuth,
provider: ModelProviderInfo,
codex_home: PathBuf,
environment_manager: Arc<EnvironmentManager>,
) -> ThreadManager
作用:这个函数创建测试用 ThreadManager,同时指定模型供应商、测试主目录和环境管理器。它适合需要模拟更完整运行环境的测试。
数据流:进去认证信息、模型供应商、codex_home 路径,以及 EnvironmentManager(执行环境管理器) → 它调用 with_models_provider_and_home_for_tests → 出来一个绑定这些测试条件的 ThreadManager。
调用关系:权限守护、子线程活动、线程启动快照等测试会用它,因为这些场景不只需要模型,还需要目录和执行环境。它是复杂测试场景的装配入口。
调用图:调用 1 个内部函数(with_models_provider_and_home_for_tests);被 3 处调用(guardian_command_execution_notifications_wrap_review_lifecycle, interrupted_subagent_activity_removes_missing_thread_watch, turn_started_omits_active_snapshot_items)。
thread_manager_with_models_provider_home_and_state95–109 ↗
fn thread_manager_with_models_provider_home_and_state(
auth: CodexAuth,
provider: ModelProviderInfo,
codex_home: PathBuf,
environment_manager: Arc<EnvironmentManager>,
state_db: Op
作用:这个函数创建测试用 ThreadManager,并且可以额外塞入状态数据库句柄。它适合测试“有历史状态”或“无状态库”两种情况。
数据流:进去认证信息、模型供应商、测试主目录、环境管理器,以及可选的 state_db → 它调用 with_models_provider_home_and_state_for_tests → 出来一个按这些条件组装好的 ThreadManager。
调用关系:它是 thread manager 测试构造器里更完整的一档。相比前几个函数,它多传了状态数据库,让测试能覆盖保存、恢复、读取状态相关的路径。
调用图:调用 1 个内部函数(with_models_provider_home_and_state_for_tests)。
start_thread_with_user_shell_override111–119 ↗
async fn start_thread_with_user_shell_override(
thread_manager: &ThreadManager,
config: Config,
user_shell_override: crate::shell::Shell,
) -> codex_protocol::error::Result<crate::NewThrea
作用:这个异步函数用测试指定的 shell 启动一个新线程。shell 可以理解成运行命令时用的命令行外壳,比如 bash 或 zsh。
数据流:进去 ThreadManager、Config 配置,以及 user_shell_override → 它调用 start_thread_with_user_shell_override_for_tests 并等待完成 → 出来要么是 NewThread,新线程启动成功;要么是错误结果。
调用关系:build_from_config 测试会调用它,用来确认配置里的 shell 选择能影响线程启动。它不自己启动底层流程,而是把活儿交给 ThreadManager 的测试专用启动方法。
调用图:调用 1 个内部函数(start_thread_with_user_shell_override_for_tests);被 1 处调用(build_from_config)。
resume_thread_from_rollout_with_user_shell_override121–136 ↗
async fn resume_thread_from_rollout_with_user_shell_override(
thread_manager: &ThreadManager,
config: Config,
rollout_path: PathBuf,
auth_manager: Arc<AuthManager>,
user_shell_over
作用:这个异步函数从一个 rollout 文件恢复线程,同时强制使用测试指定的 shell。rollout 可以理解成保存过的对话运行记录。
数据流:进去 ThreadManager、Config、rollout_path 文件路径、AuthManager,以及 shell 覆盖值 → 它调用 resume_thread_from_rollout_with_user_shell_override_for_tests 并等待 → 出来是恢复后的 NewThread,或一条错误。
调用关系:build_from_config 测试会用它检查“从历史记录恢复线程”时配置是否正确生效。它把真正读取 rollout、恢复线程的复杂流程交给 ThreadManager。
调用图:调用 1 个内部函数(resume_thread_from_rollout_with_user_shell_override_for_tests);被 1 处调用(build_from_config)。
models_manager_with_provider138–145 ↗
fn models_manager_with_provider(
codex_home: PathBuf,
auth_manager: Arc<AuthManager>,
provider: ModelProviderInfo,
) -> SharedModelsManager
作用:这个函数用指定模型供应商创建一个测试用模型管理器。模型管理器负责知道有哪些模型、模型能力是什么、该怎么请求它们。
数据流:进去 codex_home 路径、AuthManager 和 ModelProviderInfo → 它先用 create_model_provider 创建模型供应商对象,再从供应商那里拿 models_manager → 出来是 SharedModelsManager,也就是可共享的模型管理器。
调用关系:大量测试会用它搭建模型相关环境,比如权限审查请求、响应 API 错误、会话与轮次请求等。它是测试中连接“登录身份”和“模型系统”的常用桥梁。
调用图:被 24 处调用(guardian_review_request_layout_matches_model_visible_request_snapshot, guardian_review_surfaces_responses_api_errors_in_rejection_reason, guardian_test_session_and_turn_with_base_url, guardian_test_session_turn_and_rx, approve_mode_skips_guardian_in_every_permission_mode, guardian_mode_mcp_denial_returns_rationale_message, guardian_mode_skips_auto_when_annotations_do_not_require_approval, guardian_allows_shell_command_additional_permissions_requests_past_policy_validation, guardian_subagent_does_not_inherit_parent_exec_policy_rules, request_permissions_guardian_review_stops_when_cancelled (+14 more));外部调用 2 个(create_model_provider, models_manager)。
get_model_offline147–149 ↗
fn get_model_offline(model: Option<&str>) -> String
作用:这个函数在不联网的情况下拿到一个可用于测试的模型名称。这样测试不用依赖真实模型服务是否可用。
数据流:进去一个可选的模型名;如果有就按它处理,如果没有就让测试工具选默认离线模型 → 它调用 get_model_offline_for_tests → 出来是一个模型字符串。
调用关系:响应流、Azure 请求、供应商认证请求等测试会调用它。它把“选哪个模型才适合离线测试”这件事交给模型管理器的测试支持代码。
调用图:调用 1 个内部函数(get_model_offline_for_tests);被 4 处调用(responses_stream_includes_subagent_header_on_other, responses_stream_includes_subagent_header_on_review, azure_responses_request_includes_store_and_reasoning_ids, send_provider_auth_request)。
construct_model_info_offline151–153 ↗
fn construct_model_info_offline(model: &str, config: &Config) -> ModelInfo
作用:这个函数离线构造某个模型的详细信息。模型详细信息包括系统判断请求格式、能力开关等所需的数据。
数据流:进去模型名和 Config 配置 → 它先把 Config 转成模型管理器能懂的配置,再调用 construct_model_info_offline_for_tests → 出来是 ModelInfo,也就是测试用的模型资料。
调用关系:很多响应请求和指令相关测试会用它,尤其是需要精确控制模型能力时。它连接了 core 的 Config 和 models manager 的测试模型信息构造器。
调用图:调用 1 个内部函数(construct_model_info_offline_for_tests);被 9 处调用(responses_respects_model_info_overrides_from_config, responses_stream_includes_subagent_header_on_other, responses_stream_includes_subagent_header_on_review, azure_responses_request_includes_store_and_reasoning_ids, send_provider_auth_request, websocket_harness_with_provider_options, base_instructions_override_disables_personality_template, instructions_uses_base_if_feature_disabled, personality_does_not_mutate_base_instructions_without_template);外部调用 1 个(to_models_manager_config)。
responses_metadata163–191 ↗
fn responses_metadata(
installation_id: &str,
session_id: &str,
thread_id: &str,
turn_id: Option<&str>,
window_id: String,
session_source: &SessionSource,
parent_thread_id:
作用:这个函数帮测试生成一份 Codex Responses 请求元数据。元数据就是随请求带上的“说明标签”,比如会话号、线程号、请求类型、是否是子代理等。
数据流:进去安装 ID、会话 ID、线程 ID、可选轮次 ID、窗口 ID、会话来源、可选父线程 ID,以及测试用请求类型 → 它把测试枚举转换成正式的 CodexResponsesRequestKind,计算 subagent 相关头和值,再调用 CodexResponsesMetadata::new 填基础字段 → 出来是一份完整的 CodexResponsesMetadata。
调用关系:test_responses_metadata_for_client 会调用它,验证客户端看到的元数据是否符合预期。它把请求类型映射、子代理标记和基础元数据创建集中在一起,免得每个测试手写一遍。
调用图:调用 2 个内部函数(new, subagent_header_value);被 1 处调用(test_responses_metadata_for_client);外部调用 2 个(and, and_then)。
all_model_presets193–195 ↗
fn all_model_presets() -> &'static Vec<ModelPreset>
作用:这个函数返回测试可用的全部内置模型预设。模型预设可以理解成“模型选择菜单里的一个选项”。
数据流:进去没有参数 → 第一次使用时,TEST_MODEL_PRESETS 会读取内置 models.json、按优先级排序、转换成预设并标出默认项;之后直接复用缓存 → 出来是一个静态引用,指向同一份模型预设列表。
调用关系:模型缓存、可见模型列表、服务层级、默认模型等测试会调用它。它本身不重新加载数据,而是暴露已经延迟准备好的测试模型菜单。
调用图:被 5 处调用(write_models_cache, expected_visible_models, service_tier_model_and_tier_id, turn_start_sends_service_tier_id_to_model_request, bundled_default_model_slug)。
builtin_collaboration_mode_presets197–199 ↗
fn builtin_collaboration_mode_presets() -> Vec<CollaborationModeMask>
作用:这个函数返回内置的协作模式预设。协作模式可以理解成系统允许哪些协同工作方式的一组选项。
数据流:进去没有参数 → 它调用 collaboration_mode_presets 模块里的 builtin_collaboration_mode_presets → 出来是一组 CollaborationModeMask,供测试拿来比较或返回。
调用关系:list_collaboration_modes_returns_presets 测试会调用它,用来确认列出的协作模式就是内置预设。它只是把模型管理器里的预设函数重新暴露给跨包测试。
调用图:调用 1 个内部函数(builtin_collaboration_mode_presets);被 1 处调用(list_collaboration_modes_returns_presets)。
core/tests/common/test_environment.rs源码 ↗
测试有时不只在开发者电脑上跑,还可能在 Docker 容器里跑,或者用 Wine 在 Linux 上模拟 Windows 路径。这个文件就像测试启动前的“环境登记员”:先读取环境变量,看测试要用哪种地方;再把这个选择变成 TestEnvironment 这个枚举,也就是“本机 / Docker / WineExec”三种明确状态。它还会帮测试算出远程工作目录,比如 Docker 用 /tmp 下的目录,WineExec 用 C: 盘风格的目录,并按对应系统的路径习惯转换。这里也做了防呆:容器名不能为空,环境变量必须是合法 UTF-8,wine-exec 只能在 Linux 上用。没有它,测试很容易因为路径格式、容器名缺失、旧环境变量兼容等问题在不同机器上表现不一致。
TestEnvironment::is_remote20–22 ↗
fn is_remote(&self) -> bool
作用:判断这个测试环境是不是“远程”环境。这里的远程不是网络远程,而是指不直接跑在本机原生环境里,比如 Docker 或 WineExec。
数据流:进去的是一个 TestEnvironment 值 → 它检查这个值是不是 Local → 出来一个 true 或 false;Local 返回 false,Docker 和 WineExec 返回 true,不会改动任何数据。
调用关系:它是判断环境性质的小开关。get_remote_test_env 会先拿到完整环境,再用这个判断是否需要返回远程测试环境。
调用图:外部调用 1 个(matches!)。
TestEnvironment::docker_container_name24–29 ↗
fn docker_container_name(&self) -> Option<&str>
作用:如果当前测试跑在 Docker 里,它就取出 Docker 容器名;如果不是 Docker,就告诉调用者没有容器名。
数据流:进去的是一个 TestEnvironment → 它看里面是不是 Docker,并读取其中的 container_name → 出来的是 Option:Docker 时是 Some(容器名),Local 或 WineExec 时是 None;它只读取,不修改。
调用关系:这是给需要直接和 Docker 容器打交道的测试代码用的辅助入口。它不调用别的本文件函数,只把 TestEnvironment 里的信息安全地拿出来。
TestEnvironment::remote_cwd31–47 ↗
fn remote_cwd(&self, instance_id: &str) -> Result<Option<LegacyAppPathString>>
作用:为远程测试环境生成一个测试专用的当前工作目录路径。这样每个测试实例都有自己的目录,避免多个测试互相踩文件。
数据流:进去的是当前 TestEnvironment 和一个 instance_id → 如果是 Local,直接返回 None,因为本机不需要额外远程目录;如果是 Docker,就拼出 file:///tmp/codex-core-test-cwd-...;如果是 WineExec,就拼出 file:///C:/codex-core-test-cwd-... → 然后把 URI 解析成路径对象,并按当前环境的路径规则转换成旧接口需要的路径字符串 → 出来的是 Some(远程工作目录) 或 None;如果路径解析失败,会返回错误。
调用关系:它在测试准备远程工作目录时使用。它会调用 TestEnvironment::path_convention 来知道该用 Unix 风格还是 Windows 风格路径,也会借助 PathUri::parse 和 LegacyAppPathString::from_path_uri 做路径格式转换。
调用图:调用 3 个内部函数(path_convention, parse, from_path_uri);外部调用 1 个(format!)。
TestEnvironment::path_convention49–55 ↗
fn path_convention(&self) -> PathConvention
作用:告诉其他代码当前环境应该使用哪种路径写法。比如 Docker 用 Unix 的 /tmp,WineExec 用 Windows 的 C:\ 这类规则。
数据流:进去的是一个 TestEnvironment → 它按类型选择路径约定:Local 用本机默认规则,Docker 用 Posix,也就是 Unix/Linux 路径规则,WineExec 用 Windows 路径规则 → 出来的是 PathConvention,不会改动状态。
调用关系:它是路径转换时的规则提供者。TestEnvironment::remote_cwd 会调用它,确保生成的工作目录路径符合目标测试环境能看懂的格式。
调用图:调用 1 个内部函数(native);被 1 处调用(remote_cwd)。
test_environment58–71 ↗
fn test_environment() -> TestEnvironment
作用:读取真实的系统环境变量,并给出当前测试应该使用的 TestEnvironment。它是测试代码获取“我现在跑在哪里”的主要入口。
数据流:进去的不是普通参数,而是它从进程环境里读取三个环境变量:CODEX_TEST_ENVIRONMENT、CODEX_TEST_REMOTE_ENV、CODEX_TEST_REMOTE_ENV_CONTAINER_NAME → 它把这些值交给 parse_test_environment 解析 → 如果配置非法就直接报错停止测试;如果选择 wine-exec 但当前系统不是 Linux,也会立刻 panic → 出来的是一个明确的 TestEnvironment。
调用关系:这是外部测试代码最常用的入口之一。它把读取环境变量的活儿交给 std::env::var_os,把解释配置的活儿交给 parse_test_environment;get_remote_test_env 也会调用它再做进一步筛选。
调用图:调用 1 个内部函数(parse_test_environment);被 1 处调用(get_remote_test_env);外部调用 4 个(cfg!, matches!, panic!, var_os)。
get_remote_test_env73–76 ↗
fn get_remote_test_env() -> Option<TestEnvironment>
作用:只在当前测试确实需要远程环境时返回 TestEnvironment。本机测试时,它返回 None,让调用者可以简单地跳过远程专用操作。
数据流:进去没有显式参数 → 它先调用 test_environment 得到当前测试环境 → 再调用环境自身的 is_remote 判断是否远程 → 如果是 Docker 或 WineExec,出来 Some(环境);如果是 Local,出来 None。
调用关系:它是 test_environment 的一个更方便版本,适合那些“只有远程测试才需要做事”的代码。它依赖 test_environment 完成环境读取和校验。
调用图:调用 1 个内部函数(test_environment)。
parse_test_environment78–120 ↗
fn parse_test_environment(
configured_environment: Option<&OsStr>,
legacy_remote_environment: Option<&OsStr>,
docker_container: Option<&OsStr>,
) -> Result<TestEnvironment, String>
作用:把环境变量里的原始文字翻译成 TestEnvironment。它同时兼容旧的环境变量写法,并检查配置有没有写错。
数据流:进去的是三个可选环境变量值:主环境选择、旧版远程环境变量、Docker 容器名变量 → 它先确认主环境选择是合法 UTF-8 → 如果没有主环境选择,就用旧规则:有旧远程变量就当 Docker,没有就当 Local;如果主环境是 local、docker、wine-exec,就分别生成对应环境;Docker 情况下还会找容器名并检查不能为空 → 出来的是 TestEnvironment,或者一段说明配置错误的字符串。
调用关系:它是配置解释的核心,但不直接读取系统环境变量。test_environment 负责把环境变量读出来后交给它;它又把“字符串必须是 UTF-8 且不能为空”的检查交给 non_empty_utf8。
调用图:调用 1 个内部函数(non_empty_utf8);被 1 处调用(test_environment);外部调用 1 个(format!)。
non_empty_utf8122–130 ↗
fn non_empty_utf8(name: &str, value: &OsStr) -> Result<String, String>
作用:检查某个环境变量的值是不是能正常当文字读取,并且不是空白。它主要用来保证 Docker 容器名这类关键配置真的可用。
数据流:进去的是环境变量名和它的原始值 OsStr → 它先尝试把值转成 UTF-8 字符串;如果失败,返回“必须是合法 UTF-8”的错误;再去掉前后空白检查是不是空;如果为空,返回“不能为空”的错误 → 成功时出来一个普通 String。
调用关系:它是 parse_test_environment 的小帮手,专门做输入清洗。parse_test_environment 在需要读取容器名或旧版远程环境值时调用它,避免后续代码拿到乱码或空名字。
调用图:被 1 处调用(parse_test_environment);外部调用 4 个(to_str, to_string, trim, format!)。
core/tests/common/hooks.rs源码 ↗
hooks 可以理解成“自动触发的小脚本”:程序遇到某些时机,就会去执行它们。真实使用时,不能随便信任一个脚本,所以系统会记录每个 hook 的可信哈希值(哈希值可以理解成文件内容的指纹)。这个文件就是给测试开绿灯用的:它先打开 CodexHooks 这个功能开关,再从配置里找出当前能发现的 hook,确认至少找到了一个,然后把这些 hook 的当前指纹写进用户配置里的 hooks.state。这样后面的测试就像用户已经点过“信任”一样,可以直接跑流程。它不会真正改磁盘文件,而是生成一份更新过的配置层栈,放回测试用的 Config 里。
trust_discovered_hooks9–25 ↗
fn trust_discovered_hooks(config: &mut Config)
作用:这个函数用于测试开始前自动信任“当前能发现的 hook”。有人写测试时,如果不想手动准备可信记录,就可以调用它,让测试配置自动补齐这一步。
数据流:输入是一份可修改的测试配置 Config。它先打开 CodexHooks 功能开关,再用这份配置去列出 hook;如果一个 hook 都没找到,就让测试直接失败,避免后面假装成功。找到 hook 后,它把这些 hook 交给 trust_hooks,最后 Config 里的配置层栈会变成“已经信任这些 hook”的版本。
调用关系:它通常由 configure 和 enable_hooks_and_rmcp_server 在准备测试环境时调用。它自己不直接写信任记录,而是先调用外部的 list_hooks 找 hook,再把结果交给 trust_hooks 去改配置。
调用图:调用 1 个内部函数(trust_hooks);被 2 处调用(configure, enable_hooks_and_rmcp_server);外部调用 3 个(assert!, list_hooks, default)。
trust_hooks27–30 ↗
fn trust_hooks(config: &mut Config, hooks: Vec<HookListEntry>)
作用:这个函数把指定的一批 hook 写成“可信”状态,更新到测试配置里。它适合在测试已经知道要信任哪些 hook 时使用。
数据流:输入是一份可修改的 Config,以及一组 HookListEntry。它读取 Config 里原来的配置层栈和 codex_home 路径,把这些信息连同 hook 列表交给 trusted_config_layer_stack。得到新的配置层栈后,它直接替换 Config 里的 config_layer_stack。
调用关系:trust_discovered_hooks 会在自动发现 hook 后调用它;trust_plugin_hooks 也会在别的测试场景里调用它。它本身像一个转接头,把“改 Config”这件事委托给 trusted_config_layer_stack 完成。
调用图:调用 1 个内部函数(trusted_config_layer_stack);被 2 处调用(trust_discovered_hooks, trust_plugin_hooks)。
trusted_config_layer_stack32–67 ↗
fn trusted_config_layer_stack(
config_layer_stack: &ConfigLayerStack,
codex_home: &AbsolutePathBuf,
hooks: Vec<HookListEntry>,
) -> ConfigLayerStack
作用:这个函数真正负责构造一份“用户配置里已经信任这些 hook”的配置层栈。它不只是标记一个开关,而是把每个 hook 的当前指纹写到配置结构中。
数据流:输入是原来的配置层栈、Codex 主目录路径,以及要信任的 hook 列表。它先取出当前用户配置;如果没有,就新建一个空的 TOML 表。然后一路确保存在 hooks → state 这些配置表。接着对每个 hook,写入 trusted_hash,也就是这个 hook 当前内容的指纹。最后它用 codex_home 拼出配置文件路径,并返回一份带有新用户配置的 ConfigLayerStack;原输入不会被直接改坏,而是生成更新后的版本。
调用关系:trust_hooks 会调用它来完成主要工作;install_mcp_permission_request_hook 也会在安装测试 hook 后用它来补可信记录。它会用 get_active_user_layer 读取原用户配置,用 join 拼配置文件路径,再用 with_user_config 生成新的配置层栈。
调用图:调用 3 个内部函数(get_active_user_layer, with_user_config, join);被 2 处调用(install_mcp_permission_request_hook, trust_hooks);外部调用 3 个(default, String, Table)。
core/tests/common/tracing.rs源码 ↗
这是一段专门给测试用的追踪初始化代码。追踪可以理解成给一连串操作贴上同一张“快递单号”,这样系统跑起来后,测试能看出哪些动作属于同一次请求或同一个任务。文件里先设置 OpenTelemetry 的上下文传播器,也就是规定“快递单号”怎么在不同步骤之间传递。然后创建一个测试用的 tracer provider(追踪器工厂),再用给定名字拿到 tracer(真正打追踪标记的工具),最后把它接到 Rust 的 tracing 日志/追踪框架里,设成当前默认使用的追踪系统。返回的 TestTracingContext 很重要:它把 provider 和 guard 留住,避免测试还没跑完追踪系统就被释放或默认设置被撤掉。
install_test_tracing14–26 ↗
fn install_test_tracing(tracer_name: &str) -> TestTracingContext
作用:这个函数在测试开始时安装一套临时的追踪环境,让后面的代码能产生和传递 trace id。测试用它来验证任务提交、调度、WebSocket 等流程里,追踪上下文是不是按预期保留下来了。
数据流:输入是一个 tracer_name,也就是给这套测试追踪器起的名字。函数先设置全局的追踪上下文传播规则,然后创建一个追踪器工厂,从里面取出指定名字的追踪器,再把这个追踪器接到当前线程默认的 tracing 订阅器上。输出是 TestTracingContext;它不直接拿来调用,而是靠里面保存的 provider 和 guard 维持追踪设置的生命周期,等这个对象被丢掉时,临时默认设置也会随之结束。
调用关系:它通常在一批需要检查 trace id 的测试开头被调用,比如 turn 捕获当前 span、提交任务继承追踪上下文、WebSocket 复用连接但每次携带不同追踪信息等测试。它自己把底层搭建工作交给 OpenTelemetry 和 tracing_subscriber 的 builder、registry、layer、set_default 等工具完成;测试代码只需要调用它,就能得到一个已经接好的追踪环境。
调用图:被 8 处调用(new_default_turn_captures_current_span_trace_id, regular_turn_emits_turn_started_with_trace_id_without_waiting_for_startup_prewarm, spawn_task_turn_span_inherits_dispatch_trace_context, submission_dispatch_span_prefers_submission_trace_context, submission_dispatch_span_uses_debug_for_realtime_audio, submit_with_id_captures_current_span_trace_context, responses_websocket_preconnect_does_not_replace_turn_trace_payload, responses_websocket_reuses_connection_with_per_turn_trace_payloads);外部调用 5 个(builder, new, set_text_map_propagator, layer, registry)。
core/tests/common/process.rs源码 ↗
这个文件主要服务于测试场景:有些测试会启动一个后台进程,进程会把自己的 pid(进程编号,可以理解成系统给每个运行中程序贴的号码牌)写进文件。测试需要先等这个号码牌出现,再确认进程是否还在跑,最后还要等它退出。这里的做法很像在门口等人:每 25 毫秒看一眼文件或进程状态,但最多只等 2 秒,超过就报错,避免测试卡死。wait_for_pid_file 负责等 pid 文件写好;process_is_alive 用系统的 kill -0 命令探测进程是否存在,这个命令不会真的杀进程,只是“敲门确认人在不在”;wait_for_process_exit 则包一层超时,等待进程真的消失。
wait_for_pid_file6–22 ↗
async fn wait_for_pid_file(path: &Path) -> anyhow::Result<String>
作用:等待某个 pid 文件出现并且里面有内容。测试启动后台进程后会用它拿到进程编号,避免文件还没写好就继续往下跑。
数据流:进去的是一个文件路径 → 它反复读取这个文件,如果读到了非空内容,就去掉前后空白并当作 pid 返回 → 如果 2 秒内一直读不到有效内容,就返回一个带有“等待 pid 文件超时”说明的错误;它不会修改文件,只是读取。
调用关系:它会被 unified_exec_interrupt_preserves_long_running_session 和 unified_exec_keeps_long_running_session_after_turn_end 这类测试调用,用来确认测试中的长时间运行会话已经真正启动。它自己依赖文件读取、短暂睡眠和超时机制来完成等待。
调用图:被 2 处调用(unified_exec_interrupt_preserves_long_running_session, unified_exec_keeps_long_running_session_after_turn_end);外部调用 5 个(from_millis, from_secs, read_to_string, sleep, timeout)。
process_is_alive24–30 ↗
fn process_is_alive(pid: &str) -> anyhow::Result<bool>
作用:检查一个指定 pid 的进程现在是否还活着。它让测试可以问操作系统一句“这个进程还在吗”,而不是靠猜。
数据流:进去的是 pid 字符串 → 它运行系统命令 kill -0 <pid>,这个命令只检查权限和进程存在性,不会结束进程 → 如果命令成功,就返回 true,表示进程还在;如果命令不成功,就返回 false;如果连命令都运行不了,就返回错误。
调用关系:它被 wait_for_process_exit_inner 反复调用,作为判断进程是否已经退出的探针。这个函数本身不负责等待,只负责做一次“人在不在”的检查。
调用图:被 1 处调用(wait_for_process_exit_inner);外部调用 1 个(new)。
wait_for_process_exit_inner32–39 ↗
async fn wait_for_process_exit_inner(pid: String) -> anyhow::Result<()>
作用:持续等待某个进程退出。它是内部辅助函数,负责一遍遍检查进程是否还活着,并在进程消失时结束。
数据流:进去的是一个 pid 字符串 → 它循环调用 process_is_alive 检查进程状态;如果发现进程已经不在,就返回成功;如果还在,就睡 25 毫秒后再查 → 它不直接设置总超时时间,只负责一直等到进程退出或检查时报错。
调用关系:它由 wait_for_process_exit 调用,承担真正的轮询等待工作。它把每次状态判断交给 process_is_alive,自己只负责安排反复检查和短暂休息。
调用图:调用 1 个内部函数(process_is_alive);被 1 处调用(wait_for_process_exit);外部调用 2 个(from_millis, sleep)。
wait_for_process_exit41–48 ↗
async fn wait_for_process_exit(pid: &str) -> anyhow::Result<()>
作用:对外提供“最多等 2 秒让进程退出”的测试工具。这样如果进程没有按预期结束,测试会及时失败,而不是一直挂着。
数据流:进去的是 pid 字符串引用 → 它先复制成自己的字符串,再启动 wait_for_process_exit_inner 去反复检查进程是否退出,同时套上 2 秒超时 → 如果进程按时退出,就返回成功;如果等太久,就返回“等待进程退出超时”的错误。
调用关系:它会被 unified_exec_interrupt_preserves_long_running_session 和 unified_exec_keeps_long_running_session_after_turn_end 这些测试调用,用在测试收尾或验证阶段。它自己不直接查进程,而是把具体等待工作交给 wait_for_process_exit_inner,再负责加上安全的超时外壳。
调用图:调用 1 个内部函数(wait_for_process_exit_inner);被 2 处调用(unified_exec_interrupt_preserves_long_running_session, unified_exec_keeps_long_running_session_after_turn_end);外部调用 2 个(from_secs, timeout)。
传输和模拟服务器
这些文件提供可复用的模拟 HTTP/SSE 基础设施和假外部服务,支撑端到端请求和流式测试。
core/tests/common/apps_test_server.rs源码 ↗
这个文件像一个测试用的“假前台”。真实运行时,Codex 会去远端服务询问有哪些应用、有哪些工具,并通过 JSON-RPC(一种用 JSON 包装请求和回复的通信格式)调用工具。测试时如果每次都连真实服务,会慢、不稳定,也难控制结果。所以这里用 wiremock(测试里假装成服务器的工具)预先挂好几个接口:OAuth 信息、应用目录、Apps 的 JSON-RPC 入口。它还准备了日历工具、文件上传工具、只给应用界面看的工具,以及可搜索的大量假工具。测试可以用这些固定回复来验证 Codex 是否正确启用 Apps、是否隐藏不该给模型看的工具、是否把工具调用参数和元数据传出去。
AppsTestServer::mount62–64 ↗
async fn mount(server: &MockServer) -> Result<Self>
作用:用默认的“Calendar”连接器名字,把假的 Apps 服务挂到测试服务器上。测试只想要一个普通日历应用时,会用这个最省事的入口。
数据流:输入是一个 MockServer(测试用假服务器)→ 它把工作交给 AppsTestServer::mount_with_connector_name,并使用默认连接器名 → 返回 AppsTestServer,里面记着这个假服务器的网址。
调用关系:很多 Apps 相关测试会先调用它来准备环境;它自己不直接挂接口,而是交给 AppsTestServer::mount_with_connector_name 统一完成 OAuth、目录和 JSON-RPC 端点的安装。
调用图:被 12 处调用(includes_apps_guidance_as_developer_message_for_chatgpt_auth, omits_apps_guidance_for_api_key_auth_even_when_feature_enabled, omits_apps_guidance_when_configured_off, approved_mcp_tool_call_metadata_records_prior_user_input_request, apps_default_auto_review_routes_actual_mcp_approval_to_guardian, mcp_tool_call_metadata_records_prior_request_user_input_tool, codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook, codex_apps_file_params_upload_environment_files_before_mcp_tool_call, request_plugin_install_is_available_without_search_tool_after_discovery_attempts, always_defer_feature_hides_small_app_tool_sets (+2 more));外部调用 1 个(mount_with_connector_name)。
AppsTestServer::mount_searchable66–80 ↗
async fn mount_searchable(server: &MockServer) -> Result<Self>
作用:搭一个“工具很多、需要搜索”的假 Apps 服务。它用来测试模型不是直接看到所有工具,而是通过搜索找到合适工具的场景。
数据流:输入是假服务器 → 依次挂 OAuth 元数据、连接器目录、Apps JSON-RPC 接口,并把 searchable 设为 true → 返回带有服务器地址的 AppsTestServer。
调用关系:搜索工具相关测试会调用它。它把准备工作拆给 mount_oauth_metadata、mount_connectors_directory 和 mount_streamable_http_json_rpc,最后用 server.uri() 记录测试服务地址。
调用图:调用 3 个内部函数(mount_connectors_directory, mount_oauth_metadata, mount_streamable_http_json_rpc);被 9 处调用(code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools, search_tool_adds_discovery_instructions_to_tool_description, search_tool_enabled_by_default_adds_tool_search, search_tool_hides_apps_tools_without_search, tool_search_indexes_only_enabled_non_app_mcp_tools, tool_search_matches_mcp_tools_by_distinct_name_description_and_schema_terms, tool_search_returns_deferred_tools_without_follow_up_tool_injection, tool_search_surfaced_mcp_tool_errors_are_returned_to_model, tool_search_uses_non_app_mcp_server_instructions_as_namespace_description);外部调用 1 个(uri)。
AppsTestServer::mount_with_connector_name82–99 ↗
async fn mount_with_connector_name(
server: &MockServer,
connector_name: &str,
) -> Result<Self>
作用:搭一个假 Apps 服务,但允许测试指定连接器显示名。这样可以检查 Codex 在提示词或工具说明里是否正确显示应用名字。
数据流:输入是假服务器和连接器名字 → 挂 OAuth 信息、应用目录和 JSON-RPC 接口,并把指定名字放进工具元数据 → 返回记录服务器地址的 AppsTestServer。
调用关系:需要测试不同应用名称、插件提及和开发者消息渲染的用例会调用它;AppsTestServer::mount 也通过它来实现默认日历服务。
调用图:调用 3 个内部函数(mount_connectors_directory, mount_oauth_metadata, mount_streamable_http_json_rpc);被 3 处调用(capability_sections_render_in_developer_message_in_order, explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth, explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins);外部调用 1 个(uri)。
AppsTestServer::mount_with_app_only_tool101–118 ↗
async fn mount_with_app_only_tool(
server: &MockServer,
tool_loading: AppsTestToolLoading,
) -> Result<Self>
作用:搭一个包含“只给应用界面用”的工具的假服务。它用于确认这类工具不会暴露给代码模式模型,也不能被模型直接调用。
数据流:输入是假服务器和工具加载方式 Direct 或 Searchable → 挂基础接口,再按加载方式决定是否把工具做成可搜索,并要求包含 app-only 工具 → 返回测试服务地址。
调用关系:专门测试 app-only 工具可见性的用例会调用它;它仍然复用 mount_oauth_metadata、mount_connectors_directory 和 mount_streamable_http_json_rpc 来搭出完整假服务。
调用图:调用 3 个内部函数(mount_connectors_directory, mount_oauth_metadata, mount_streamable_http_json_rpc);被 2 处调用(app_only_tools_are_not_visible_or_runnable_by_code_mode_model, app_only_tools_are_not_visible_or_runnable_by_direct_model_calls);外部调用 2 个(uri, matches!)。
configure_search_capable_model121–131 ↗
fn configure_search_capable_model(config: &mut Config)
作用:把测试配置里的模型改成支持工具搜索的模型。没有这一步,搜索型 Apps 工具测试可能不会走搜索分支。
数据流:输入是可修改的 Config → 读取内置模型目录,找到 gpt-5.4,把配置模型设为它,并把 supports_search_tool 标成 true → 修改后的 Config 能触发搜索工具能力。
调用关系:configure_search_capable_apps 会调用它;它依赖 bundled_models_response 拿到项目内置的模型列表。
调用图:被 1 处调用(configure_search_capable_apps);外部调用 1 个(bundled_models_response)。
configure_apps133–139 ↗
fn configure_apps(config: &mut Config, apps_base_url: &str)
作用:在测试配置里打开 Apps 功能,并告诉 Codex 假 Apps 服务在哪里。它是所有 Apps 测试配置的基础开关。
数据流:输入是 Config 和 Apps 服务地址 → 启用 Feature::Apps,并把 chatgpt_base_url 改成假服务器地址 → 后续 Codex 请求会打到测试服务器而不是真实网络。
调用关系:configure_search_capable_apps 和 apps_enabled_builder 都会用它;它负责最基础的 Apps 开启工作。
调用图:被 1 处调用(configure_search_capable_apps)。
configure_search_capable_apps141–144 ↗
fn configure_search_capable_apps(config: &mut Config, apps_base_url: &str)
作用:一次性打开 Apps,并把模型调成支持搜索。它适合需要同时测试 Apps 和工具搜索的用例。
数据流:输入是 Config 和 Apps 地址 → 先用 configure_apps 打开 Apps、设置地址,再用 configure_search_capable_model 设置搜索模型 → 输出是已被原地改好的测试配置。
调用关系:search_capable_apps_builder 会调用它,把配置步骤打包进测试 Codex 构建器。
调用图:调用 2 个内部函数(configure_apps, configure_search_capable_model)。
apps_enabled_builder146–151 ↗
fn apps_enabled_builder(apps_base_url: impl Into<String>) -> TestCodexBuilder
作用:创建一个已经登录、已经开启 Apps 的测试 Codex 构建器。测试可以直接拿它启动一个会连假 Apps 服务的 Codex。
数据流:输入是 Apps 服务地址 → 创建 test_codex 构建器,塞入测试用假 ChatGPT 登录信息,再注册配置修改函数来开启 Apps → 输出 TestCodexBuilder。
调用关系:文件上传和直接模型调用相关测试会用它;它把认证准备交给 create_dummy_chatgpt_auth_for_testing,把配置修改交给 configure_apps。
调用图:调用 2 个内部函数(test_codex, create_dummy_chatgpt_auth_for_testing);被 3 处调用(codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook, codex_apps_file_params_upload_environment_files_before_mcp_tool_call, app_only_tools_are_not_visible_or_runnable_by_direct_model_calls);外部调用 1 个(into)。
search_capable_apps_builder153–158 ↗
fn search_capable_apps_builder(apps_base_url: impl Into<String>) -> TestCodexBuilder
作用:创建一个已经登录、开启 Apps、并支持工具搜索的测试 Codex 构建器。它是搜索型 Apps 测试的常用入口。
数据流:输入是 Apps 服务地址 → 创建 test_codex 构建器,加上测试登录信息,并注册 configure_search_capable_apps → 输出可以启动搜索型 Apps 场景的 TestCodexBuilder。
调用关系:大量工具搜索、延迟工具注入、审批和元数据记录测试会调用它;它把低层配置细节包起来,避免每个测试重复写。
调用图:调用 2 个内部函数(test_codex, create_dummy_chatgpt_auth_for_testing);被 14 处调用(app_only_tools_are_not_visible_or_runnable_by_code_mode_model, approved_mcp_tool_call_metadata_records_prior_user_input_request, apps_default_auto_review_routes_actual_mcp_approval_to_guardian, mcp_tool_call_metadata_records_prior_request_user_input_tool, always_defer_feature_hides_small_app_tool_sets, explicit_app_mentions_respect_always_defer, search_tool_adds_discovery_instructions_to_tool_description, search_tool_enabled_by_default_adds_tool_search, search_tool_hides_apps_tools_without_search, tool_search_indexes_only_enabled_non_app_mcp_tools (+4 more));外部调用 1 个(into)。
apps_tool_call_id160–166 ↗
fn apps_tool_call_id(body: &Value) -> Option<&str>
作用:从一次 Apps 工具调用的 JSON 请求里取出 call_id。call_id 可以理解成这次调用的小票号,用来精确找到同一次调用。
数据流:输入是一段 JSON 请求体 → 按 params._meta._codex_apps.call_id 这条路径往里找 → 找到就返回字符串引用,找不到就返回空值。
调用关系:它是检查测试记录时的小工具;recorded_apps_tool_call_by_call_id 用它从一堆请求里筛出指定 call_id 的那一次。
调用图:外部调用 1 个(get)。
recorded_apps_tool_calls168–181 ↗
async fn recorded_apps_tool_calls(server: &MockServer) -> Vec<Value>
作用:从假服务器记录的所有请求里,挑出真正的 Apps 工具调用请求。测试用它确认 Codex 到底有没有调用工具、请求体长什么样。
数据流:输入是假服务器 → 读取服务器收到过的请求,解析每个请求体为 JSON,只保留路径是 /api/codex/apps 且 method 是 tools/call 的请求 → 输出这些请求体组成的列表。
调用关系:recorded_apps_tool_call_by_call_id 和 recorded_apps_tool_call_by_name 都以它为基础;它直接向 MockServer 要收到过的请求记录。
调用图:被 2 处调用(recorded_apps_tool_call_by_call_id, recorded_apps_tool_call_by_name);外部调用 1 个(received_requests)。
recorded_apps_tool_call_by_call_id183–198 ↗
async fn recorded_apps_tool_call_by_call_id(server: &MockServer, call_id: &str) -> Value
作用:按 call_id 找到唯一一次 Apps 工具调用。它用于测试某个具体用户请求最终对应的工具调用有没有传对元数据。
数据流:输入是假服务器和 call_id → 先取出所有 Apps 工具调用,再用 apps_tool_call_id 过滤 → 如果不是正好一条就让测试失败,正好一条就返回那段 JSON。
调用关系:审批、Guardian 路由、先前用户输入记录、延迟工具调用等测试会调用它;它把收集请求的工作交给 recorded_apps_tool_calls。
调用图:调用 1 个内部函数(recorded_apps_tool_calls);被 4 处调用(approved_mcp_tool_call_metadata_records_prior_user_input_request, apps_default_auto_review_routes_actual_mcp_approval_to_guardian, mcp_tool_call_metadata_records_prior_request_user_input_tool, tool_search_returns_deferred_tools_without_follow_up_tool_injection);外部调用 1 个(assert_eq!)。
recorded_apps_tool_call_by_name200–215 ↗
async fn recorded_apps_tool_call_by_name(server: &MockServer, tool_name: &str) -> Value
作用:按工具名字找到唯一一次 Apps 工具调用。它常用于确认某个指定工具是否真的被调用,并查看传入参数。
数据流:输入是假服务器和工具名 → 读取所有 Apps 工具调用,筛选 params.name 等于目标工具名的请求 → 要求结果正好一条,然后返回该请求 JSON。
调用关系:文件上传相关测试会用它确认上传后的工具调用是否发生;它复用 recorded_apps_tool_calls 来拿到候选请求。
调用图:调用 1 个内部函数(recorded_apps_tool_calls);被 1 处调用(codex_apps_file_params_upload_environment_files_before_mcp_tool_call);外部调用 1 个(assert_eq!)。
mount_oauth_metadata217–227 ↗
async fn mount_oauth_metadata(server: &MockServer)
作用:给假服务器挂上 OAuth 元数据接口。OAuth 可以理解成“授权登录”的标准流程,Codex 会先问这里该去哪里授权、哪里换令牌。
数据流:输入是假服务器 → 注册一个 GET /.well-known/oauth-authorization-server/mcp 的固定回复,里面包含 authorize 和 token 地址 → 之后测试中的 Codex 访问这个路径会拿到假授权信息。
调用关系:所有 mount_* 搭服务函数都会先调用它;它用 wiremock 的 Mock、method、path 和 ResponseTemplate 把这条假接口装到服务器上。
调用图:被 3 处调用(mount_searchable, mount_with_app_only_tool, mount_with_connector_name);外部调用 5 个(given, new, json!, method, path)。
mount_connectors_directory229–258 ↗
async fn mount_connectors_directory(server: &MockServer)
作用:给假服务器挂上“应用目录”接口,让 Codex 能发现有哪些应用可用。这里固定返回 Google Calendar 和 Gmail 两个示例应用。
数据流:输入是假服务器 → 注册 /connectors/directory/list 返回两个应用,注册 /connectors/directory/list_workspace 返回空列表 → Codex 查询目录时会得到稳定、可预测的结果。
调用关系:mount_searchable、mount_with_app_only_tool 和 mount_with_connector_name 都会调用它;它负责模拟真实服务里的应用发现阶段。
调用图:被 3 处调用(mount_searchable, mount_with_app_only_tool, mount_with_connector_name);外部调用 5 个(given, new, json!, method, path)。
mount_streamable_http_json_rpc260–277 ↗
async fn mount_streamable_http_json_rpc(
server: &MockServer,
connector_name: String,
connector_description: String,
searchable: bool,
include_app_only_tool: bool,
)
作用:给假服务器挂上 Apps 的 JSON-RPC 主入口。Codex 后续初始化、列工具、调用工具,都会打到这个入口。
数据流:输入是假服务器、连接器名字、描述、是否可搜索、是否包含 app-only 工具 → 注册一个匹配 /api/codex/apps 的 POST 接口,并指定 CodexAppsJsonRpcResponder 来动态生成回复 → 后续请求由 responder 按 method 处理。
调用关系:各个 mount_* 函数在挂完 OAuth 和目录后都会调用它;它把真正判断 JSON-RPC 请求内容的工作交给 CodexAppsJsonRpcResponder::respond。
调用图:被 3 处调用(mount_searchable, mount_with_app_only_tool, mount_with_connector_name);外部调用 3 个(given, method, path_regex)。
CodexAppsJsonRpcResponder::respond287–527 ↗
fn respond(&self, request: &Request) -> ResponseTemplate
作用:这是假的 Apps JSON-RPC 服务器的大脑。它查看请求里的 method,按初始化、列工具、调用工具等不同命令返回对应的假结果。
数据流:输入是 wiremock 收到的 HTTP 请求 → 先把请求体解析成 JSON;如果 JSON 或 method 不对就返回 400;如果是 initialize 就返回协议和服务器信息;如果是 tools/list 就返回日历工具列表,并按配置追加大量可搜索工具或 app-only 工具;如果是 tools/call 就把工具名、标题、时间、文件 id 和元数据拼成一次成功调用结果;未知方法返回 JSON-RPC 的 method not found 错误 → 输出 ResponseTemplate,也就是假服务器发回给 Codex 的响应。
调用关系:mount_streamable_http_json_rpc 把它安装到 /api/codex/apps;测试运行中只要 Codex POST 到这个路径,wiremock 就会调用它来现场生成回复。
调用图:外部调用 3 个(new, json!, from_slice)。
core/tests/common/context_snapshot.rs源码 ↗
测试里经常要确认“这次发给模型的上下文是不是对”。但原始请求是 JSON,里面有用户消息、工具调用、图片占位、推理摘要、环境信息等,既长又容易因为临时路径、时间戳不同而变化。这个文件就像一台“拍照前的整理机”:先按条目编号,把不同类型的内容翻译成简短文字;再按选项决定是显示全文、只显示类型,还是把敏感或不稳定内容换成固定占位符;最后还能把两份请求做成只显示变化行的差异文本。它还特别处理了 AGENTS.md、能力说明、环境上下文、系统技能路径、UUID、沙箱名等内容,保证快照既能说明问题,又不会被无关细节干扰。
ContextSnapshotOptions::default31–37 ↗
fn default() -> Self
作用:生成一份默认的快照设置。默认会用“遮盖后的文字”模式,也不会主动删掉能力说明或 AGENTS.md 用户上下文。
数据流:进去没有额外输入 → 它填好三项默认开关:渲染模式是 RedactedText,两个删除开关都是 false → 出来一份可直接传给快照格式化函数的 ContextSnapshotOptions。
调用关系:很多测试和测试辅助函数会先调用它拿到基础设置,再按需要接着调用 render_mode、strip_capability_instructions 或 strip_agents_md_user_context 改成特定场景。
调用图:被 19 处调用(guardian_snapshot_options, fork_startup_context_then_first_turn_diff_snapshot, full_text_mode_normalizes_crlf_line_endings, full_text_mode_preserves_unredacted_text, image_only_message_is_rendered_as_non_text_span, kind_with_text_prefix_mode_normalizes_crlf_line_endings, mixed_text_and_image_message_keeps_image_span, redacted_text_mode_keeps_canonical_placeholders, redacted_text_mode_keeps_capability_instruction_placeholders, redacted_text_mode_normalizes_environment_context_with_subagents (+9 more))。
ContextSnapshotOptions::render_mode41–44 ↗
fn render_mode(mut self, render_mode: ContextSnapshotRenderMode) -> Self
作用:修改快照的显示方式,比如显示全文、只显示类型,或只显示文字前缀。这样同一套格式化工具可以适配不同测试想看的细节程度。
数据流:进去一份已有选项和一个新的 ContextSnapshotRenderMode → 它把选项里的 render_mode 字段换成新模式 → 出来修改后的选项,方便继续链式设置。
调用关系:通常跟 ContextSnapshotOptions::default 连着用,测试先拿默认设置,再用它切换到 FullText、KindOnly 或 KindWithTextPrefix 等模式。
ContextSnapshotOptions::strip_capability_instructions46–49 ↗
fn strip_capability_instructions(mut self) -> Self
作用:打开“删掉能力说明片段”的开关。测试如果只关心其它上下文,就可以避免 apps、skills、plugins 这类说明把快照撑得很长。
数据流:进去一份已有选项 → 它把 strip_capability_instructions 设为 true → 出来一份会在格式化 developer 消息时跳过能力说明的选项。
调用关系:它影响 format_response_items_snapshot 对 developer 消息的处理;相关测试会用它确认能力说明能被省略。
ContextSnapshotOptions::strip_agents_md_user_context51–54 ↗
fn strip_agents_md_user_context(mut self) -> Self
作用:打开“删掉用户消息里 AGENTS.md 说明片段”的开关。这样测试可以只看真正关心的用户上下文,比如环境信息。
数据流:进去一份已有选项 → 它把 strip_agents_md_user_context 设为 true → 出来一份会在格式化 user 消息时跳过 AGENTS.md 片段的选项。
调用关系:它影响 format_response_items_snapshot 对 user 消息内容数组的筛选;对应测试验证 AGENTS.md 片段会被拿掉。
format_request_input_snapshot57–63 ↗
fn format_request_input_snapshot(
request: &ResponsesRequest,
options: &ContextSnapshotOptions,
) -> String
作用:把一个完整 ResponsesRequest 里的 input 部分变成快照文字。测试想看“请求输入列表长什么样”时会用它。
数据流:进去一个 ResponsesRequest 和快照选项 → 它从请求里读取 input 列表 → 把列表交给 format_response_items_snapshot → 出来多行文字快照。
调用关系:它是对 format_response_items_snapshot 的一层方便包装;上层测试拿着请求对象时不用自己先拆 input。
调用图:调用 2 个内部函数(format_response_items_snapshot, input)。
format_response_items_snapshot65–209 ↗
fn format_response_items_snapshot(items: &[Value], options: &ContextSnapshotOptions) -> String
作用:这是本文件最核心的整理函数:把一串响应/请求条目 JSON 翻译成稳定、紧凑、适合断言比较的文字。
数据流:进去一组 JSON 条目和快照选项 → 它逐个看 type 字段,按 message、function_call、function_call_output、local_shell_call、reasoning、compaction 等类型分别整理;文本会按选项遮盖、截断或保留,非文本内容会变成尖括号占位 → 出来按编号排列的多行快照字符串。
调用关系:format_request_input_snapshot 会把请求输入交给它;很多单元测试也直接调用它,检查各种消息、图片、环境上下文和能力说明能不能被正确显示或隐藏。
调用图:被 13 处调用(format_request_input_snapshot, full_text_mode_normalizes_crlf_line_endings, full_text_mode_preserves_unredacted_text, image_only_message_is_rendered_as_non_text_span, kind_with_text_prefix_mode_normalizes_crlf_line_endings, mixed_text_and_image_message_keeps_image_span, redacted_text_mode_keeps_canonical_placeholders, redacted_text_mode_keeps_capability_instruction_placeholders, redacted_text_mode_normalizes_environment_context_with_subagents, redacted_text_mode_normalizes_system_skill_temp_paths (+3 more));外部调用 1 个(iter)。
format_labeled_requests_snapshot211–227 ↗
fn format_labeled_requests_snapshot(
scenario: &str,
sections: &[(&str, &ResponsesRequest)],
options: &ContextSnapshotOptions,
) -> String
作用:把多个带标题的请求快照拼成一份报告。适合比较一个场景里的几个阶段,比如启动前后、第一次对话前后。
数据流:进去场景名、若干个“标题 + ResponsesRequest”、以及选项 → 它分别调用 format_request_input_snapshot 生成每段内容,再加上 ## 标题和 Scenario 头 → 出来一份分区清楚的文字快照。
调用关系:一些上下文差异测试会用它把多个请求排在一起看;它内部依赖 format_request_input_snapshot 完成每个请求的具体渲染。
调用图:被 5 处调用(fork_startup_context_then_first_turn_diff_snapshot, format_labeled_requests_snapshot, format_labeled_requests_snapshot, format_labeled_requests_snapshot, new_context_tool_starts_new_window_before_follow_up);外部调用 2 个(iter, format!)。
format_labeled_items_snapshot229–245 ↗
fn format_labeled_items_snapshot(
scenario: &str,
sections: &[(&str, &[Value])],
options: &ContextSnapshotOptions,
) -> String
作用:把多组已经拆好的 JSON 条目按标题拼成一份快照报告。适合测试手里没有完整请求、只有条目列表的情况。
数据流:进去场景名、若干个“标题 + 条目数组”、以及选项 → 它对每组条目调用 format_response_items_snapshot,再加标题和场景名 → 出来一份多区块文本。
调用关系:调用方如 assert_two_responses_input_snapshot 会用它对比两组响应输入;它把具体条目格式化工作交给 format_response_items_snapshot。
调用图:被 1 处调用(assert_two_responses_input_snapshot);外部调用 2 个(iter, format!)。
format_request_body_diff_snapshot251–263 ↗
fn format_request_body_diff_snapshot(
scenario: &str,
before_title: &str,
before_request: &ResponsesRequest,
after_title: &str,
after_request: &ResponsesRequest,
options: &Cont
作用:比较两份完整请求 JSON,只输出变化过的行。这样测试失败时不用翻完整大 JSON,也能看见关键变化。
数据流:进去场景名、前后两个标题、前后两个 ResponsesRequest、以及选项 → 它先分别生成规范化后的请求 body 文本,再用差异函数找出删掉和新增的行 → 出来带 Scenario、---、+++ 和 +/- 行的差异快照。
调用关系:它串起 format_request_body_snapshot 和 format_changed_lines_diff;适合“请求整体应该基本一致,只允许某些字段变化”的测试。
调用图:调用 2 个内部函数(format_changed_lines_diff, format_request_body_snapshot);外部调用 1 个(format!)。
format_request_body_snapshot265–272 ↗
fn format_request_body_snapshot(
request: &ResponsesRequest,
options: &ContextSnapshotOptions,
) -> String
作用:把完整请求体 JSON 变成稳定的漂亮文本。它不只是打印 JSON,还会先把不稳定内容整理掉。
数据流:进去一个 ResponsesRequest 和选项 → 它读取 body_json,递归规范化里面的值,然后用 serde_json 的 pretty 格式转成缩进 JSON 字符串 → 出来可比较的请求体文本。
调用关系:它只被 format_request_body_diff_snapshot 调用,作为生成差异前的准备步骤;真正递归清理 JSON 的活交给 canonicalize_json_snapshot_value。
调用图:调用 2 个内部函数(canonicalize_json_snapshot_value, body_json);被 1 处调用(format_request_body_diff_snapshot);外部调用 1 个(to_string_pretty)。
canonicalize_json_snapshot_value274–295 ↗
fn canonicalize_json_snapshot_value(value: &mut Value, options: &ContextSnapshotOptions)
作用:递归清理 JSON 值,让请求体快照稳定。它会排序对象字段,并把字符串里的动态内容换成固定写法。
数据流:进去一个可修改的 JSON 值和选项 → 如果是数组就逐项处理;如果是对象就先按键名排序再放回;如果是字符串就调用 format_snapshot_json_string 清理;数字、布尔值和 null 不动 → 出来同一个 JSON 值被原地改成更适合快照比较的版本。
调用关系:format_request_body_snapshot 在打印 JSON 前调用它;它遇到字符串时把工作交给 format_snapshot_json_string。
调用图:调用 1 个内部函数(format_snapshot_json_string);被 1 处调用(format_request_body_snapshot);外部调用 1 个(take)。
format_snapshot_json_string297–320 ↗
fn format_snapshot_json_string(text: &str, options: &ContextSnapshotOptions) -> String
作用:专门处理 JSON 字符串里的文本,让它适合放进请求体快照。它会按模式保留全文、遮盖固定大块内容,或截断长文本。
数据流:进去一段字符串和选项 → 它根据渲染模式决定是否做规范化:统一换行、替换 AGENTS.md/环境信息等大块内容、替换 UUID/时间戳/沙箱等动态值;如果是前缀模式且太长,就只留指定长度再加省略号 → 出来处理后的字符串。
调用关系:canonicalize_json_snapshot_value 会在遇到 JSON 字符串时调用它;测试 redacted_text_mode_normalizes_turn_metadata_dynamic_json_strings 也直接检查它能否替换动态元数据。
调用图:调用 3 个内部函数(canonicalize_snapshot_text, normalize_snapshot_dynamic_values, normalize_snapshot_line_endings);被 2 处调用(canonicalize_json_snapshot_value, redacted_text_mode_normalizes_turn_metadata_dynamic_json_strings);外部调用 2 个(format!, unreachable!)。
format_changed_lines_diff322–343 ↗
fn format_changed_lines_diff(
before_title: &str,
before: &str,
after_title: &str,
after: &str,
) -> String
作用:把两段文字做成简化版差异结果,只显示删除和新增的行。相同的行会被跳过,减少噪音。
数据流:进去前后两个标题和两段文本 → 它先写出 --- 和 +++ 头,再用 TextDiff 按行比较;删除行前加 -,新增行前加 +,相同行不输出 → 出来一段紧凑的 diff 文本。
调用关系:format_request_body_diff_snapshot 用它展示两份请求体快照的变化;它依赖 similar 库来判断每行是相同、删除还是新增。
调用图:被 1 处调用(format_request_body_diff_snapshot);外部调用 2 个(from_lines, format!)。
format_snapshot_text345–365 ↗
fn format_snapshot_text(text: &str, options: &ContextSnapshotOptions) -> String
作用:把普通消息文本整理成一行快照里的可读片段。它会按模式决定是否遮盖、保留全文或截断。
数据流:进去一段文本和选项 → 它统一换行符,并在需要时调用 canonicalize_snapshot_text 替换大块固定内容;最后把真实换行改成字符形式的 \n,避免一条消息把快照排版打乱;前缀模式会在超长时加省略号 → 出来一段适合嵌在单行里的文字。
调用关系:format_response_items_snapshot 在处理 message、function_call_output、local_shell_call、reasoning 摘要等文本时会用它。
调用图:调用 2 个内部函数(canonicalize_snapshot_text, normalize_snapshot_line_endings);外部调用 2 个(format!, unreachable!)。
normalize_snapshot_line_endings367–369 ↗
fn normalize_snapshot_line_endings(text: &str) -> String
作用:统一换行符。不同系统可能用不同换行写法,这个函数把它们都变成 \n,避免测试因为操作系统不同而失败。
数据流:进去一段文本 → 它把 Windows 风格的 \r\n 和单独的 \r 都替换成 \n → 出来换行风格统一的新字符串。
调用关系:format_snapshot_text 和 format_snapshot_json_string 都会调用它;多条测试专门确认 FullText 和前缀模式也会统一换行。
调用图:被 2 处调用(format_snapshot_json_string, format_snapshot_text)。
canonicalize_snapshot_text371–426 ↗
fn canonicalize_snapshot_text(text: &str) -> String
作用:把快照文本里又长、敏感、或每次会变的固定大块内容换成短占位符。这样测试关注“有没有这类内容”,而不是被具体正文拖住。
数据流:进去一段文本 → 它检查开头是不是权限说明、apps/skills/plugins 说明、AGENTS.md、环境上下文、压缩总结提示等特殊内容;匹配到就返回如 <AGENTS_MD>、<ENVIRONMENT_CONTEXT:cwd=<CWD>> 这样的占位符;否则再规范化系统技能临时路径 → 出来更稳定、更短的文本。
调用关系:format_snapshot_text 和 format_snapshot_json_string 都依赖它做主要遮盖;它会把路径类清理交给 normalize_dynamic_snapshot_paths。
调用图:调用 1 个内部函数(normalize_dynamic_snapshot_paths);被 2 处调用(format_snapshot_json_string, format_snapshot_text);外部调用 2 个(new, format!)。
is_capability_instruction_text428–432 ↗
fn is_capability_instruction_text(text: &str) -> bool
作用:判断一段文本是不是能力说明,也就是 apps、skills 或 plugins 说明。这个判断用于决定是否在快照里跳过这些片段。
数据流:进去一段文本 → 它检查文本是否以对应的三个开放标签开头 → 出来 true 或 false。
调用关系:format_response_items_snapshot 在 strip_capability_instructions 开关打开、且正在处理 developer 消息时会用它筛掉能力说明。
normalize_dynamic_snapshot_paths434–443 ↗
fn normalize_dynamic_snapshot_paths(text: &str) -> String
作用:把系统技能文件的临时目录路径改成固定根路径。临时目录每次运行都可能不一样,不处理就会让快照测试反复失败。
数据流:进去一段文本 → 它用正则表达式(一种按模式查找文字的工具)找出类似 /.../skills/.system/某技能/SKILL.md 的路径 → 把前面不稳定的部分替换成 <SYSTEM_SKILLS_ROOT> → 出来路径稳定后的文本。
调用关系:canonicalize_snapshot_text 在没有匹配到其它大块占位规则时会调用它;对应测试确认系统技能临时路径会被替换。
调用图:被 1 处调用(canonicalize_snapshot_text);外部调用 1 个(new)。
normalize_snapshot_dynamic_values445–467 ↗
fn normalize_snapshot_dynamic_values(text: &str) -> String
作用:替换 JSON 字符串里每次运行都会变的值,比如 UUID、开始时间戳和沙箱名。这样快照比较只看结构和关键内容。
数据流:进去一段文本 → 它用三个正则分别找 UUID、turn_started_at_unix_ms 数字字段、sandbox 字符串字段 → 替换成 <UUID>、<UNIX_MS>、<SANDBOX> → 出来动态值被固定占位符替代的文本。
调用关系:format_snapshot_json_string 在遮盖模式和前缀模式下会调用它;相关测试直接验证这些元数据会被规范化。
调用图:被 1 处调用(format_snapshot_json_string);外部调用 1 个(new)。
tests::full_text_mode_preserves_unredacted_text479–498 ↗
fn full_text_mode_preserves_unredacted_text()
作用:测试 FullText 模式确实保留原文,不把 AGENTS.md 内容替换成占位符。
数据流:进去测试里构造的一条 user 消息 → 它用默认选项切到 FullText,再调用 format_response_items_snapshot → 比较输出是否仍包含完整 AGENTS.md 文本。
调用关系:它验证 format_response_items_snapshot 和 format_snapshot_text 在 FullText 模式下不会走遮盖逻辑。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::full_text_mode_normalizes_crlf_line_endings501–517 ↗
fn full_text_mode_normalizes_crlf_line_endings()
作用:测试 FullText 模式虽然保留文字内容,但仍会统一换行符。
数据流:进去一条带 \r\n 的 user 消息 → 它用 FullText 模式格式化 → 断言输出里的换行已经变成 \n 的快照写法。
调用关系:它覆盖 format_response_items_snapshot 到 format_snapshot_text,再到 normalize_snapshot_line_endings 的路径。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::redacted_text_mode_keeps_canonical_placeholders520–536 ↗
fn redacted_text_mode_keeps_canonical_placeholders()
作用:测试默认遮盖模式会把 AGENTS.md 大块内容换成短占位符。
数据流:进去一条包含 AGENTS.md 指令的 user 消息 → 它用默认选项格式化 → 断言输出是 <AGENTS_MD>,而不是原始长文本。
调用关系:它验证 format_response_items_snapshot 会通过 format_snapshot_text 使用 canonicalize_snapshot_text。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::redacted_text_mode_keeps_capability_instruction_placeholders539–568 ↗
fn redacted_text_mode_keeps_capability_instruction_placeholders()
作用:测试 apps、skills、plugins 三类能力说明在遮盖模式下会显示为各自占位符,而不是丢失或展开全文。
数据流:进去一条 developer 消息,里面有三段能力说明 → 它用默认遮盖模式格式化 → 断言输出包含三个编号片段:<APPS_INSTRUCTIONS>、<SKILLS_INSTRUCTIONS>、<PLUGINS_INSTRUCTIONS>。
调用关系:它检查 format_response_items_snapshot 处理多段 message content 的排版,也检查 canonicalize_snapshot_text 对能力说明的识别。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::strip_capability_instructions_omits_capability_parts_from_developer_messages571–590 ↗
fn strip_capability_instructions_omits_capability_parts_from_developer_messages()
作用:测试打开删除能力说明开关后,developer 消息里的 skills 和 plugins 说明会被省略。
数据流:进去一条 developer 消息,包含权限说明、skills 说明和 plugins 说明 → 它用默认选项再打开 strip_capability_instructions → 格式化后断言只剩权限说明占位符。
调用关系:它验证 ContextSnapshotOptions::strip_capability_instructions 会影响 format_response_items_snapshot,并通过 is_capability_instruction_text 过滤能力说明。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::strip_agents_md_user_context_omits_agents_fragment_from_user_messages593–617 ↗
fn strip_agents_md_user_context_omits_agents_fragment_from_user_messages()
作用:测试打开删除 AGENTS.md 用户上下文开关后,用户消息里的 AGENTS.md 片段会被跳过。
数据流:进去一条 user 消息,先是 AGENTS.md 指令,再是环境上下文 → 它打开 strip_agents_md_user_context 后格式化 → 断言输出只保留环境上下文占位符。
调用关系:它验证 ContextSnapshotOptions::strip_agents_md_user_context 会改变 format_response_items_snapshot 对 user 消息内容数组的筛选。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::redacted_text_mode_normalizes_environment_context_with_subagents620–639 ↗
fn redacted_text_mode_normalizes_environment_context_with_subagents()
作用:测试环境上下文会被压缩成稳定占位符,并且能保留子代理数量这个有用信息。
数据流:进去一条包含 cwd、shell 和两个 subagents 的环境上下文文本 → 它用默认遮盖模式格式化 → 断言输出是 <ENVIRONMENT_CONTEXT:cwd=<CWD>:subagents=2>。
调用关系:它主要验证 canonicalize_snapshot_text 对 <environment_context> 的特殊解析,并通过 format_response_items_snapshot 走完整快照流程。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::kind_with_text_prefix_mode_normalizes_crlf_line_endings642–662 ↗
fn kind_with_text_prefix_mode_normalizes_crlf_line_endings()
作用:测试“只显示文本前缀”模式会统一换行,并在超过长度时截断。
数据流:进去一条带 \r\n 的 developer 消息 → 它设置 KindWithTextPrefix 且最大 64 个字符 → 格式化后断言输出换行统一,并以省略号结尾。
调用关系:它覆盖 ContextSnapshotOptions::render_mode、format_response_items_snapshot、format_snapshot_text 和 normalize_snapshot_line_endings 的配合。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::image_only_message_is_rendered_as_non_text_span665–678 ↗
fn image_only_message_is_rendered_as_non_text_span()
作用:测试只有图片的消息不会被当成缺少文字,而是显示成带额外字段名的图片占位。
数据流:进去一条 user 消息,content 里只有 input_image 和 image_url → 它用默认选项格式化 → 断言输出为 <input_image:image_url>。
调用关系:它验证 format_response_items_snapshot 对非文本 content 项的兜底展示,确保图片这类内容在快照中仍可见。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::mixed_text_and_image_message_keeps_image_span681–707 ↗
fn mixed_text_and_image_message_keeps_image_span()
作用:测试文字和图片混在一起时,图片占位不会被吞掉,多个内容片段也会按编号展开。
数据流:进去一条 user 消息,内容是 <image> 文本、图片、</image> 文本 → 它格式化后断言输出三段编号内容,中间保留 <input_image:image_url>。
调用关系:它检查 format_response_items_snapshot 处理多 content 项时的分段排版,以及文本和非文本项混合时的行为。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::redacted_text_mode_normalizes_system_skill_temp_paths710–726 ↗
fn redacted_text_mode_normalizes_system_skill_temp_paths()
作用:测试系统技能文件里的临时路径会被替换成稳定占位路径。
数据流:进去一条 developer 消息,文本里包含临时目录下的 skills/.system/openai-docs/SKILL.md → 它用默认遮盖模式格式化 → 断言输出把临时根目录换成 <SYSTEM_SKILLS_ROOT>。
调用关系:它验证 canonicalize_snapshot_text 会调用 normalize_dynamic_snapshot_paths,避免快照受机器临时目录影响。
调用图:调用 2 个内部函数(default, format_response_items_snapshot);外部调用 2 个(assert_eq!, vec!)。
tests::redacted_text_mode_normalizes_turn_metadata_dynamic_json_strings729–739 ↗
fn redacted_text_mode_normalizes_turn_metadata_dynamic_json_strings()
作用:测试 JSON 字符串里的 turn_id、sandbox 和 turn_started_at_unix_ms 会被替换成固定占位符。
数据流:进去一段包含 UUID、沙箱名和毫秒时间戳的 JSON 字符串 → 它调用 format_snapshot_json_string → 断言输出里的三类动态值分别变成 <UUID>、<SANDBOX> 和 <UNIX_MS>。
调用关系:它直接覆盖 format_snapshot_json_string 到 normalize_snapshot_dynamic_values 的清理逻辑,保证请求体快照不会因为运行时元数据变化而失败。
调用图:调用 2 个内部函数(default, format_snapshot_json_string);外部调用 1 个(assert_eq!)。
core/tests/common/responses.rs源码 ↗
项目里很多代码要跟远端模型服务说话。测试时不能真的每次都连外网,所以这个文件搭了几种假服务:普通 /responses 请求、压缩用的 /responses/compact、模型列表 /models,还有 WebSocket 实时连接。它像一个训练用的假柜台:测试先告诉它“等会儿有人来时你要回这些内容”,它收到请求后会把请求存下来,再按剧本返回 SSE 事件流或 JSON。测试随后可以检查请求正文、请求头、消息文本、工具调用输出等。一个重要细节是,它会主动检查工具调用和工具输出是否一一配对;如果客户端发了孤儿输出或漏了输出,测试会立刻失败。这能把很多协议层面的错误提前抓出来。
ResponseMock::new44–48 ↗
fn new() -> Self
作用:创建一个用来记录 /responses 请求的空记录本。测试挂载假接口时会用它来保存之后收到的请求。
数据流:进去没有业务输入 → 建一个线程安全的空列表,里面以后会放 ResponsesRequest → 出来一个 ResponseMock,可被假服务器和测试代码共享。
调用关系:它由 base_mock 和 compact_mock 调用,是 HTTP Responses 类测试记录请求的起点。
调用图:被 2 处调用(base_mock, compact_mock);外部调用 3 个(new, new, new)。
ResponseMock::single_request50–56 ↗
fn single_request(&self) -> ResponsesRequest
作用:取出唯一一次请求,并强制确认真的只收到一次。适合测试“这一步应该只打一次接口”的场景。
数据流:进去是已有的请求记录 → 加锁读取列表,数量不是 1 就直接让测试失败 → 出来那一条克隆后的请求。
调用关系:测试辅助代码如 command_result 会用它做断言;它不再把工作交给别的本文件函数。
调用图:被 1 处调用(command_result);外部调用 1 个(panic!)。
ResponseMock::requests58–60 ↗
fn requests(&self) -> Vec<ResponsesRequest>
作用:拿到目前捕获到的所有 Responses 请求。测试可用它数请求、筛请求、等请求出现。
数据流:进去是共享记录本 → 加锁复制整个请求列表 → 出来一个普通列表,调用者可以安全查看。
调用关系:很多等待和检查函数会调用它;saw_function_call 和 function_call_output_text 也靠它先拿到全部请求。
调用图:被 7 处调用(wait_for_request_count, function_call_output_text, saw_function_call, capture_from_requests, wait_for_matching_request, wait_for_requests, wait_for_request)。
ResponseMock::last_request62–64 ↗
fn last_request(&self) -> Option<ResponsesRequest>
作用:取最近一次请求。适合测试只关心最后客户端发了什么的情况。
数据流:进去是共享请求列表 → 加锁看最后一项 → 出来最后请求的副本;如果还没请求就返回空。
调用关系:它是一个直接查看记录的小工具,不依赖其他本文件函数。
ResponseMock::saw_function_call68–72 ↗
fn saw_function_call(&self, call_id: &str) -> bool
作用:检查所有捕获请求里,是否出现过指定编号的函数调用。这里的函数调用是模型要求客户端执行某个工具的记录。
数据流:进去一个 call_id → 先取出所有请求,再逐条检查请求输入里有没有这个函数调用编号 → 出来 true 或 false。
调用关系:它调用 ResponseMock::requests,再把具体判断交给每个 ResponsesRequest::has_function_call。
调用图:调用 1 个内部函数(requests)。
ResponseMock::function_call_output_text76–80 ↗
fn function_call_output_text(&self, call_id: &str) -> Option<String>
作用:从所有请求里找某个函数调用的输出文字。测试可用它确认工具执行结果有没有被送回模型。
数据流:进去一个 call_id → 遍历已记录请求,找匹配的 function_call_output → 出来输出字符串;找不到就返回空。
调用关系:它先调用 ResponseMock::requests,再依赖 ResponsesRequest::function_call_output_text 解析单个请求。
调用图:调用 1 个内部函数(requests)。
is_zstd_encoding86–90 ↗
fn is_zstd_encoding(value: &str) -> bool
作用:判断请求头里的压缩方式是否包含 zstd。zstd 是一种压缩格式,正文被它压过就不能直接当 JSON 读。
数据流:进去一个编码头字符串 → 按逗号拆开并忽略大小写比较 → 出来是否含有 zstd。
调用关系:它被 decode_body_bytes 使用,是读取压缩请求正文前的小判断。
decode_body_bytes92–99 ↗
fn decode_body_bytes(body: &[u8], content_encoding: Option<&str>) -> Vec<u8>
作用:把请求正文还原成可读字节。如果正文用了 zstd 压缩,它会先解压;否则原样返回。
数据流:进去原始字节和可选的 content-encoding 头 → 判断是否 zstd,必要时解压 → 出来未压缩的正文 bytes。
调用关系:它被 ResponsesRequest::body_json 和 validate_request_body_invariants 调用,保证后续解析 JSON 时拿到的是正常内容。
调用图:调用 1 个内部函数(new);被 2 处调用(body_json, validate_request_body_invariants);外部调用 1 个(decode_all)。
ResponsesRequest::body_json102–111 ↗
fn body_json(&self) -> Value
作用:把捕获到的 HTTP 请求正文读成 JSON。JSON 可以理解成结构化的键值数据,测试主要靠它看客户端发了什么。
数据流:进去是一个保存的 wiremock 请求 → 读取正文,按请求头需要时解压,再解析为 JSON → 出来 Value 形式的 JSON。
调用关系:许多检查函数都以它为基础,例如读 input、instructions、工具定义和正文文本。
调用图:调用 1 个内部函数(decode_body_bytes);被 9 处调用(format_request_body_snapshot, body_contains_text, input, instructions_text, tool_by_name, assert_request_contains_custom_realtime_start, assert_request_contains_realtime_end, assert_request_contains_realtime_start, tool_names);外部调用 1 个(from_slice)。
ResponsesRequest::body_bytes113–115 ↗
fn body_bytes(&self) -> Vec<u8>
作用:拿到请求正文的原始字节,不做解压也不解析。适合测试底层传输内容。
数据流:进去是保存的请求 → 复制里面的 body 字节 → 出来一份原始 bytes。
调用关系:它是低层查看工具,不调用其他本文件函数。
ResponsesRequest::body_contains_text117–123 ↗
ResponsesRequest::tool_by_name125–127 ↗
fn tool_by_name(&self, namespace: &str, tool_name: &str) -> Option<Value>
作用:在请求里的工具列表中,查找某个命名空间下面的子工具。命名空间可以理解成一个工具文件夹。
数据流:进去命名空间名和工具名 → 解析请求 JSON,再在 tools 里找对应子项 → 出来工具 JSON;找不到返回空。
调用关系:它调用 ResponsesRequest::body_json 和 namespace_child_tool,把具体查找逻辑交给后者。
调用图:调用 2 个内部函数(body_json, namespace_child_tool)。
ResponsesRequest::instructions_text129–134 ↗
fn instructions_text(&self) -> String
作用:取出请求里的总指令文本。测试可用它确认系统提示词或开发者指令是否正确发送。
数据流:进去是请求 → 解析 JSON,读取 instructions 字段并当成字符串 → 出来指令文本。
调用关系:它被 token 估算等测试辅助调用,依赖 ResponsesRequest::body_json。
调用图:调用 1 个内部函数(body_json);被 1 处调用(estimate_compact_payload_tokens)。
ResponsesRequest::message_input_texts137–146 ↗
fn message_input_texts(&self, role: &str) -> Vec<String>
作用:取出指定角色消息里的所有输入文字片段。角色通常是 user、developer 等。
数据流:进去角色名 → 先筛出 message 类型输入,再只保留该角色,展开 content 中的 input_text → 出来文字列表。
调用关系:它调用 ResponsesRequest::inputs_of_type,被许多测试用来检查用户指令、权限文本、预算提示等是否出现。
调用图:调用 1 个内部函数(inputs_of_type);被 9 处调用(instruction_fragments, message_input_text_contains, instruction_fragments, request_hook_prompt_texts, user_instructions_wrapper_count, permissions_texts, has_subagent_notification, token_budget_texts, phase2_prompt_text)。
ResponsesRequest::message_input_text_groups149–162 ↗
fn message_input_text_groups(&self, role: &str) -> Vec<Vec<String>>
作用:按每条消息分组取出输入文字。它不把所有文字混在一起,方便测试“一条消息里包含哪些片段”。
数据流:进去角色名 → 找到该角色的 message 输入,对每条消息分别收集 input_text → 出来二维文字列表。
调用关系:它调用 ResponsesRequest::inputs_of_type,主要服务于 has_message_with_input_texts。
调用图:调用 1 个内部函数(inputs_of_type);被 1 处调用(has_message_with_input_texts)。
ResponsesRequest::has_message_with_input_texts164–172 ↗
fn has_message_with_input_texts(
&self,
role: &str,
predicate: impl Fn(&[String]) -> bool,
) -> bool
作用:检查是否存在一条指定角色的消息,能满足调用者给出的条件。这个条件由测试自己定义。
数据流:进去角色名和判断函数 → 先按消息分组取文字,再逐组交给判断函数 → 出来是否有任一组通过。
调用关系:它调用 ResponsesRequest::message_input_text_groups,是更灵活的消息内容断言工具。
调用图:调用 1 个内部函数(message_input_text_groups)。
ResponsesRequest::message_input_image_urls175–188 ↗
fn message_input_image_urls(&self, role: &str) -> Vec<String>
作用:取出指定角色消息中的图片地址。用于确认客户端是否把图片输入正确发给模型。
数据流:进去角色名 → 筛 message、筛角色、展开 content,找 input_image 里的 image_url → 出来图片地址列表。
调用关系:它调用 ResponsesRequest::inputs_of_type,和文字提取函数是一类测试检查工具。
调用图:调用 1 个内部函数(inputs_of_type)。
ResponsesRequest::input190–195 ↗
fn input(&self) -> Vec<Value>
作用:取出请求 JSON 里的 input 数组。这个数组是 Responses API 中最核心的对话和工具历史。
数据流:进去请求 → 解析正文 JSON,读取 input 并要求它必须是数组 → 出来数组副本;没有就让测试失败。
调用关系:很多函数靠它继续筛消息、找工具调用和输出,是单个请求解析的中心入口。
调用图:调用 1 个内部函数(body_json);被 6 处调用(format_request_input_snapshot, call_output, function_call_output_text, has_function_call, inputs_of_type, estimate_compact_input_tokens)。
ResponsesRequest::inputs_of_type197–203 ↗
fn inputs_of_type(&self, ty: &str) -> Vec<Value>
作用:从 input 数组里筛出某一种类型的项目,比如 message 或 function_call。
数据流:进去类型名 → 读取完整 input,逐项比较 type 字段 → 出来匹配项目列表。
调用关系:它调用 ResponsesRequest::input,被消息文字、图片提取等函数复用。
调用图:调用 1 个内部函数(input);被 3 处调用(message_input_image_urls, message_input_text_groups, message_input_texts)。
ResponsesRequest::function_call_output205–207 ↗
fn function_call_output(&self, call_id: &str) -> Value
作用:取出某个普通函数调用的输出项目。普通函数调用是模型让客户端执行一个命名工具。
数据流:进去调用编号 → 在 input 里找 function_call_output 且编号一致的项目 → 出来该 JSON;找不到就失败。
调用关系:它只是把类型名固定后交给 ResponsesRequest::call_output。
调用图:调用 1 个内部函数(call_output);被 4 处调用(function_tool_output_items, call_output, call_output_content_and_success, call_output)。
ResponsesRequest::custom_tool_call_output209–211 ↗
fn custom_tool_call_output(&self, call_id: &str) -> Value
作用:取出某个自定义工具调用的输出项目。自定义工具通常是特殊格式的工具输入输出。
数据流:进去调用编号 → 在 input 里找 custom_tool_call_output 且编号一致的项目 → 出来该 JSON;找不到就失败。
调用关系:它调用通用的 ResponsesRequest::call_output,供自定义工具相关测试使用。
调用图:调用 1 个内部函数(call_output);被 3 处调用(custom_tool_output_items, custom_tool_output_last_non_empty_text, custom_call_output)。
ResponsesRequest::tool_search_output213–215 ↗
fn tool_search_output(&self, call_id: &str) -> Value
作用:取出某个工具搜索调用的输出项目。工具搜索是让客户端帮模型查找可用工具的一类调用。
数据流:进去调用编号 → 在 input 里找 tool_search_output 且编号一致的项目 → 出来该 JSON。
调用关系:它固定输出类型后调用 ResponsesRequest::call_output。
调用图:调用 1 个内部函数(call_output);被 1 处调用(tool_search_output_item)。
ResponsesRequest::call_output217–225 ↗
fn call_output(&self, call_id: &str, call_type: &str) -> Value
作用:按调用编号和输出类型,从请求 input 中找到对应工具输出。它是几类输出查找函数的共同底座。
数据流:进去 call_id 和输出类型 → 遍历 input,比较 type 和 call_id → 出来匹配 JSON;没有就让测试失败。
调用关系:它调用 ResponsesRequest::input,被普通函数、自定义工具、工具搜索输出函数调用。
调用图:调用 1 个内部函数(input);被 4 处调用(call_output_content_and_success, custom_tool_call_output, function_call_output, tool_search_output)。
ResponsesRequest::has_function_call229–234 ↗
fn has_function_call(&self, call_id: &str) -> bool
作用:判断这个请求里是否包含某个普通函数调用。用于确认模型调用历史是否被带回下一轮请求。
数据流:进去调用编号 → 遍历 input 中 function_call 项并比较 call_id → 出来是否存在。
调用关系:它调用 ResponsesRequest::input,常由 ResponseMock::saw_function_call 间接使用。
调用图:调用 1 个内部函数(input)。
ResponsesRequest::function_call_output_text238–247 ↗
fn function_call_output_text(&self, call_id: &str) -> Option<String>
作用:取出某个普通函数调用输出里的纯文本 output。找不到或不是字符串就返回空。
数据流:进去调用编号 → 找到对应 function_call_output 项,再读它的 output 字段 → 出来字符串或空。
调用关系:它调用 ResponsesRequest::input,被 ResponseMock::function_call_output_text 用来跨请求搜索。
调用图:调用 1 个内部函数(input)。
ResponsesRequest::function_call_output_content_and_success249–254 ↗
fn function_call_output_content_and_success(
&self,
call_id: &str,
) -> Option<(Option<String>, Option<bool>)>
作用:读取普通函数调用输出的内容和成功标记。它兼容旧式字符串输出和带 content、success 的对象输出。
数据流:进去调用编号 → 指定类型为 function_call_output 后解析输出 → 出来 (内容, 是否成功),两者都可能为空。
调用关系:它把具体解析交给 ResponsesRequest::call_output_content_and_success。
调用图:调用 1 个内部函数(call_output_content_and_success);被 3 处调用(call_output, call_output_content_and_success, call_output)。
ResponsesRequest::custom_tool_call_output_content_and_success256–261 ↗
fn custom_tool_call_output_content_and_success(
&self,
call_id: &str,
) -> Option<(Option<String>, Option<bool>)>
作用:读取自定义工具调用输出的内容和成功标记。测试可用它确认工具结果和状态是否正确回传。
数据流:进去调用编号 → 指定类型为 custom_tool_call_output 后解析输出 → 出来内容和成功标记。
调用关系:它调用通用的 ResponsesRequest::call_output_content_and_success。
调用图:调用 1 个内部函数(call_output_content_and_success);被 2 处调用(custom_tool_output_body_and_success, custom_call_output)。
ResponsesRequest::call_output_content_and_success263–283 ↗
fn call_output_content_and_success(
&self,
call_id: &str,
call_type: &str,
) -> Option<(Option<String>, Option<bool>)>
作用:统一解析工具输出里的“文字内容”和“成功与否”。它处理字符串、单个文字片段数组、以及对象三种形状。
数据流:进去调用编号和输出类型 → 找到对应输出,查看 output 是字符串、数组还是对象 → 出来可选内容和可选成功布尔值。
调用关系:它调用 ResponsesRequest::call_output 找项目,并调用 output_value_to_text 把简单输出转成文字。
调用图:调用 2 个内部函数(call_output, output_value_to_text);被 2 处调用(custom_tool_call_output_content_and_success, function_call_output_content_and_success)。
ResponsesRequest::header285–291 ↗
fn header(&self, name: &str) -> Option<String>
作用:读取捕获请求中的某个 HTTP 头。HTTP 头可以理解成请求附带的额外小纸条。
数据流:进去头名 → 在请求头表里查找并转成字符串 → 出来头值;不存在或不是合法字符串就返回空。
调用关系:它是测试检查请求元数据的小工具,例如检查窗口 ID 等。
调用图:被 1 处调用(window_id_parts)。
ResponsesRequest::path293–295 ↗
fn path(&self) -> String
作用:返回请求 URL 的路径部分,比如 /v1/responses。用于确认请求打到了哪个接口。
数据流:进去请求 → 从 URL 中取 path → 出来路径字符串。
调用关系:它独立工作,不调用其他本文件函数。
ResponsesRequest::query_param297–303 ↗
fn query_param(&self, name: &str) -> Option<String>
作用:读取 URL 查询参数里的某个值。查询参数就是网址问号后面的 name=value。
数据流:进去参数名 → 遍历 URL 的查询参数 → 出来第一个匹配值;没有则为空。
调用关系:它是检查请求 URL 细节的辅助函数。
output_value_to_text306–317 ↗
fn output_value_to_text(value: &Value) -> Option<String>
作用:把工具输出的 JSON 值尽量转成一段文字。只有字符串或“单个 input_text 片段数组”会被接受。
数据流:进去一个 JSON 值 → 如果是字符串就直接取;如果是只含一个 input_text 的数组就取 text;其他形状返回空 → 出来可选文字。
调用关系:它被 ResponsesRequest::call_output_content_and_success 以及其他自定义工具文本检查使用。
调用图:被 2 处调用(call_output_content_and_success, custom_tool_call_output_text)。
namespace_child_tool319–342 ↗
fn namespace_child_tool(
body: &'a Value,
namespace: &str,
tool_name: &str,
) -> Option<&'a Value>
作用:在请求的工具清单里找某个命名空间下的子工具。可以把它想成在工具文件夹里找指定文件。
数据流:进去整个请求 JSON、命名空间名、子工具名 → 遍历 tools,先找类型为 namespace 的父工具,再找里面同名子工具 → 出来子工具 JSON 引用或空。
调用关系:它被 ResponsesRequest::tool_by_name 和多处工具搜索相关测试调用。
调用图:被 6 处调用(tool_by_name, tool_search_indexes_only_enabled_non_app_mcp_tools, tool_search_output_has_namespace_child, tool_search_returns_deferred_v1_multi_agent_tools, spawn_agent_description, spawn_agent_tool_description_mentions_role_locked_settings);外部调用 1 个(get)。
tests::request_with_input351–361 ↗
fn request_with_input(input: Value) -> ResponsesRequest
作用:测试本文件内部解析逻辑时,快速造一个带 input 的假 ResponsesRequest。
数据流:进去一段 input JSON → 包成 /v1/responses POST 请求正文 → 出来 ResponsesRequest。
调用关系:它只在本文件单元测试里被 tests::call_output_content_and_success_returns_only_single_text_content_item 使用。
tests::call_output_content_and_success_returns_only_single_text_content_item364–409 ↗
fn call_output_content_and_success_returns_only_single_text_content_item()
作用:验证工具输出解析只接受单个文字内容,混有图片或多个内容时不会误当成文本。
数据流:进去无外部输入 → 构造几种函数输出请求,调用解析函数并比较结果 → 测试通过或失败。
调用关系:它调用 tests::request_with_input,覆盖 function_call_output_content_and_success 和 custom_tool_call_output_content_and_success 的关键行为。
调用图:外部调用 3 个(assert_eq!, request_with_input, json!)。
WebSocketRequest::body_json418–420 ↗
fn body_json(&self) -> Value
作用:返回 WebSocket 测试服务器收到的一条消息正文。WebSocket 是一种长连接通信方式。
数据流:进去已记录的 WebSocket 请求 → 克隆保存的 JSON body → 出来 JSON。
调用关系:它被 WebSocket 请求检查工具调用,用来读实时请求里的指令或文本。
调用图:被 2 处调用(websocket_request_instructions, websocket_request_text);外部调用 1 个(clone)。
WebSocketHandshake::uri430–432 ↗
fn uri(&self) -> &str
作用:查看 WebSocket 握手时客户端请求的 URI。握手是建立 WebSocket 连接前的开场请求。
数据流:进去握手记录 → 返回保存的 URI 字符串引用 → 不改动任何状态。
调用关系:测试可通过它确认客户端连的是正确路径。
WebSocketHandshake::header434–439 ↗
fn header(&self, name: &str) -> Option<String>
作用:读取 WebSocket 握手里的某个头,忽略大小写。用于检查鉴权、实验开关等头是否带上。
数据流:进去头名 → 在握手头列表中大小写不敏感地查找 → 出来对应值或空。
调用关系:它服务于 WebSocket 握手断言,不调用其他本文件函数。
WebSocketTestServer::uri468–470 ↗
fn uri(&self) -> &str
作用:拿到测试 WebSocket 服务器的连接地址。客户端测试需要用这个地址替代真实服务。
数据流:进去服务器对象 → 返回内部保存的 ws://... 地址引用 → 状态不变。
调用关系:构建远端实时测试客户端时会调用它。
调用图:被 1 处调用(remote_realtime_test_codex_builder)。
WebSocketTestServer::connections472–474 ↗
fn connections(&self) -> Vec<Vec<WebSocketRequest>>
作用:拿到所有 WebSocket 连接中收到的请求记录。外层列表是一条条连接,内层列表是该连接里的消息。
数据流:进去服务器对象 → 加锁复制连接请求日志 → 出来二维请求列表。
调用关系:等待匹配 WebSocket 请求的测试辅助会用它查看当前进度。
调用图:被 1 处调用(wait_for_matching_websocket_request)。
WebSocketTestServer::single_connection476–482 ↗
fn single_connection(&self) -> Vec<WebSocketRequest>
作用:取出唯一一条 WebSocket 连接的请求记录,并确认确实只有一条连接。
数据流:进去服务器对象 → 加锁检查连接数,不是 1 就让测试失败 → 出来这条连接的请求列表。
调用关系:适合简单实时测试,直接检查单连接中的所有消息。
调用图:外部调用 1 个(panic!)。
WebSocketTestServer::wait_for_request484–502 ↗
async fn wait_for_request(
&self,
connection_index: usize,
request_index: usize,
) -> WebSocketRequest
作用:等待某条 WebSocket 连接上的某个请求出现。这样测试不用靠固定睡眠猜时机。
数据流:进去连接序号和请求序号 → 循环查看日志,没看到就等待通知 → 出来目标请求副本。
调用关系:它依赖 request_log_updated 通知;WebSocket 服务主循环收到消息后会唤醒等待者。
调用图:被 2 处调用(sideband_outbound_request, wait_for_websocket_request)。
WebSocketTestServer::handshakes504–506 ↗
fn handshakes(&self) -> Vec<WebSocketHandshake>
作用:拿到所有 WebSocket 握手记录。测试可检查连接时的 URI 和头。
数据流:进去服务器对象 → 加锁复制握手日志 → 出来握手列表。
调用关系:它是握手检查的直接读取接口。
WebSocketTestServer::wait_for_handshakes512–530 ↗
async fn wait_for_handshakes(&self, expected: usize, timeout: Duration) -> bool
作用:在限定时间内等待至少若干次 WebSocket 握手出现。用于测试后台连接行为。
数据流:进去期望数量和超时时间 → 每 10 毫秒左右检查一次握手数量,直到够数或超时 → 出来 true 或 false。
调用关系:它不靠事件通知,而用短间隔轮询,避免测试空转又能稳定等待。
调用图:外部调用 4 个(from_millis, min, now, sleep)。
WebSocketTestServer::single_handshake531–537 ↗
fn single_handshake(&self) -> WebSocketHandshake
作用:取出唯一一次 WebSocket 握手,并确认握手次数正好是 1。
数据流:进去服务器对象 → 加锁检查握手数量,不是 1 就失败 → 出来握手副本。
调用关系:用于简单握手断言场景。
调用图:外部调用 1 个(panic!)。
WebSocketTestServer::shutdown539–549 ↗
ModelsMock::new558–562 ↗
fn new() -> Self
作用:创建一个记录 /models 请求的 mock。/models 是客户端获取模型列表的接口。
数据流:进去无业务输入 → 建一个线程安全空请求列表 → 出来 ModelsMock。
调用关系:它由 models_mock 调用,是模型列表接口测试记录的起点。
调用图:被 1 处调用(models_mock);外部调用 3 个(new, new, new)。
ModelsMock::requests564–566 ↗
fn requests(&self) -> Vec<wiremock::Request>
作用:返回所有捕获到的模型列表请求。
数据流:进去 mock 对象 → 加锁复制请求列表 → 出来请求副本列表。
调用关系:测试可直接用它检查模型刷新是否发生。
ModelsMock::single_request_path568–574 ↗
fn single_request_path(&self) -> String
作用:确认只收到一次模型列表请求,并返回这次请求的路径。
数据流:进去 mock 对象 → 检查记录数量必须为 1 → 出来该请求 URL 的 path。
调用关系:用于断言客户端确实只访问了一次 /models。
调用图:外部调用 1 个(panic!)。
ModelsMock::matches578–581 ↗
fn matches(&self, request: &wiremock::Request) -> bool
作用:作为 wiremock 的匹配器,捕获每个 /models 请求并永远表示匹配成功。
数据流:进去 wiremock 请求 → 把请求克隆进记录列表 → 出来 true,让 mock 继续响应。
调用关系:它由 wiremock 在收到请求时自动调用。
调用图:外部调用 1 个(clone)。
ResponseMock::matches585–595 ↗
fn matches(&self, request: &wiremock::Request) -> bool
作用:作为 wiremock 的匹配器,记录每个 /responses 请求,并顺手检查请求体协议是否合理。
数据流:进去 wiremock 请求 → 包成 ResponsesRequest 存入列表,再运行不变量检查 → 出来 true。
调用关系:wiremock 收到 Responses 请求时调用它;它会调用 validate_request_body_invariants 抓协议错误。
调用图:调用 1 个内部函数(validate_request_body_invariants);外部调用 1 个(clone)。
sse599–612 ↗
fn sse(events: Vec<Value>) -> String
作用:把一组 JSON 事件拼成 SSE 文本。SSE 是服务器一条条推事件给客户端的 HTTP 流格式。
数据流:进去事件 JSON 列表 → 每个事件写成 event: 类型 和可选 data: ... → 出来完整 SSE 字符串。
调用关系:大量测试用它造模型流式返回;更具体的 sse_completed、sse_failed 等也基于它。
调用图:被 455 处调用(create_mock_responses_server_repeating_assistant, create_apply_patch_sse_response, create_exec_command_sse_response, create_final_assistant_message_sse_response, create_request_permissions_sse_response, create_request_user_input_sse_response, create_shell_command_sse_response, external_auth_refreshes_on_unauthorized, review_start_sends_parent_lineage_in_turn_metadata_for_thread_fork_v2, turn_start_forwards_client_metadata_to_responses_request_v2 (+15 more));外部调用 3 个(new, write!, writeln!)。
sse_completed614–616 ↗
fn sse_completed(id: &str) -> String
作用:快速生成“响应创建后立刻完成”的 SSE 流。适合不关心中间内容的测试。
数据流:进去响应 id → 生成 created 和 completed 两个事件,再拼成 SSE → 出来 SSE 字符串。
调用关系:它调用 ev_response_created、ev_completed 和 sse。
调用图:调用 1 个内部函数(sse);被 12 处调用(default_service_tier_override_is_omitted_from_http_turn, flex_service_tier_is_applied_to_http_turn, null_service_tier_override_is_omitted_from_http_turn_with_catalog_default, unsupported_service_tier_is_omitted_from_http_turn, config_personality_none_sends_no_personality, config_personality_some_sets_instructions_template, default_personality_is_pragmatic_without_config_toml, remote_model_friendly_personality_instructions_with_feature, user_turn_personality_none_does_not_add_update_message, openai_model_header_casing_only_mismatch_does_not_warn (+2 more));外部调用 1 个(vec!)。
ev_completed619–627 ↗
fn ev_completed(id: &str) -> Value
作用:生成一个 Responses API 的完成事件,表示这轮模型响应结束。
数据流:进去响应 id → 构造包含 usage 计数字段的 JSON → 出来事件 JSON。
调用关系:它常被 sse_completed 或各类流式测试放进 SSE 序列。
调用图:被 3 处调用(plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done, plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_parsed, unified_exec_prunes_exited_sessions_first);外部调用 1 个(json!)。
ev_response_created630–637 ↗
fn ev_response_created(id: &str) -> Value
作用:生成一个 Responses API 的创建事件,表示一轮响应刚开始。
数据流:进去响应 id → 构造 response.created JSON → 出来事件 JSON。
调用关系:常作为 SSE 流的第一个事件,也被函数调用测试使用。
调用图:外部调用 1 个(json!)。
ev_model_verification_metadata639–648 ↗
fn ev_model_verification_metadata(id: &str, verifications: Vec<&str>) -> Value
作用:生成模型验证相关的元数据事件。测试可用它模拟服务端给出的验证建议。
数据流:进去响应 id 和建议列表 → 包成 response.metadata JSON → 出来事件 JSON。
调用关系:用于模型验证通知相关测试,可被 sse 包成流。
调用图:外部调用 1 个(json!)。
ev_completed_with_tokens650–664 ↗
fn ev_completed_with_tokens(id: &str, total_tokens: i64) -> Value
作用:生成带指定 token 数的完成事件。token 可以粗略理解为模型计费和上下文长度的字数单位。
数据流:进去响应 id 和总 token 数 → 构造 usage 中 input 和 total token 都为该数的 JSON → 出来事件 JSON。
调用关系:用于测试压缩、预算和上下文窗口相关逻辑。
调用图:外部调用 1 个(json!)。
ev_assistant_message667–677 ↗
fn ev_assistant_message(id: &str, text: &str) -> Value
作用:生成一条助手最终消息事件。它表示模型输出了一条完整文本回复。
数据流:进去消息 id 和文本 → 包成 response.output_item.done 的 message JSON → 出来事件 JSON。
调用关系:常和 sse、ev_completed 配合,模拟模型回答。
调用图:被 2 处调用(plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done, plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_parsed);外部调用 1 个(json!)。
user_message_item679–689 ↗
fn user_message_item(text: &str) -> ResponseItem
作用:构造协议里的用户消息对象。它不是 HTTP 事件,而是内部模型数据结构。
数据流:进去用户文本 → 包成 ResponseItem::Message,角色为 user,内容为 input_text → 出来用户消息项。
调用关系:测试需要直接构造历史消息时使用它。
调用图:外部调用 1 个(vec!)。
ev_message_item_added691–701 ↗
fn ev_message_item_added(id: &str, text: &str) -> Value
作用:生成“助手消息项刚被添加”的事件。它适合模拟流式输出开始阶段。
数据流:进去消息 id 和文本 → 构造 response.output_item.added 的 assistant message JSON → 出来事件 JSON。
调用关系:可与文本 delta 事件和完成事件一起组成更真实的流。
调用图:外部调用 1 个(json!)。
ev_output_text_delta703–708 ↗
fn ev_output_text_delta(delta: &str) -> Value
作用:生成一段助手文本增量事件。增量就是流式输出里新吐出来的一小段字。
数据流:进去增量文本 → 包成 response.output_text.delta JSON → 出来事件 JSON。
调用关系:流式解析测试会把它夹在 added 和 done/completed 事件之间。
调用图:被 2 处调用(plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done, plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_parsed);外部调用 1 个(json!)。
ev_reasoning_item710–740 ↗
fn ev_reasoning_item(id: &str, summary: &[&str], raw_content: &[&str]) -> Value
作用:生成一条推理内容完成事件,包括摘要、加密内容和可选原始推理文本。用于测试推理信息如何展示或保存。
数据流:进去 id、摘要数组、原始内容数组 → 摘要转 JSON,原始内容拼接后加一点填充并 base64 编码,必要时附上 content → 出来 reasoning 事件 JSON。
调用关系:它被推理项和自动压缩相关测试调用,生成可被客户端解析的假推理事件。
调用图:被 2 处调用(multiple_auto_compact_per_task_runs_after_token_limit_hit, reasoning_item_is_emitted);外部调用 2 个(Array, json!)。
ev_reasoning_item_added742–756 ↗
fn ev_reasoning_item_added(id: &str, summary: &[&str]) -> Value
作用:生成推理项刚添加的事件,只包含摘要。用于模拟推理流开始。
数据流:进去 id 和摘要数组 → 把摘要转成 summary_text 列表 → 出来 added 事件 JSON。
调用关系:可与推理 delta 事件配合使用。
调用图:外部调用 1 个(json!)。
ev_reasoning_summary_text_delta758–764 ↗
fn ev_reasoning_summary_text_delta(delta: &str) -> Value
作用:生成推理摘要文本的流式增量事件。
数据流:进去增量文本 → 包成带 summary_index: 0 的 JSON → 出来事件 JSON。
调用关系:用于测试客户端接收和拼接推理摘要流。
调用图:外部调用 1 个(json!)。
ev_reasoning_text_delta766–772 ↗
fn ev_reasoning_text_delta(delta: &str) -> Value
作用:生成原始推理文本的流式增量事件。
数据流:进去增量文本 → 包成带 content_index: 0 的 JSON → 出来事件 JSON。
调用关系:用于推理文本流解析相关测试。
调用图:外部调用 1 个(json!)。
ev_web_search_call_added_partial774–783 ↗
fn ev_web_search_call_added_partial(id: &str, status: &str) -> Value
作用:生成网页搜索调用刚添加但信息不完整的事件。用于测试半成品状态。
数据流:进去调用 id 和状态 → 构造 web_search_call added JSON → 出来事件 JSON。
调用关系:网页搜索项展示测试会使用它模拟搜索开始。
调用图:被 1 处调用(web_search_item_is_emitted);外部调用 1 个(json!)。
ev_web_search_call_done785–795 ↗
fn ev_web_search_call_done(id: &str, status: &str, query: &str) -> Value
作用:生成网页搜索调用完成事件,包含搜索词。
数据流:进去 id、状态和查询词 → 构造带 action.search.query 的 done JSON → 出来事件 JSON。
调用关系:常跟 ev_web_search_call_added_partial 一起测试搜索项生命周期。
调用图:被 1 处调用(web_search_item_is_emitted);外部调用 1 个(json!)。
ev_image_generation_call797–813 ↗
fn ev_image_generation_call(
id: &str,
status: &str,
revised_prompt: &str,
result: &str,
) -> Value
作用:生成图片生成调用完成事件。它包含修订后的提示词和生成结果。
数据流:进去 id、状态、修订提示词、结果 → 包成 image_generation_call JSON → 出来事件 JSON。
调用关系:用于图片生成相关测试构造服务端事件。
调用图:外部调用 1 个(json!)。
ev_function_call815–825 ↗
fn ev_function_call(call_id: &str, name: &str, arguments: &str) -> Value
作用:生成模型要求客户端执行普通函数工具的事件。
数据流:进去调用编号、工具名、参数字符串 → 构造 function_call 的 output_item.done JSON → 出来事件 JSON。
调用关系:很多 shell、工具调用、代理测试都用它;更专门的 shell 调用函数也会调用它。
调用图:被 18 处调用(ev_apply_patch_shell_command_call_via_heredoc, ev_shell_command_call_with_args, exec_command_event, shell_event_with_prefix_rule, ev_shell_command_call, tool_call, shell_command_call, exec_command_event, exec_command_event_with_missing_additional_permissions, exec_command_event_with_request_permissions (+8 more));外部调用 1 个(json!)。
ev_function_call_with_namespace827–843 ↗
fn ev_function_call_with_namespace(
call_id: &str,
namespace: &str,
name: &str,
arguments: &str,
) -> Value
作用:生成带命名空间的普通函数调用事件。命名空间用于区分不同工具来源或工具组。
数据流:进去调用编号、命名空间、工具名、参数字符串 → 构造包含 namespace 的 function_call JSON → 出来事件 JSON。
调用关系:用于多工具组或子代理工具相关测试。
调用图:外部调用 1 个(json!)。
ev_tool_search_call845–855 ↗
fn ev_tool_search_call(call_id: &str, arguments: &serde_json::Value) -> Value
作用:生成工具搜索调用事件,表示模型让客户端帮忙搜索可用工具。
数据流:进去调用编号和参数 JSON → 构造 tool_search_call,执行方标为 client → 出来事件 JSON。
调用关系:工具搜索测试会把它放入 SSE 或 WebSocket 返回事件中。
调用图:外部调用 1 个(json!)。
ev_custom_tool_call857–867 ↗
fn ev_custom_tool_call(call_id: &str, name: &str, input: &str) -> Value
作用:生成自定义工具调用事件。自定义工具的输入通常是原始字符串,而不是标准 JSON 参数。
数据流:进去调用编号、工具名、输入文本 → 构造 custom_tool_call JSON → 出来事件 JSON。
调用关系:用于 apply_patch 等特殊工具调用测试。
调用图:外部调用 1 个(json!)。
ev_local_shell_call869–882 ↗
fn ev_local_shell_call(call_id: &str, status: &str, command: Vec<&str>) -> Value
作用:生成本地 shell 调用事件,表示模型请求执行一条命令数组。
数据流:进去调用编号、状态、命令列表 → 包成 local_shell_call 和 action.exec.command → 出来事件 JSON。
调用关系:用于本地命令执行路径的测试。
调用图:外部调用 1 个(json!)。
ev_apply_patch_custom_tool_call887–897 ↗
ev_shell_command_call899–902 ↗
fn ev_shell_command_call(call_id: &str, command: &str) -> Value
作用:快速生成 shell_command 函数调用事件,参数只有一条命令字符串。
数据流:进去调用编号和命令 → 先包成 {command: ...} JSON,再交给带参数版本 → 出来事件 JSON。
调用关系:它调用 ev_shell_command_call_with_args,是 shell 调用测试的便捷入口。
调用图:调用 1 个内部函数(ev_shell_command_call_with_args);外部调用 1 个(json!)。
ev_shell_command_call_with_args904–907 ↗
fn ev_shell_command_call_with_args(call_id: &str, args: &serde_json::Value) -> Value
作用:用自定义参数 JSON 生成 shell_command 函数调用事件。
数据流:进去调用编号和参数 JSON → 把参数序列化成字符串,再构造名为 shell_command 的函数调用 → 出来事件 JSON。
调用关系:它调用 ev_function_call;ev_shell_command_call 是它的简化包装。
调用图:调用 1 个内部函数(ev_function_call);被 1 处调用(ev_shell_command_call);外部调用 1 个(to_string)。
ev_apply_patch_shell_command_call_via_heredoc909–914 ↗
fn ev_apply_patch_shell_command_call_via_heredoc(call_id: &str, patch: &str) -> Value
作用:生成一条通过 shell heredoc 执行 apply_patch 的函数调用。heredoc 是把多行文本喂给命令的一种 shell 写法。
数据流:进去调用编号和补丁文本 → 拼出 apply_patch <<'EOF'... 命令,序列化成 shell_command 参数 → 出来函数调用事件 JSON。
调用关系:它调用 ev_function_call,用于测试模型把补丁伪装成 shell 命令的情况。
调用图:调用 1 个内部函数(ev_function_call);外部调用 2 个(json!, to_string)。
sse_failed916–924 ↗
fn sse_failed(id: &str, code: &str, message: &str) -> String
作用:生成失败的 SSE 响应流。用于模拟远端模型服务返回错误。
数据流:进去响应 id、错误码、错误信息 → 构造 response.failed 事件并交给 sse → 出来 SSE 字符串。
调用关系:错误处理和重试测试会使用它。
调用图:调用 1 个内部函数(sse);被 5 处调用(thread_read_reports_system_error_idle_flag_after_failed_turn, thread_unsubscribe_preserves_cached_status_before_idle_unload, context_window_error_sets_total_tokens_to_model_window, manual_compact_non_context_failure_retries_then_emits_task_error, manual_compact_retries_after_context_window_error);外部调用 1 个(vec!)。
sse_response926–930 ↗
fn sse_response(body: String) -> ResponseTemplate
作用:把 SSE 字符串包装成 HTTP 响应模板,并设置正确的内容类型。
数据流:进去 SSE 文本 → 创建 200 响应,设置 content-type: text/event-stream 和原始 body → 出来 ResponseTemplate。
调用关系:挂载 SSE mock 的函数会调用它,如 mount_sse_once、mount_sse_once_match。
调用图:被 29 处调用(respond, create_mock_responses_server_repeating_assistant, turn_steer_updates_client_metadata_on_follow_up_responses_request_v2, start_ctrl_c_restart_fixture, respond, model_verification_emits_typed_notification_and_warning_v2, openai_model_header_mismatch_emits_model_rerouted_notification_v2, response_model_field_mismatch_emits_model_rerouted_notification_v2_when_header_matches_requested, turn_moderation_metadata_emits_typed_notification_v2, thread_resume_rejects_history_when_thread_is_running (+15 more));外部调用 1 个(new)。
mount_response_once932–939 ↗
async fn mount_response_once(server: &MockServer, response: ResponseTemplate) -> ResponseMock
作用:在假服务器上挂一个只响应一次的 /responses mock,返回调用者给定的 HTTP 响应。
数据流:进去 mock 服务器和响应模板 → 创建基础 POST /responses 匹配器,设置响应和一次次数限制 → 出来记录请求的 ResponseMock。
调用关系:它调用 base_mock,常用于需要自定义响应模板的测试。
调用图:调用 1 个内部函数(base_mock);被 16 处调用(cyber_policy_response_emits_typed_error_notification_v2, model_verification_emits_typed_notification_and_warning_v2, openai_model_header_mismatch_emits_model_rerouted_notification_v2, response_model_field_mismatch_emits_model_rerouted_notification_v2_when_header_matches_requested, turn_moderation_metadata_emits_typed_notification_v2, thread_resume_rejects_history_when_thread_is_running, thread_resume_rejects_mismatched_path_for_running_thread_id, request_permissions_guardian_review_stops_when_cancelled, renews_cache_ttl_on_matching_models_etag, refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch (+6 more))。
mount_response_once_match941–956 ↗
async fn mount_response_once_match(
server: &MockServer,
matcher: M,
response: ResponseTemplate,
) -> ResponseMock
作用:像 mount_response_once,但额外要求请求满足一个自定义匹配条件。
数据流:进去服务器、额外 matcher、响应模板 → 基础 mock 加上 matcher,设置只响应一次 → 出来 ResponseMock。
调用关系:它调用 base_mock,用于测试特定请求形状才应命中的场景。
调用图:调用 1 个内部函数(base_mock);被 3 处调用(plaintext_multi_agent_v2_completion_sends_agent_message, setup_turn_one_with_custom_spawned_child, replaces_invalid_local_image_after_bad_request)。
base_mock958–964 ↗
fn base_mock() -> (MockBuilder, ResponseMock)
作用:创建 /responses POST 请求的基础 wiremock 匹配器,并附带请求记录器。
数据流:进去无业务输入 → 新建 ResponseMock,构造匹配 POST 且路径以 /responses 结尾的 mock → 出来 (MockBuilder, ResponseMock)。
调用关系:大多数 Responses HTTP mock 挂载函数都从它开始。
调用图:调用 1 个内部函数(new);被 6 处调用(mount_response_once, mount_response_once_match, mount_response_sequence, mount_sse_once, mount_sse_once_match, mount_sse_sequence);外部调用 3 个(given, method, path_regex)。
compact_mock966–972 ↗
fn compact_mock() -> (MockBuilder, ResponseMock)
作用:创建 /responses/compact POST 请求的基础 mock。compact 是把长历史压缩成摘要的接口。
数据流:进去无业务输入 → 新建 ResponseMock,构造匹配 POST 且路径以 /responses/compact 结尾的 mock → 出来 builder 和记录器。
调用关系:压缩相关 mock 挂载函数都调用它。
调用图:调用 1 个内部函数(new);被 3 处调用(mount_compact_json_once_match, mount_compact_response_once, mount_compact_user_history_with_summary_sequence);外部调用 3 个(given, method, path_regex)。
models_mock974–980 ↗
fn models_mock() -> (MockBuilder, ModelsMock)
作用:创建 /models GET 请求的基础 mock,并记录模型列表请求。
数据流:进去无业务输入 → 新建 ModelsMock,构造匹配 GET 且路径以 /models 结尾的 mock → 出来 builder 和记录器。
调用关系:模型列表响应挂载函数调用它。
调用图:调用 1 个内部函数(new);被 3 处调用(mount_models_once, mount_models_once_with_delay, mount_models_once_with_etag);外部调用 3 个(given, method, path_regex)。
mount_sse_once_match982–993 ↗
async fn mount_sse_once_match(server: &MockServer, matcher: M, body: String) -> ResponseMock
作用:挂载一个只响应一次、且带自定义匹配条件的 SSE /responses mock。
数据流:进去服务器、matcher、SSE 文本 → 创建基础 mock,加 matcher,包装 SSE 响应并限制一次 → 出来 ResponseMock。
调用关系:它调用 base_mock 和 sse_response,用于精确匹配请求的流式测试。
调用图:调用 2 个内部函数(base_mock, sse_response);被 27 处调用(direct_input_to_multi_agent_v2_subagent_is_rejected, turn_start_emits_spawn_agent_item_with_effective_role_model_metadata_v2, turn_start_emits_spawn_agent_item_with_model_metadata_v2, responses_stream_includes_subagent_header_on_other, responses_stream_includes_subagent_header_on_review, v2_nested_spawn_checks_shared_active_execution_capacity, run_subagent_global_instruction_case, spawned_subagent_execpolicy_amendment_propagates_to_parent_session, context_window_error_sets_total_tokens_to_model_window, provider_auth_command_supplies_bearer_token (+15 more))。
mount_sse_once995–1002 ↗
async fn mount_sse_once(server: &MockServer, body: String) -> ResponseMock
作用:挂载一个只响应一次的 SSE /responses mock。
数据流:进去服务器和 SSE 文本 → 创建基础 mock,设置 SSE 响应和一次次数限制 → 出来 ResponseMock。
调用关系:它调用 base_mock 和 sse_response,是最常用的流式响应挂载工具。
调用图:调用 2 个内部函数(base_mock, sse_response);被 352 处调用(review_start_sends_parent_lineage_in_turn_metadata_for_thread_fork_v2, turn_start_forwards_client_metadata_to_responses_request_v2, turn_start_sends_fork_lineage_in_turn_metadata_for_thread_fork_v2, turn_start_sends_other_subagent_lineage_after_cold_thread_resume_v2, selected_executor_root_exposes_plugin_skill, standalone_image_generation_is_exposed_in_code_mode_only, local_executor_does_not_expose_orchestrator_skills, turn_start_accepts_output_schema_v2, turn_start_output_schema_is_per_turn_v2, thread_inject_items_adds_raw_response_items_to_thread_history (+15 more))。
mount_compact_json_once_match1004–1023 ↗
async fn mount_compact_json_once_match(
server: &MockServer,
matcher: M,
body: serde_json::Value,
) -> ResponseMock
作用:挂载一个只响应一次、带额外匹配条件的 /responses/compact JSON 响应。
数据流:进去服务器、matcher、JSON body → 创建 compact mock,加 matcher,设置 200 JSON 响应 → 出来 ResponseMock。
调用关系:它调用 compact_mock,用于压缩请求形状需要额外验证的测试。
调用图:调用 1 个内部函数(compact_mock);外部调用 2 个(new, clone)。
mount_compact_json_once1025–1033 ↗
async fn mount_compact_json_once(server: &MockServer, body: serde_json::Value) -> ResponseMock
作用:挂载一个只响应一次的 /responses/compact JSON 响应。
数据流:进去服务器和 JSON body → 包成 200 application/json 响应模板,再交给通用挂载函数 → 出来 ResponseMock。
调用关系:它调用 mount_compact_response_once,被大量远端压缩测试使用。
调用图:调用 1 个内部函数(mount_compact_response_once);被 19 处调用(auto_compaction_remote_emits_started_and_completed_items, auto_compact_counts_encrypted_reasoning_before_last_user, auto_compact_runs_after_resume_when_token_usage_is_over_limit, auto_compact_runs_when_reasoning_header_clears_between_turns, auto_remote_compact_failure_stops_agent_loop, remote_compact_and_resume_refresh_stale_developer_instructions, remote_compact_filters_deferred_dynamic_tools, remote_compact_persists_replacement_history_in_rollout, remote_compact_refreshes_stale_developer_instructions_without_resume, remote_compact_replaces_history_for_followups (+9 more));外部调用 1 个(new)。
mount_compact_user_history_with_summary_once1038–1043 ↗
async fn mount_compact_user_history_with_summary_once(
server: &MockServer,
summary_text: &str,
) -> ResponseMock
作用:挂载一次模拟远端压缩响应:保留用户和开发者消息,追加一条摘要压缩项。
数据流:进去服务器和摘要文本 → 把单个摘要变成列表,交给序列版本 → 出来 ResponseMock。
调用关系:它调用 mount_compact_user_history_with_summary_sequence,是单次压缩测试的便捷入口。
调用图:调用 1 个内部函数(mount_compact_user_history_with_summary_sequence);被 12 处调用(assert_remote_manual_compact_request_parity, auto_remote_compact_trims_function_call_history_to_fit_context_window, remote_compact_rewrites_multiple_trailing_function_call_outputs, remote_compact_runs_automatically, remote_compact_trim_estimate_uses_session_base_instructions, remote_compact_trims_function_call_history_to_fit_context_window, remote_compact_trims_tool_search_output_to_empty_tools_array, remote_manual_compact_emits_context_compaction_items, snapshot_request_shape_remote_mid_turn_continuation_compaction, snapshot_request_shape_remote_pre_turn_compaction_including_incoming_user_message (+2 more));外部调用 1 个(vec!)。
mount_compact_user_history_with_summary_sequence1047–1118 ↗
async fn mount_compact_user_history_with_summary_sequence(
server: &MockServer,
summary_texts: Vec<String>,
) -> ResponseMock
作用:挂载一串模拟远端压缩响应,每次 compact 请求依次使用下一段摘要。
数据流:进去服务器和摘要文本列表 → responder 收到请求时解压并解析 input,只保留 user/developer message,追加 compaction 摘要 → 出来记录请求的 ResponseMock。
调用关系:它调用 compact_mock,内部 responder 实现 Respond,用于多次压缩流程测试。
调用图:调用 1 个内部函数(compact_mock);被 2 处调用(mount_compact_user_history_with_summary_once, snapshot_request_shape_remote_mid_turn_compaction_multi_summary_reinjects_above_last_summary);外部调用 1 个(new)。
mount_compact_response_once1120–1130 ↗
async fn mount_compact_response_once(
server: &MockServer,
response: ResponseTemplate,
) -> ResponseMock
作用:挂载一个只响应一次的 /responses/compact 响应模板。
数据流:进去服务器和响应模板 → 创建 compact mock,设置响应并限制一次 → 出来 ResponseMock。
调用关系:它调用 compact_mock,被 JSON compact 和特殊 compact 测试复用。
调用图:调用 1 个内部函数(compact_mock);被 4 处调用(mount_compact_json_once, remote_mid_turn_compact_v1_sends_turn_state_over_http, remote_pre_turn_compact_response_seeds_turn_state, snapshot_request_shape_remote_pre_turn_compaction_context_window_exceeded)。
mount_models_once1132–1143 ↗
async fn mount_models_once(server: &MockServer, body: ModelsResponse) -> ModelsMock
作用:挂载一个只响应一次的 /models JSON 响应。
数据流:进去服务器和模型列表对象 → 创建模型 mock,设置 200 JSON body → 出来 ModelsMock。
调用关系:它调用 models_mock;start_mock_server 也用它提供默认空模型列表。
调用图:调用 1 个内部函数(models_mock);被 40 处调用(list_models_uses_chatgpt_remote_catalog_as_source_of_truth, new_uses_active_provider_for_model_refresh, start_mock_server, remote_model_override_uses_catalog_model_for_strict_auto_review, body_after_prefix_model_switch_budget_compacts_with_next_model, pre_sampling_compact_recovers_comp_hash_after_resume, pre_sampling_compact_runs_after_resume_and_switch_to_smaller_model, pre_sampling_compact_runs_on_switch_to_smaller_context_model, pre_sampling_compact_runs_when_comp_hash_changes, pre_sampling_compact_skips_missing_comp_hash_after_resume (+15 more));外部调用 2 个(new, clone)。
mount_models_once_with_delay1145–1161 ↗
async fn mount_models_once_with_delay(
server: &MockServer,
body: ModelsResponse,
delay: Duration,
) -> ModelsMock
作用:挂载一个带延迟的 /models 响应,用来测试超时或等待行为。
数据流:进去服务器、模型列表、延迟时间 → 创建模型 mock,设置 JSON 响应和延迟 → 出来 ModelsMock。
调用关系:它调用 models_mock,超时测试会用它模拟慢服务。
调用图:调用 1 个内部函数(models_mock);被 1 处调用(remote_models_request_times_out_after_5s);外部调用 2 个(new, clone)。
mount_models_once_with_etag1163–1180 ↗
async fn mount_models_once_with_etag(
server: &MockServer,
body: ModelsResponse,
etag: &str,
) -> ModelsMock
作用:挂载一个带 ETag 头的 /models 响应。ETag 是服务端告诉客户端“这份内容版本号”的 HTTP 头。
数据流:进去服务器、模型列表、etag 字符串 → 创建模型 mock,响应 JSON 并插入 ETag 头 → 出来 ModelsMock。
调用关系:它调用 models_mock,用于模型目录缓存刷新测试。
调用图:调用 1 个内部函数(models_mock);被 2 处调用(renews_cache_ttl_on_matching_models_etag, refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch);外部调用 2 个(new, clone)。
start_mock_server1182–1192 ↗
async fn start_mock_server() -> MockServer
作用:启动一个 wiremock HTTP 假服务器,并默认挂上空的 /models 响应。
数据流:进去无业务输入 → 启动服务器,设置较大的 body 打印限制,再挂载默认模型列表 → 出来 MockServer。
调用关系:大量集成测试先调用它,再继续挂载 Responses 或 compact 响应。
调用图:调用 1 个内部函数(mount_models_once);被 623 处调用(create_mock_responses_server_repeating_assistant, create_mock_responses_server_sequence, create_mock_responses_server_sequence_unchecked, review_start_sends_parent_lineage_in_turn_metadata_for_thread_fork_v2, turn_start_forwards_client_metadata_to_responses_request_v2, turn_start_sends_fork_lineage_in_turn_metadata_for_thread_fork_v2, turn_start_sends_other_subagent_lineage_after_cold_thread_resume_v2, turn_steer_updates_client_metadata_on_follow_up_responses_request_v2, auto_compaction_local_emits_started_and_completed_items, auto_compaction_remote_emits_started_and_completed_items (+15 more));外部调用 3 个(Limited, builder, new)。
start_websocket_server1199–1210 ↗
async fn start_websocket_server(connections: Vec<Vec<Vec<Value>>>) -> WebSocketTestServer
作用:启动一个简单 WebSocket 测试服务器,按给定剧本为每条连接、每个请求返回事件。
数据流:进去连接剧本:连接列表、请求列表、事件列表 → 转成默认连接配置,再启动带头配置的版本 → 出来 WebSocketTestServer。
调用关系:它调用 start_websocket_server_with_headers,是实时 WebSocket 测试的常用入口。
调用图:调用 1 个内部函数(start_websocket_server_with_headers);被 81 处调用(turn_start_forwards_client_metadata_to_responses_websocket_request_body_v2, realtime_conversation_requires_feature_flag, realtime_conversation_stop_emits_closed_notification, realtime_conversation_streams_v2_notifications, realtime_start_can_skip_startup_context, realtime_text_output_modality_requests_text_output_and_final_transcript, realtime_webrtc_start_surfaces_backend_error, websocket_first_turn_uses_startup_prewarm_and_create, websocket_test_codex_shell_chain, websocket_v2_first_turn_drops_fast_tier_after_startup_prewarm (+15 more))。
start_websocket_server_with_headers1212–1385 ↗
async fn start_websocket_server_with_headers(
connections: Vec<WebSocketConnectionConfig>,
) -> WebSocketTestServer
作用:启动可配置的 WebSocket 测试服务器,能记录握手和消息,按剧本发送响应事件,还能控制响应头、握手延迟和是否关闭连接。
数据流:进去一组连接配置 → 绑定本地端口,后台循环接受连接;握手时记录 URI/头并插入响应头;收到每条消息后解析 JSON、写日志、发送对应事件帧 → 出来包含地址、日志和关闭句柄的服务器对象。
调用关系:它调用 websocket_accept_config 配置压缩扩展,调用 parse_ws_request_body 解析收到的消息;测试通过返回的 WebSocketTestServer 等待和检查记录。
调用图:调用 2 个内部函数(parse_ws_request_body, websocket_accept_config);被 15 处调用(attestation_generate_round_trip_adds_header_to_responses_websocket_handshake, new_with_main_loop_responses_server_and_sandbox, realtime_webrtc_start_emits_sdp_notification, start_websocket_server, websocket_first_turn_handles_handshake_delay_with_startup_prewarm, responses_websocket_emits_rate_limit_events, responses_websocket_emits_reasoning_included_event, responses_websocket_v2_surfaces_terminal_error_without_close_handshake, conversation_webrtc_close_while_sideband_connecting_drops_pending_join, conversation_webrtc_start_posts_generated_session (+5 more));外部调用 17 个(clone, new, new, new, bind, new, from, eprintln!, format!, channel (+7 more))。
parse_ws_request_body1387–1393 ↗
fn parse_ws_request_body(message: Message) -> Option<Value>
作用:把 WebSocket 收到的文本帧或二进制帧解析成 JSON。
数据流:进去一条 WebSocket 消息 → 如果是 Text 就按字符串解析 JSON,如果是 Binary 就按字节解析 JSON,其他类型忽略 → 出来 JSON 或空。
调用关系:它被 start_websocket_server_with_headers 的接收循环调用。
调用图:被 1 处调用(start_websocket_server_with_headers);外部调用 2 个(from_slice, from_str)。
websocket_accept_config1395–1402 ↗
fn websocket_accept_config() -> WebSocketConfig
作用:生成 WebSocket 接受连接时使用的配置,并开启 permessage-deflate 压缩扩展。
数据流:进去无业务输入 → 创建默认扩展配置,打开 deflate,再放进 WebSocket 配置 → 出来配置对象。
调用关系:它被 start_websocket_server_with_headers 用在握手接受函数里。
调用图:被 1 处调用(start_websocket_server_with_headers);外部调用 3 个(default, default, default)。
mount_function_call_agent_response1410–1433 ↗
async fn mount_function_call_agent_response(
server: &MockServer,
call_id: &str,
arguments: &str,
tool_name: &str,
) -> FunctionCallResponseMocks
作用:挂载两次模型响应:第一次让模型发起工具调用,第二次返回最终助手消息。用于测试“模型叫工具,客户端执行,再继续问模型”的流程。
数据流:进去服务器、调用编号、参数、工具名 → 构造第一次 SSE:created、function_call、completed;再构造第二次 SSE:assistant message、completed → 出来包含两次请求记录器的结构。
调用关系:它调用 sse、ev_response_created、ev_function_call、ev_completed、ev_assistant_message 和 mount_sse_once。
调用图:调用 2 个内部函数(mount_sse_once, sse);被 2 处调用(shell_zsh_fork_skill_scripts_ignore_declared_permissions, shell_zsh_fork_still_enforces_workspace_write_sandbox);外部调用 1 个(vec!)。
mount_sse_sequence1438–1475 ↗
async fn mount_sse_sequence(server: &MockServer, bodies: Vec<String>) -> ResponseMock
作用:挂载一串 SSE 响应,让每次 POST /responses 依次拿到下一段流。
数据流:进去服务器和 SSE body 列表 → responder 用原子计数记录第几次调用,按顺序取响应;超过数量就失败 → 出来 ResponseMock。
调用关系:它调用 base_mock,并设置 wiremock 期望调用次数,适合多轮对话测试。
调用图:调用 1 个内部函数(base_mock);被 263 处调用(auto_compaction_local_emits_started_and_completed_items, auto_compaction_remote_emits_started_and_completed_items, thread_compact_start_triggers_compaction_and_returns_empty_response, selected_executor_plugin_exposes_its_stdio_mcp_only_to_that_thread, external_agent_config_import_compacts_huge_session_before_first_follow_up, run_image_edit_test, standalone_image_generation_failure_emits_terminal_item, standalone_image_generation_is_callable_from_code_mode_only, standalone_image_generation_returns_saved_path_hint_to_model, orchestrator_skill_can_read_referenced_resource_without_an_executor (+15 more));外部调用 1 个(new)。
mount_response_sequence1479–1514 ↗
async fn mount_response_sequence(
server: &MockServer,
responses: Vec<ResponseTemplate>,
) -> ResponseMock
作用:挂载一串普通 HTTP 响应模板,让每次 POST /responses 依次返回下一份响应。
数据流:进去服务器和响应模板列表 → responder 按调用次数取对应模板;缺少响应就失败 → 出来 ResponseMock。
调用关系:它调用 base_mock,用于需要混合状态码、错误或不同 body 的多请求测试。
调用图:调用 1 个内部函数(base_mock);被 18 处调用(external_auth_refresh_error_fails_turn, external_auth_refresh_invalid_access_token_fails_turn, external_auth_refresh_mismatched_workspace_fails_turn, external_auth_refreshes_on_unauthorized, turn_steer_updates_client_metadata_on_follow_up_responses_request_v2, thread_resume_rejoins_running_thread_even_with_override_mismatch, thread_settings_update_while_turn_is_active_emits_notification, turn_start_tracks_turn_event_analytics, guardian_review_surfaces_responses_api_errors_in_rejection_reason, responses_stream_includes_turn_metadata_header_for_git_workspace_e2e (+8 more));外部调用 1 个(new)。
validate_request_body_invariants1526–1643 ↗
fn validate_request_body_invariants(request: &wiremock::Request)
作用:检查发往 /responses 的请求体是否满足工具调用配对规则。它像门卫一样,防止测试悄悄接受错误历史。
数据流:进去 wiremock 请求 → 只处理 POST /responses;解压并解析 JSON,读取 input;收集各种调用 id 和输出 id;检查输出不能没调用、调用不能没输出 → 违规就断言失败,正常则无返回。
调用关系:它由 ResponseMock::matches 在每次捕获请求时调用,依赖 decode_body_bytes 读取可能压缩的请求体。
调用图:调用 1 个内部函数(decode_body_bytes);被 1 处调用(matches);外部调用 2 个(assert!, from_slice)。
core/tests/common/streaming_sse.rs源码 ↗
真实的流式接口不会一次把答案全吐出来,而是一小块一小块发。测试如果直接连真实服务,会慢、不稳定,也很难控制时机。这个文件就像一个可遥控的假服务:测试先准备好一队要返回的 SSE 分块,SSE 可以理解成“服务器边生成边推送消息”的文本流;每个分块还可以带一个 gate,也就是一把“放行开关”,开关没打开就先不发。服务器支持两个路由:GET /v1/models 返回空模型列表,POST /v1/responses 返回预先排好的流式内容。它还会记录收到的请求正文,方便测试事后检查客户端到底发了什么。文件后半部分是一组自测,确认它能按顺序发送、等待开关、返回错误码、读完请求体,并且 shutdown 能停掉后台监听任务。
StreamingSseServer::uri30–32 ↗
fn uri(&self) -> &str
作用:把这个测试服务器的访问地址拿出来,例如 http://127.0.0.1:某个端口。测试或其他测试辅助代码需要用这个地址去连接假服务器。
数据流:进去的是服务器对象本身 → 它读取对象里保存好的 uri 字符串 → 出来的是这个地址的只读引用,不修改任何状态。
调用关系:它是服务器句柄上最常用的小入口之一;调用方比如 build_with_streaming_server 会先启动服务器,再通过这个函数把地址交给被测客户端。
调用图:被 1 处调用(build_with_streaming_server)。
StreamingSseServer::requests34–36 ↗
async fn requests(&self) -> Vec<Vec<u8>>
作用:取出目前服务器收到过的所有请求正文。测试可以用它检查客户端有没有把正确内容发到 /v1/responses。
数据流:进去的是服务器对象 → 它给保存请求列表的异步互斥锁加锁,互斥锁就是防止多个异步任务同时改同一份数据的一把锁 → 出来的是请求正文列表的拷贝,原列表还留在服务器里。
调用关系:它通常在测试发完请求后使用,用来观察服务器记录;它不参与回包,只是给测试做验收。
StreamingSseServer::wait_for_request_count38–45 ↗
async fn wait_for_request_count(&self, count: usize)
作用:等待服务器至少收到指定数量的请求。它解决的是测试里常见的“我还没等到客户端发请求,就开始断言”的抢跑问题。
数据流:进去的是目标数量 count → 它反复查看已记录请求的长度,不够就等待通知 → 等数量达到后返回,不产出新数据,但保证后续检查时请求已经到达。
调用关系:它和服务器接收请求的后台任务配合:后台任务每记录一个请求就发通知,这个函数收到通知后再检查数量,避免测试靠睡眠猜时间。
StreamingSseServer::shutdown47–50 ↗
async fn shutdown(self)
作用:关掉这个测试服务器,并等待它的后台任务真正结束。没有它,测试结束后可能留下还在监听端口的异步任务。
数据流:进去的是服务器句柄本身 → 它通过一次性通道发送关闭信号,再等待后台任务结束 → 出来没有返回值,但服务器的监听循环会停掉。
调用关系:测试通常在最后调用它收尾;它把关闭信号交给 start_streaming_sse_server 创建的后台监听循环。
调用图:外部调用 1 个(send)。
start_streaming_sse_server59–173 ↗
async fn start_streaming_sse_server(
responses: Vec<Vec<StreamingSseChunk>>,
) -> (StreamingSseServer, Vec<oneshot::Receiver<i64>>)
作用:启动一个可控的假 SSE 服务器,并把服务器句柄和每条响应完成时会响的通知器返回给测试。测试用它来模拟外部流式接口。
数据流:进去的是多组响应分块,每组对应一次 POST /v1/responses 请求 → 它绑定本机随机端口,准备共享状态、请求记录、关闭信号和完成通知,然后启动后台监听任务 → 出来是服务器句柄,以及一组一次性接收器;每条流发完最后一块后,对应接收器会收到完成时间戳。
调用关系:这是整个文件的核心启动函数。很多测试都会先调用它;它内部再调用读请求、解析请求、读取正文、写普通 HTTP 响应、写 SSE 头、取下一条排队响应、记录完成时间等小函数,把一个迷你 HTTP/SSE 流程串起来。
调用图:被 28 处调用(thread_unsubscribe_during_turn_keeps_turn_running, gated_chunks_wait_for_signal_and_preserve_order, get_models_returns_empty_list, malformed_request_returns_400, multiple_responses_are_fifo_and_completion_timestamps_monotonic, none_gate_streams_immediately, post_responses_streams_in_order_and_closes, post_responses_with_no_queue_returns_500, responses_post_drains_request_body, shutdown_terminates_accept_loop (+15 more));外部调用 12 个(clone, new, new, bind, new, new, with_capacity, from, format!, channel (+2 more))。
take_next_stream180–187 ↗
async fn take_next_stream(
state: &TokioMutex<StreamingSseState>,
) -> Option<(Vec<StreamingSseChunk>, oneshot::Sender<i64>)>
作用:从排队的响应里拿出下一条要返回的流,同时拿出它对应的完成通知发送端。它保证响应内容和完成通知是一一配对的。
数据流:进去的是共享的服务器状态 → 它加锁后从响应队列头部取出一组分块,又从完成通知队列头部取出一个发送器 → 出来是这一组分块和发送器;如果队列空了,就出来 None。
调用关系:POST /v1/responses 到来时会用它决定这次请求该拿哪条预设响应;自测 tests::take_next_stream_consumes_in_lockstep 专门确认它按先进先出的顺序取,并且不会把响应和完成通知错配。
调用图:被 1 处调用(take_next_stream_consumes_in_lockstep);外部调用 1 个(lock)。
read_http_request189–206 ↗
async fn read_http_request(stream: &mut tokio::net::TcpStream) -> (String, Vec<u8>)
作用:从 TCP 连接里先读出 HTTP 请求头,读到头部结束标记就停。这样服务器能尽快知道请求是什么,而不是傻等整个连接关闭。
数据流:进去的是一个 TCP 流 → 它一段一段读字节,直到发现 \r\n\r\n 这个 HTTP 头结束标记;多读出来的部分会当作正文开头保留下来 → 出来是请求头字符串和已经读到的正文前缀。
调用关系:每个新连接进来后,后台任务先用它读请求;它依赖 header_terminator_index 找头部结束位置。tests::read_http_request_returns_after_header_terminator 验证它不会多等。
调用图:调用 1 个内部函数(header_terminator_index);被 1 处调用(read_http_request_returns_after_header_terminator);外部调用 3 个(from_utf8_lossy, read, new)。
parse_request_line208–214 ↗
fn parse_request_line(request: &str) -> Option<(&str, &str)>
作用:从 HTTP 请求的第一行里拆出方法和路径,比如 GET 和 /v1/models。服务器靠它决定该走哪个假接口。
数据流:进去的是完整请求头文本 → 它取第一行,按空白切开,拿前两个字段 → 出来是方法和路径;如果格式不够,就出来 None。
调用关系:连接处理逻辑读完请求头后会调用它;如果它解析失败,服务器就返回 400 bad request。tests::parse_request_line_handles_valid_and_invalid 检查正常和异常输入。
header_terminator_index216–218 ↗
fn header_terminator_index(buf: &[u8]) -> Option<usize>
作用:在一串字节里寻找 HTTP 请求头结束的位置。HTTP 头和正文之间靠连续的空行,也就是 \r\n\r\n,来分隔。
数据流:进去的是收到的原始字节 → 它滑动检查每连续 4 个字节是否等于 \r\n\r\n → 出来是结束标记的起始位置,找不到就出来 None。
调用关系:read_http_request 每读到一批字节后都会用它判断头部是否完整;它是低层的小工具。
调用图:被 1 处调用(read_http_request)。
content_length220–231 ↗
fn content_length(headers: &str) -> Option<usize>
作用:从 HTTP 头里找 Content-Length,也就是请求正文有多少字节。没有这个数字,服务器就不知道还要继续读多少正文。
数据流:进去的是请求头文本 → 它逐行找名叫 content-length 的头,大小写不敏感,然后把值转成数字 → 出来是正文长度;找不到或转不成数字就出来 None。
调用关系:read_request_body 会先调用它;如果没有 Content-Length,就直接把已经读到的正文前缀当作完整正文。
调用图:被 1 处调用(read_request_body)。
read_request_body233–255 ↗
async fn read_request_body(
stream: &mut tokio::net::TcpStream,
headers: &str,
mut body_prefix: Vec<u8>,
) -> std::io::Result<Vec<u8>>
作用:按 Content-Length 把请求正文读完整。它避免客户端发了 JSON 正文但服务器只读到一半,导致测试结果不真实。
数据流:进去的是 TCP 流、请求头、以及读请求头时顺手多读到的正文前缀 → 它先查 Content-Length,前缀太长就截断,不够就继续从连接里读剩余字节 → 出来是完整正文的字节数组,或者读网络时的错误。
调用关系:GET /v1/models 和 POST /v1/responses 分支都会用它把请求体“吸干”;其中 responses_post_drains_request_body 这个测试专门确认真实 HTTP 客户端发来的 JSON 正文也会被读完。
调用图:调用 1 个内部函数(content_length);外部调用 2 个(read_exact, vec!)。
write_sse_headers257–260 ↗
async fn write_sse_headers(stream: &mut tokio::net::TcpStream) -> std::io::Result<()>
作用:给客户端写出 SSE 流式响应的 HTTP 头。它告诉客户端:接下来不是普通 JSON,而是 text/event-stream 这种持续推送的文本流。
数据流:进去的是 TCP 流 → 它写入状态码 200、content-type: text/event-stream、禁止缓存、连接关闭等头部 → 出来是写入成功或失败的结果,不写具体事件内容。
调用关系:POST /v1/responses 准备发送分块前会先调用它;之后服务器才逐块写 StreamingSseChunk 的 body。
调用图:外部调用 1 个(write_all)。
write_http_response262–275 ↗
async fn write_http_response(
stream: &mut tokio::net::TcpStream,
status: i64,
body: &str,
content_type: &str,
) -> std::io::Result<()>
作用:写一个普通的、一次性结束的 HTTP 响应,比如 200 的模型列表、400 错误、404 找不到、500 没有预设响应。
数据流:进去的是 TCP 流、状态码、响应正文和内容类型 → 它计算正文长度,拼出 HTTP 头,写头、写正文,然后关闭写入方向 → 出来是 I/O 成功或失败的结果。
调用关系:服务器遇到非流式场景或错误时会用它快速回包;它和 write_sse_headers 分工,一个管普通短响应,一个管流式响应开头。
unix_ms_now277–282 ↗
fn unix_ms_now() -> i64
作用:取当前 Unix 时间的毫秒数。这里用它标记一条流式响应是什么时候发送完成的。
数据流:进去没有参数 → 它读取系统时间,减去 1970-01-01 这个 Unix 起点,换算成毫秒 → 出来是一个整数时间戳;如果系统时间异常,就退回 0。
调用关系:POST /v1/responses 发完所有分块后会调用它,再把时间戳发给对应的完成通知接收器;相关测试会检查这个时间戳大于 0,多个响应大体按时间递增。
调用图:外部调用 1 个(now)。
tests::split_response293–297 ↗
fn split_response(response: &str) -> (&str, &str)
作用:把一整段 HTTP 响应拆成头部和正文两段,方便测试分别检查状态码、头字段和内容。
数据流:进去的是完整响应字符串 → 它按 \r\n\r\n 这个分隔符切开 → 出来是头部文本和正文文本;如果没有分隔符,测试会直接失败。
调用关系:多个测试在读完整响应后会调用它,然后再交给 status_code、header_value 或直接比较正文。
tests::status_code299–305 ↗
fn status_code(headers: &str) -> u16
作用:从 HTTP 响应头第一行里取出状态码,比如 200、404。这样测试不用手写字符串包含判断。
数据流:进去的是响应头文本 → 它取第一行,按空白切开,读取第二段并转成数字 → 出来是 u16 状态码;格式不对时测试失败。
调用关系:配合 tests::split_response 使用;各个路由测试用它确认服务器返回了预期的成功或错误状态。
tests::header_value307–318 ↗
fn header_value(headers: &'a str, name: &str) -> Option<&'a str>
作用:从响应头里按名字查某个头字段的值,比如 content-type。它让测试能检查服务器有没有告诉客户端正确的数据类型。
数据流:进去的是响应头文本和要找的字段名 → 它逐行按冒号拆开,并忽略大小写比较字段名 → 出来是字段值的引用,找不到就出来 None。
调用关系:许多测试在确认状态码后继续用它检查 content-type,确保普通 JSON、纯文本错误和 SSE 流没有混淆。
tests::connect320–325 ↗
async fn connect(uri: &str) -> TcpStream
作用:按服务器给出的 http:// 地址建立一条原始 TCP 连接。测试用它直接发送手写 HTTP 请求,方便控制边界情况。
数据流:进去的是服务器 URI → 它去掉 http:// 前缀,拿剩下的地址和端口连接 → 出来是一条 TcpStream;连接失败时测试失败。
调用关系:大多数自测先 start_streaming_sse_server,再用它连接服务器;它把地址转换和连接细节藏起来。
调用图:外部调用 1 个(connect)。
tests::read_to_end327–331 ↗
async fn read_to_end(stream: &mut TcpStream) -> String
作用:从 TCP 连接一直读到对方关闭,并把收到的字节转成字符串。测试用它获取完整响应。
数据流:进去的是可读的 TCP 流 → 它把剩余所有字节读进缓冲区,再用宽松的 UTF-8 转换变成字符串 → 出来是完整响应文本。
调用关系:普通响应测试和完整流式响应测试都会用它;它通常跟 split_response 一起出现。
调用图:外部调用 3 个(from_utf8_lossy, read_to_end, new)。
tests::read_until333–354 ↗
async fn read_until(stream: &mut TcpStream, needle: &str) -> (String, String)
作用:从 TCP 连接读到某个指定文本出现为止,并把之前读到的部分和后面多读到的部分分开。测试用它验证“头已经来了,但正文还没放行”。
数据流:进去的是 TCP 流和要等待的字符串 needle → 它一小块一小块读,直到缓冲区里出现 needle → 出来是包含 needle 的前半段字符串,以及 needle 后面已经顺手读到的剩余字符串;如果连接结束还没找到,也返回已读内容。
调用关系:需要检查流式响应时机的测试会用它先读到 HTTP 头结束,再观察正文是否被 gate 卡住。
调用图:外部调用 4 个(from_utf8_lossy, new, read, new)。
tests::send_request356–361 ↗
async fn send_request(stream: &mut TcpStream, request: &str)
作用:把一段手写 HTTP 请求字符串写进 TCP 连接。它让测试可以直接模拟各种请求,包括格式错误的请求。
数据流:进去的是 TCP 流和请求文本 → 它把文本转成字节写出去 → 出来没有业务结果;写失败时测试失败。
调用关系:大多数测试连接服务器后马上调用它;后面再用 read_to_end 或 read_until 读取服务器响应。
调用图:外部调用 1 个(write_all)。
tests::get_models_returns_empty_list364–388 ↗
async fn get_models_returns_empty_list()
作用:确认 GET /v1/models 会返回一个空模型列表,并且是 JSON。这个路由用来满足被测客户端启动时可能会查模型列表的需求。
数据流:进去没有外部输入 → 它启动空响应队列的服务器,发送 GET /v1/models,读完整响应 → 断言状态码是 200、content-type 是 application/json、正文是 {data: [], object: list},最后关闭服务器。
调用关系:它通过 start_streaming_sse_server 搭环境,再使用 connect、send_request、read_to_end、split_response 等测试辅助函数完成检查。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 7 个(new, assert_eq!, connect, read_to_end, send_request, split_response, from_str)。
tests::post_responses_streams_in_order_and_closes391–424 ↗
async fn post_responses_streams_in_order_and_closes()
作用:确认 POST /v1/responses 会按预设顺序发送多个 SSE 分块,并在发完后关闭连接。它验证最基本的流式返回行为。
数据流:进去没有外部输入 → 它准备两个分块 event: one 和 event: two,启动服务器,发送 POST 请求 → 读到的正文必须是两个分块顺序拼接;完成通知会返回一个大于 0 的时间戳。
调用关系:它覆盖 start_streaming_sse_server 的主要路径:取队列、写 SSE 头、逐块写 body、发送完成时间、关闭流。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 7 个(assert!, assert_eq!, connect, read_to_end, send_request, split_response, vec!)。
tests::none_gate_streams_immediately427–445 ↗
async fn none_gate_streams_immediately()
作用:确认没有 gate 的分块会立刻发出,不会被错误地卡住。gate 可以理解成测试手里的放行按钮,没有按钮就应该马上通过。
数据流:进去没有外部输入 → 它准备一个 gate 为 None 的分块,发送 POST 后先读到响应头,再读剩余正文 → 断言正文马上就是 event: immediate。
调用关系:它使用 read_until 先停在 HTTP 头结束处,再用 read_to_end 看正文;这专门检查分块放行逻辑的默认行为。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 7 个(assert_eq!, connect, read_until, send_request, split_response, format!, vec!)。
tests::post_responses_with_no_queue_returns_500448–462 ↗
async fn post_responses_with_no_queue_returns_500()
作用:确认没有预设响应时,POST /v1/responses 会返回 500 和明确错误文字。这样测试失败时原因更清楚,而不是连接莫名其妙断掉。
数据流:进去没有外部输入 → 它用空响应队列启动服务器,发送 POST 请求 → 读到响应后断言状态码是 500、类型是 text/plain、正文是 no responses queued。
调用关系:它检查 take_next_stream 取不到流时的错误分支,也验证 write_http_response 能写出错误响应。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 6 个(new, assert_eq!, connect, read_to_end, send_request, split_response)。
tests::gated_chunks_wait_for_signal_and_preserve_order465–514 ↗
async fn gated_chunks_wait_for_signal_and_preserve_order()
作用:确认带 gate 的分块会等测试发信号后才发送,而且第二块不会抢在第一块前面。这个测试用来模拟真实服务“过一会儿才吐下一段”的情况。
数据流:进去没有外部输入 → 它给两个分块分别准备一次性放行信号,发送 POST 后先确认正文没来;放行第一块后只读到第一块,再放行第二块后读到第二块 → 最终证明等待和顺序都正确。
调用关系:它强力覆盖 start_streaming_sse_server 里逐块等待 gate 的逻辑;使用 timeout 确认在没放行时读操作会一直等。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 11 个(from_millis, assert!, assert_eq!, connect, read_to_end, read_until, send_request, split_response, channel, timeout (+1 more))。
tests::multiple_responses_are_fifo_and_completion_timestamps_monotonic517–558 ↗
async fn multiple_responses_are_fifo_and_completion_timestamps_monotonic()
作用:确认多次 POST 会按先进先出的顺序拿预设响应,并且每次完成都有对应时间戳。先进先出就像排队买票,先排的先服务。
数据流:进去没有外部输入 → 它准备 first 和 second 两条响应,连续发两次 POST → 第一次读到 first,第二次读到 second;两个完成通知都有时间戳,且第二个不早于第一个。
调用关系:它主要验证 take_next_stream 对 responses 队列和 completions 队列的同步消费,也间接检查完成通知没有错位。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 7 个(assert!, assert_eq!, connect, read_to_end, send_request, split_response, vec!)。
tests::unknown_route_returns_404561–575 ↗
async fn unknown_route_returns_404()
作用:确认不认识的路由会返回 404 not found。这样假服务器的行为更像真实 HTTP 服务。
数据流:进去没有外部输入 → 它启动服务器,发送 GET /v1/unknown → 读完整响应后断言状态码 404、纯文本类型、正文 not found。
调用关系:它覆盖连接处理逻辑最后的兜底分支,确保只有明确支持的两个路由会被接受。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 6 个(new, assert_eq!, connect, read_to_end, send_request, split_response)。
tests::malformed_request_returns_400578–588 ↗
async fn malformed_request_returns_400()
作用:确认格式不对的 HTTP 请求会返回 400 bad request。它验证服务器能识别坏请求,而不是误当成某个正常路由。
数据流:进去没有外部输入 → 它发送只有 BAD 的不完整请求行 → parse_request_line 解析失败后服务器回 400;测试检查状态码、类型和正文。
调用关系:它覆盖 parse_request_line 返回 None 后的错误路径,并通过 write_http_response 发出错误响应。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 6 个(new, assert_eq!, connect, read_to_end, send_request, split_response)。
tests::responses_post_drains_request_body591–626 ↗
async fn responses_post_drains_request_body()
作用:确认服务器会把 POST 请求体读完,尤其是用真实 reqwest 客户端发送 JSON 时也正常。reqwest 是一个常用 HTTP 客户端库。
数据流:进去没有外部输入 → 它启动服务器,构造一段 JSON 请求,用 reqwest POST 到 /v1/responses → 服务器返回预设 SSE 正文;测试确认状态 200、正文一致、完成时间戳有效。
调用关系:它比手写 TCP 请求更接近真实调用方式,重点验证 read_request_body 和 content_length 能配合真实 HTTP 客户端。
调用图:调用 2 个内部函数(new, start_streaming_sse_server);外部调用 5 个(assert!, assert_eq!, format!, json!, vec!)。
tests::read_http_request_returns_after_header_terminator629–657 ↗
async fn read_http_request_returns_after_header_terminator()
作用:确认 read_http_request 一看到 HTTP 头结束标记就返回,不会等客户端关闭连接。这个细节对长连接和带正文请求很重要。
数据流:进去没有外部输入 → 它临时开一个监听器和客户端连接,客户端只写请求头,服务器端调用 read_http_request → 在很短超时内收到完整头和空正文前缀,说明函数及时返回。
调用关系:它直接测试 read_http_request,而不是通过完整 SSE 服务器间接测试;也验证 header_terminator_index 的实际效果。
调用图:调用 1 个内部函数(read_http_request);外部调用 8 个(from_millis, bind, connect, assert!, assert_eq!, channel, spawn, timeout)。
tests::parse_request_line_handles_valid_and_invalid660–667 ↗
fn parse_request_line_handles_valid_and_invalid()
作用:确认请求行解析函数能区分合法和不合法输入。这样坏请求不会误进正常路由。
数据流:进去没有外部输入 → 它分别传入空字符串、单个 BAD、以及正常的 GET /v1/models HTTP/1.1 → 断言前两者解析失败,正常请求能得到 GET 和 /v1/models。
调用关系:它直接覆盖 parse_request_line 的边界情况;完整服务器的 400 测试则覆盖它在真实连接里的用法。
调用图:外部调用 1 个(assert_eq!)。
tests::take_next_stream_consumes_in_lockstep670–701 ↗
async fn take_next_stream_consumes_in_lockstep()
作用:确认 take_next_stream 会把响应队列和完成通知队列同步向前推进。也就是说,第一条响应配第一个完成通知,第二条响应配第二个完成通知。
数据流:进去没有外部输入 → 它手动构造包含两条响应和两个完成发送器的状态 → 第一次取到 first 并能通过第一个通知发 11,第二次取到 second 并能发 22,第三次取不到任何东西。
调用关系:它直接测试 take_next_stream 这个队列核心函数,避免完整服务器里出现“响应是第一条,完成通知却发给第二条”的隐蔽错误。
调用图:调用 1 个内部函数(take_next_stream);外部调用 6 个(new, from, assert!, assert_eq!, channel, vec!)。
tests::shutdown_terminates_accept_loop704–708 ↗
async fn shutdown_terminates_accept_loop()
作用:确认调用 server.shutdown() 能让后台接受连接的循环及时停下。否则测试进程可能因为后台任务没结束而卡住。
数据流:进去没有外部输入 → 它启动服务器后马上调用 shutdown,并给这个过程一个很短的超时时间 → 如果能在超时前结束,测试通过。
调用关系:它验证 StreamingSseServer::shutdown 和 start_streaming_sse_server 创建的关闭通道、后台任务之间能正确配合。
调用图:调用 1 个内部函数(start_streaming_sse_server);外部调用 4 个(from_millis, new, assert!, timeout)。
框架构建器
这些框架模块构建主要的 Codex 测试夹具,以及用于集成场景的专用可执行文件或基于 shell 的变体。
core/tests/common/zsh_fork.rs源码 ↗
zsh-fork 这类测试不能直接假设每台电脑都装好了同样的 zsh,也不能假设它支持命令拦截。这个文件就像测试开场前的检查员:先找到项目里声明的测试版 zsh,再试一下它能不能通过 EXEC_WRAPPER 拦截要执行的程序;如果不行,就告诉测试跳过。确认能跑以后,它把 Codex 的测试配置改成需要的样子:打开 Shell 工具、打开 zsh-fork 功能、指定 zsh 路径和 execve 包装器路径,并设置权限策略。这里的 execve 包装器可以理解成“门卫”,程序想启动别的程序时会先经过它。文件还提供普通 zsh-fork 测试和 unified exec(统一执行工具)测试两种构建入口,让不同测试不用重复写同一套准备代码。
ZshForkRuntime::apply_to_config22–44 ↗
fn apply_to_config(
&self,
config: &mut Config,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
)
作用:把一份普通测试配置改造成 zsh-fork 测试需要的配置。有人用它,是为了保证测试真的走 zsh-fork 这条执行路径,而不是走默认 shell 路径。
数据流:输入是一份可修改的 Config、审批策略和权限档位,再加上 ZshForkRuntime 里保存的 zsh 路径和包装器路径。它打开 ShellTool 和 ShellZshFork 两个功能开关,填入 zsh 与 execve 包装器的位置,关闭登录 shell,并写入审批与权限设置。出来的结果不是新对象,而是原来的配置被改好了,可以拿去启动 TestCodex。
调用关系:它是两个构建函数里的核心改配置步骤。build_zsh_fork_test 用它准备普通 zsh-fork 测试;build_unified_exec_zsh_fork_test 也先用它打底,然后再额外打开 unified exec 相关开关。它内部会用 allow_any 包装审批策略,并克隆路径,避免把运行时对象里的路径搬走。
restrictive_workspace_write_profile47–54 ↗
fn restrictive_workspace_write_profile() -> PermissionProfile
作用:生成一种比较严格的测试权限档位:允许写工作区,但网络受限,并且不把临时目录当成可随便用的逃生口。测试用它来确认沙箱限制真的生效。
数据流:它没有外部输入。函数把“额外允许路径为空”“网络受限”“排除 TMPDIR 环境变量指向的目录”“排除 /tmp”这些选择交给 workspace_write_with。输出是一份 PermissionProfile,也就是一张权限说明书,后续测试会把它塞进配置里。
调用关系:多个 zsh-fork 相关测试会调用它,例如检查 Python 启动脚本能否请求提权、匹配规则是否能绕过沙箱、技能脚本是否忽略声明权限、工作区写入沙箱是否仍然生效,以及 unified exec 下父进程审批行为是否正确。它本身只造权限档位,不启动测试。
调用图:调用 1 个内部函数(workspace_write_with);被 6 处调用(env_zsh_script_spawned_by_python_can_request_escalation_under_zsh_fork, matched_prefix_rule_runs_unsandboxed_under_zsh_fork, shell_zsh_fork_skill_scripts_ignore_declared_permissions, shell_zsh_fork_still_enforces_workspace_write_sandbox, unified_exec_zsh_fork_parent_approval_escalates_intercepted_exec, unified_exec_zsh_fork_parent_approval_keeps_explicit_prompt_rule)。
zsh_fork_runtime56–77 ↗
fn zsh_fork_runtime(test_name: &str) -> Result<Option<ZshForkRuntime>>
作用:检查当前环境能不能跑 zsh-fork 测试,并在能跑时返回所需的运行材料。不能跑时它返回 None,让测试跳过,而不是报一个误导性的失败。
数据流:输入是测试名字,用来打印跳过原因。它先调用 find_test_zsh_path 找测试用 zsh;找不到就返回 Ok(None)。找到后,它用 supports_exec_wrapper_intercept 验证这个 zsh 是否支持 EXEC_WRAPPER 拦截;不支持也返回 Ok(None)。最后它用 cargo_bin 找到 codex-execve-wrapper 这个二进制程序。全部成功后,输出 Some(ZshForkRuntime),里面装着 zsh 路径和包装器路径。
调用关系:这是很多 zsh-fork 测试的第一道门。具体测试会先调用它拿运行时对象;如果拿不到,就跳过。它把查找 zsh 的活交给 find_test_zsh_path,把能力探测交给 supports_exec_wrapper_intercept,把查找包装器二进制的活交给 cargo_bin,并在失败时用 eprintln! 打印清楚原因。
调用图:调用 2 个内部函数(find_test_zsh_path, supports_exec_wrapper_intercept);被 5 处调用(env_zsh_script_spawned_by_python_can_request_escalation_under_zsh_fork, matched_prefix_rule_runs_unsandboxed_under_zsh_fork, shell_zsh_fork_skill_scripts_ignore_declared_permissions, shell_zsh_fork_still_enforces_workspace_write_sandbox, build_unified_exec_zsh_fork_test_or_skip);外部调用 2 个(cargo_bin, eprintln!)。
build_zsh_fork_test79–95 ↗
async fn build_zsh_fork_test(
server: &wiremock::MockServer,
runtime: ZshForkRuntime,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
pre_build_hook: F,
) -
作用:用已经确认可用的 zsh-fork 运行材料,真正搭出一个 TestCodex 测试实例。测试作者用它可以少写很多重复的配置代码。
数据流:输入包括 mock 服务器、ZshForkRuntime、审批策略、权限档位,以及一个预构建钩子。它先从 test_codex 创建测试构建器,挂上预构建钩子,再在配置阶段调用 runtime.apply_to_config 写入 zsh-fork 所需设置。最后它异步 build,输出一个可运行的 TestCodex。
调用关系:它位于“环境检查完成之后、测试正式运行之前”。多个普通 zsh-fork 测试会调用它。它自己不判断 zsh 是否可用,这一步通常已经由 zsh_fork_runtime 做完;它的重点是把 TestCodex 按正确配置建起来。
调用图:调用 1 个内部函数(test_codex);被 4 处调用(env_zsh_script_spawned_by_python_can_request_escalation_under_zsh_fork, matched_prefix_rule_runs_unsandboxed_under_zsh_fork, shell_zsh_fork_skill_scripts_ignore_declared_permissions, shell_zsh_fork_still_enforces_workspace_write_sandbox)。
build_unified_exec_zsh_fork_test97–122 ↗
async fn build_unified_exec_zsh_fork_test(
server: &wiremock::MockServer,
runtime: ZshForkRuntime,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
pre_build
作用:搭建一种更特殊的 zsh-fork 测试实例:同时启用 unified exec,也就是项目里统一的命令执行工具。它用于验证新执行通道和 zsh-fork 配合时是否正常。
数据流:输入和 build_zsh_fork_test 类似:mock 服务器、运行材料、审批策略、权限档位和预构建钩子。它先创建测试构建器并挂钩子,再调用 runtime.apply_to_config 设置基础 zsh-fork 配置。随后它把 use_experimental_unified_exec_tool 设为 true,并打开 UnifiedExec 与 UnifiedExecZshFork 两个功能开关。最后 build 出 TestCodex。
调用关系:它服务于 build_unified_exec_zsh_fork_test_or_skip 这类上层测试辅助流程。它复用 apply_to_config 做共同部分,然后追加 unified exec 的实验性开关。这样普通 zsh-fork 测试和 unified exec zsh-fork 测试既共享基础设置,又不会混在一起。
调用图:调用 1 个内部函数(test_codex);被 1 处调用(build_unified_exec_zsh_fork_test_or_skip)。
find_test_zsh_path124–142 ↗
fn find_test_zsh_path() -> Result<Option<PathBuf>>
作用:找到测试专用的 zsh 程序路径。它不是随便用系统里的 zsh,而是从项目声明的 DotSlash 文件取,保证测试用的版本更可控。
数据流:它没有显式输入。函数先通过 repo_root 找到仓库根目录,再拼出 codex-rs/app-server/tests/suite/zsh 这个 DotSlash 文件路径。DotSlash 可以理解成一个“小指路牌”,指向实际要下载或缓存的工具。如果这个文件不存在,就打印跳过原因并返回 None;如果存在,就调用 fetch_dotslash_file 获取真实可执行文件路径。成功时输出 Some(PathBuf),失败时打印错误并输出 None。
调用关系:它只被 zsh_fork_runtime 调用,是 zsh-fork 测试准备流程里的第一步。它负责“找到 zsh 在哪里”,但不验证这个 zsh 是否支持拦截;验证工作随后交给 supports_exec_wrapper_intercept。
调用图:被 1 处调用(zsh_fork_runtime);外部调用 3 个(repo_root, fetch_dotslash_file, eprintln!)。
supports_exec_wrapper_intercept144–154 ↗
fn supports_exec_wrapper_intercept(zsh_path: &Path) -> bool
作用:验证某个 zsh 是否真的支持 EXEC_WRAPPER 拦截机制。这个检查很重要,因为没有拦截能力,后面的权限和审批测试就测不到真正想测的东西。
数据流:输入是一个 zsh 可执行文件路径。函数启动这个 zsh,让它执行 /usr/bin/true,同时把环境变量 EXEC_WRAPPER 设置成 /usr/bin/false。正常的 true 会成功,但如果 zsh 支持拦截,它会改走 false,于是命令失败。函数看到“启动成功但状态不是成功”就返回 true;启动失败或命令仍然成功则返回 false。
调用关系:它被 zsh_fork_runtime 调用,用在找到 zsh 之后、构建运行时对象之前。它通过 std::process::Command 启动一个很小的探测试验,相当于先敲一下门卫系统,确认门卫真的会拦人,再让正式测试进场。
调用图:被 1 处调用(zsh_fork_runtime);外部调用 1 个(new)。
core/tests/common/test_codex.rs源码 ↗
真实跑 Codex 测试时,最麻烦的是准备环境:要有家目录、工作区、模型接口、权限设置、文件系统、会话线程,还要能检查模型请求和工具输出。这个文件把这些零散步骤包装成几套测试工具。TestCodexBuilder 像装配清单,让测试按需改配置、换模型、换 shell、接远程执行服务。TestCodex 是已经启动好的会话,能提交一轮用户输入并等到结束。TestCodexHarness 再往外包一层,连 mock 服务器和读写文件的小工具也准备好。它还支持远程测试环境,并在结束时清理远程目录,避免测试互相污染。没有它,每个测试都要重复写大量搭环境代码,而且很容易漏掉权限、目录或会话等待这些细节。
RecordingUserInstructionsProvider::new85–90 ↗
fn new(inner: Arc<dyn UserInstructionsProvider>) -> Self
作用:创建一个会记录读取次数的用户指令提供器。测试用它确认“用户说明文件”到底被加载了几次。
数据流:传入一个真正负责读取用户指令的对象 → 把它包起来,并把计数器设为 0 → 返回一个带计数功能的新提供器。
调用关系:一些检查用户指令加载行为的测试会先调用它;之后真正读取时,会走到 RecordingUserInstructionsProvider::load_user_instructions。
调用图:被 2 处调用(loads_user_instructions_without_a_primary_environment, multi_environment_thread_loads_every_project_and_keeps_creation_snapshot);外部调用 1 个(new)。
RecordingUserInstructionsProvider::load_count92–94 ↗
fn load_count(&self) -> usize
作用:查看用户指令被加载过多少次。测试用它判断加载逻辑有没有多读或少读。
数据流:不需要额外输入 → 从内部原子计数器读取当前数字 → 返回次数,不改变其它状态。
调用关系:通常在测试执行完某些会话操作后调用,用来核对 load_user_instructions 累加出来的结果。
调用图:外部调用 1 个(load)。
RecordingUserInstructionsProvider::load_user_instructions98–101 ↗
fn load_user_instructions(&self) -> LoadUserInstructionsFuture<'_>
作用:在真正读取用户指令前先把次数加一。它像门口的计数器,每有人进去一次就记一笔。
数据流:收到读取用户指令的请求 → 先把内部计数器加 1 → 再把读取工作交给里面包着的真实提供器,并返回它的异步结果。
调用关系:由 Codex 会话加载用户指令时触发;它不自己解析文件,只负责计数后转交给内部提供器。
调用图:外部调用 1 个(fetch_add)。
local104–109 ↗
fn local(cwd: AbsolutePathBuf) -> TurnEnvironmentSelection
作用:生成一个“使用本地环境”的环境选择。测试需要明确告诉某一轮对话在哪个工作目录、哪个执行环境里跑时会用它。
数据流:传入一个绝对路径形式的当前目录 → 转成路径 URI,并配上本地环境 ID → 返回一条环境选择记录。
调用关系:被多环境、远程路由等测试使用;local_selections 也基于它生成完整的环境选择列表。
调用图:调用 1 个内部函数(from_abs_path);被 3 处调用(default_turn_does_not_overlay_legacy_fallback_cwd_onto_stored_thread_environments, exec_command_routes_to_selected_remote_environment, view_image_routes_to_selected_remote_environment)。
local_selections111–113 ↗
fn local_selections(cwd: AbsolutePathBuf) -> TurnEnvironmentSelections
作用:生成只包含本地环境的一组选项。它是测试里最常见的“就用这个本地目录跑”的简写。
数据流:传入当前目录 → 调用 local 做出单个本地环境选择 → 包成 TurnEnvironmentSelections 返回。
调用关系:很多提交用户轮次的测试用它指定环境;它把底层的 local 封装成更方便的一组选择。
调用图:调用 1 个内部函数(new);被 72 处调用(user_turn_updates_approvals_reviewer, env_zsh_script_spawned_by_python_can_request_escalation_under_zsh_fork, matched_prefix_rule_runs_unsandboxed_under_zsh_fork, network_approval_retry_keeps_deny_read_sandbox_for_escalated_command, submit_turn, remote_model_override_uses_catalog_model_for_strict_auto_review, user_turn_collaboration_mode_overrides_model_and_effort, user_turn_explicit_reasoning_summary_overrides_model_catalog_default, collaboration_instructions_added_on_user_turn, collaboration_instructions_omitted_when_disabled (+15 more));外部调用 2 个(clone, vec!)。
TestEnv::local125–137 ↗
async fn local() -> Result<Self>
作用:创建一个纯本地的测试执行环境。没有远程执行服务器时,测试就靠它获得临时目录和本地文件系统。
数据流:不需要输入 → 新建临时工作目录,创建测试用执行环境 → 返回 TestEnv,里面记着目录、环境和清理用信息。
调用关系:普通构建流程、流式服务器测试、WebSocket 测试和恢复测试都会调用它;test_env 在没有远程配置时也退回到它。
调用图:调用 1 个内部函数(create_for_tests);被 5 处调用(build, build_with_streaming_server, build_with_websocket_server, resume, test_env);外部调用 2 个(new, new)。
TestEnv::cwd139–141 ↗
fn cwd(&self) -> &AbsolutePathBuf
作用:拿到这个测试环境的当前工作目录。测试构建配置时需要把 Codex 的工作区指到这里。
数据流:读取 TestEnv 内部保存的路径 → 不做修改 → 返回这个路径的引用。
调用关系:TestCodexBuilder::build_with_home_and_base_url 用它把执行环境目录写入配置。
调用图:被 1 处调用(build_with_home_and_base_url)。
TestEnv::environment143–145 ↗
fn environment(&self) -> &codex_exec_server::Environment
作用:拿到测试用的执行环境对象。这个对象能提供文件系统、执行服务等能力。
数据流:读取 TestEnv 内部的环境字段 → 不改动它 → 返回环境引用。
调用关系:TestCodex::fs 和构建流程会用它取得文件系统,后续文件读写和工作区准备都依赖它。
调用图:被 2 处调用(fs, build_with_home_and_base_url)。
TestEnv::local_cwd_temp_dir147–149 ↗
fn local_cwd_temp_dir(&self) -> Option<Arc<TempDir>>
作用:如果这是本地测试环境,就取出本地临时目录的持有者。这样目录在测试期间不会被提前删除。
数据流:查看内部是否有本地临时目录 → 有就克隆一份引用计数指针 → 返回可选的临时目录对象。
调用关系:构建 Codex 时调用;如果是远程环境,就会使用备用临时目录来维持生命周期。
调用图:被 1 处调用(build_with_home_and_base_url)。
TestEnv::drop153–158 ↗
fn drop(&mut self)
作用:在测试环境被丢弃时清理远程容器里的工作目录。它防止远程测试留下垃圾文件,影响后面的测试。
数据流:检查是否记录了远程 Docker 容器名 → 有的话拼出删除工作目录的 shell 命令 → 调用 Docker 执行,失败也不阻断析构。
调用关系:由 Rust 在 TestEnv 生命周期结束时自动调用;实际跑 Docker 命令的工作交给 docker_command_capture_stdout。
调用图:调用 1 个内部函数(docker_command_capture_stdout);外部调用 1 个(format!)。
test_env161–190 ↗
async fn test_env() -> Result<TestEnv>
作用:按当前测试配置选择本地还是远程执行环境。它让同一套测试可以在本机跑,也可以连到远程执行服务器跑。
数据流:读取远程测试环境配置 → 如果有远程环境,就取服务器地址、创建远程目录并返回远程 TestEnv;如果没有,就返回本地 TestEnv。
调用关系:远程执行相关测试和 builder 的远程构建入口会调用它;它内部会用 remote_exec_server_url、remote_test_instance_id,必要时退到 TestEnv::local。
调用图:调用 4 个内部函数(local, remote_exec_server_url, remote_test_instance_id, create_for_tests);被 9 处调用(remote_exec_server_rejects_inherited_fd_launches, unified_exec_uses_remote_exec_server_when_configured, build_with_remote_and_local_env, build_with_remote_env, remote_test_env_can_connect_and_use_filesystem, remote_test_env_copy_preserves_symlink_source, remote_test_env_remove_removes_symlink_not_target, remote_test_env_sandboxed_read_allows_readable_root, remote_test_env_sandboxed_read_rejects_symlink_parent_dotdot_escape);外部调用 1 个(get_remote_test_env)。
remote_exec_server_url192–203 ↗
remote_test_instance_id205–208 ↗
docker_command_capture_stdout210–224 ↗
fn docker_command_capture_stdout(args: [&str; N]) -> Result<String>
作用:运行一条 Docker 命令并拿回标准输出。这里主要用于测试结束时清理远程容器里的目录。
数据流:传入 Docker 参数数组 → 启动 docker 命令 → 成功就把 stdout 转成 UTF-8 字符串返回,失败就把 stdout/stderr 放进错误信息。
调用关系:TestEnv::drop 调用它执行清理命令;它把系统命令调用的细节集中在一个地方。
turn_permission_fields240–248 ↗
fn turn_permission_fields(
permission_profile: PermissionProfile,
cwd: &Path,
) -> (SandboxPolicy, Option<PermissionProfile>)
作用:把新的权限配置转换成提交一轮对话时需要的旧沙盒字段。沙盒就是限制程序能读写什么的安全圈。
数据流:传入权限档案和当前目录 → 尝试转成旧格式的 SandboxPolicy,失败就用只读策略兜底 → 返回沙盒策略和原权限档案。
调用关系:TestCodex::submit_turn_with_context 和许多权限测试都会用它,确保测试提交时新旧权限字段都填好。
调用图:调用 1 个内部函数(to_legacy_sandbox_policy);被 63 处调用(submit_turn_with_context, apply_patch_turn_diff_tracks_local_and_remote_environment_paths, env_zsh_script_spawned_by_python_can_request_escalation_under_zsh_fork, matched_prefix_rule_runs_unsandboxed_under_zsh_fork, network_approval_retry_keeps_deny_read_sandbox_for_escalated_command, remote_model_override_uses_catalog_model_for_strict_auto_review, code_mode_can_call_hidden_dynamic_tools, disabled_permission_user_turn, execpolicy_blocks_shell_invocation, submit_user_turn (+15 more))。
TestCodexBuilder::with_config264–270 ↗
fn with_config(mut self, mutator: T) -> Self
作用:给测试配置追加一个修改步骤。测试可以用它在真正启动 Codex 前改任意配置项。
数据流:传入一个会修改 Config 的闭包 → 存进 builder 的修改列表 → 返回更新后的 builder,方便继续链式调用。
调用关系:with_model、with_model_info_override 和很多测试配置入口都靠它挂载配置改动;这些改动稍后在 prepare_config 里统一执行。
调用图:被 2 处调用(with_model, with_model_info_override);外部调用 1 个(new)。
TestCodexBuilder::with_auth272–275 ↗
fn with_auth(mut self, auth: CodexAuth) -> Self
作用:指定测试用的登录身份。测试要模拟不同认证状态时会用它。
数据流:传入一个 CodexAuth → 替换 builder 里默认的认证信息 → 返回 builder。
调用关系:构建会话时,build_from_config 会取出这里设置的认证信息交给线程管理器。
TestCodexBuilder::with_model277–282 ↗
fn with_model(self, model: &str) -> Self
作用:指定测试要使用的模型名字。它是改 config.model 的快捷方式。
数据流:传入模型名 → 生成一个配置修改器,把模型名写入配置 → 返回 builder。
调用关系:内部调用 with_config;真正生效是在 prepare_config 执行所有配置修改器时。
调用图:调用 1 个内部函数(with_config)。
TestCodexBuilder::with_model_info_override284–301 ↗
fn with_model_info_override(self, model: &str, override_model_info: T) -> Self
作用:指定模型并顺手改这个模型的详细资料。测试要模拟某个模型能力变化时会用它。
数据流:传入模型名和修改模型信息的闭包 → 在配置的模型目录里找到该模型并修改 → 同时把当前模型设成它。
调用关系:通过 with_config 延后执行;prepare_config 会运行这个修改器,之后 ensure_test_model_catalog 可能再补齐测试模型目录。
调用图:调用 1 个内部函数(with_config)。
TestCodexBuilder::with_pre_build_hook303–309 ↗
fn with_pre_build_hook(mut self, hook: F) -> Self
作用:添加一个构建前钩子。钩子就是启动前先运行的一小段自定义代码,常用来往临时 home 里放文件。
数据流:传入一个接收 home 路径的函数 → 存入钩子列表 → 返回 builder。
调用关系:prepare_config 在加载配置前会逐个执行这些钩子,让测试能提前布置家目录内容。
调用图:外部调用 1 个(new)。
TestCodexBuilder::with_workspace_setup311–319 ↗
fn with_workspace_setup(mut self, setup: F) -> Self
作用:添加一个工作区准备步骤。测试可以用它在 Codex 启动前通过执行环境文件系统创建文件或目录。
数据流:传入一个异步准备函数 → 包装成统一的 boxed future 存起来 → 返回 builder。
调用关系:build_with_home_and_base_url 会在配置准备好后执行这些步骤,文件系统来自 TestEnv::environment。
调用图:外部调用 1 个(new)。
TestCodexBuilder::with_home321–324 ↗
fn with_home(mut self, home: Arc<TempDir>) -> Self
作用:指定测试使用的 Codex home 目录。home 类似应用的个人配置目录。
数据流:传入一个临时目录引用 → 存进 builder → 返回 builder。
调用关系:各个 build... 方法会优先使用这里提供的 home;没有设置才新建临时目录。
TestCodexBuilder::with_cloud_config_bundle326–332 ↗
fn with_cloud_config_bundle(
mut self,
cloud_config_bundle: CloudConfigBundleLoader,
) -> Self
作用:给测试指定一份云端配置包。这样可以测试云配置如何影响本地默认配置。
数据流:传入云配置加载器 → 保存到 builder → 返回 builder。
调用关系:prepare_config 看到它后会调用带云配置包的加载函数,而不是普通默认配置加载函数。
TestCodexBuilder::with_user_shell334–337 ↗
fn with_user_shell(mut self, user_shell: Shell) -> Self
作用:指定测试会话使用的用户 shell。shell 可以理解为运行命令的外壳,比如 bash、zsh 或 cmd。
数据流:传入 shell 描述 → 存成用户 shell 覆盖值 → 返回 builder。
调用关系:with_windows_cmd_shell 会调用它;build_from_config 根据这个值决定启动或恢复线程时是否使用 shell 覆盖。
调用图:被 1 处调用(with_windows_cmd_shell)。
TestCodexBuilder::with_exec_server_url339–342 ↗
fn with_exec_server_url(mut self, exec_server_url: impl Into<String>) -> Self
作用:手动指定执行服务器地址。测试要连某个特定执行服务时会用它。
数据流:传入可转成字符串的地址 → 转成 String 并保存 → 返回 builder。
调用关系:build_with_home_and_base_url 会优先使用这里的地址;没有设置才使用 TestEnv 自带的远程地址。
调用图:外部调用 1 个(into)。
TestCodexBuilder::with_extensions344–347 ↗
fn with_extensions(mut self, extensions: Arc<ExtensionRegistry<Config>>) -> Self
作用:指定测试要加载的扩展注册表。扩展可以给 Codex 增加额外工具或能力。
数据流:传入扩展注册表 → 替换 builder 中的默认空注册表 → 返回 builder。
调用关系:build_from_config 创建 ThreadManager 时会把它传进去,让会话启动时能看到这些扩展。
TestCodexBuilder::with_user_instructions_provider349–355 ↗
fn with_user_instructions_provider(
mut self,
provider: Arc<dyn UserInstructionsProvider>,
) -> Self
作用:指定用户指令的来源。测试可以换成自定义提供器,观察或控制指令加载。
数据流:传入用户指令提供器 → 保存到 builder → 返回 builder。
调用关系:build_from_config 会优先使用它;没有设置时才使用从 Codex home 读取指令的默认提供器。
TestCodexBuilder::with_windows_cmd_shell357–363 ↗
fn with_windows_cmd_shell(self) -> Self
作用:在 Windows 上把 shell 改成 cmd.exe。这让测试能覆盖 Windows 命令行的特殊行为。
数据流:检查当前系统 → 如果是 Windows,就根据 cmd.exe 路径生成 shell 并调用 with_user_shell;否则原样返回 builder。
调用关系:它是 with_user_shell 的平台快捷入口;只有 Windows 测试真正改变配置。
调用图:调用 2 个内部函数(get_shell_by_model_provided_path, with_user_shell);外部调用 2 个(from, cfg!)。
TestCodexBuilder::build365–377 ↗
async fn build(&mut self, server: &wiremock::MockServer) -> anyhow::Result<TestCodex>
作用:用普通 mock 服务器和本地环境启动一个测试 Codex。多数核心测试会用这个入口。
数据流:传入 mock 服务器 → 准备 home、拼出 /v1 基础地址、创建本地 TestEnv → 调用 build_with_home_and_base_url 返回 TestCodex。
调用关系:TestCodexHarness::with_builder 会调用它;它把常见本地构建流程收口到一个简单入口。
调用图:调用 2 个内部函数(build_with_home_and_base_url, local);被 1 处调用(with_builder);外部调用 4 个(new, pin, new, format!)。
TestCodexBuilder::build_with_remote_env379–394 ↗
async fn build_with_remote_env(
&mut self,
server: &wiremock::MockServer,
) -> anyhow::Result<TestCodex>
作用:用 mock 模型服务器和可能的远程执行环境启动测试 Codex。适合测试远程执行路径。
数据流:准备 home 和模型接口地址 → 调用 test_env 决定本地或远程环境 → 交给 build_with_home_and_base_url 完成构建。
调用关系:TestCodexHarness::with_remote_env_builder 和远程环境相关测试会调用它。
调用图:调用 2 个内部函数(build_with_home_and_base_url, test_env);被 2 处调用(with_remote_env_builder, agents_instructions);外部调用 4 个(new, pin, new, format!)。
TestCodexBuilder::build_with_remote_and_local_env396–411 ↗
async fn build_with_remote_and_local_env(
&mut self,
server: &wiremock::MockServer,
) -> anyhow::Result<TestCodex>
作用:启动一个同时包含远程和本地环境的测试 Codex。它用于检查多环境选择和路由。
数据流:准备 home、模型地址和 test_env → 调用 build_with_home_and_base_url,并要求把本地环境也加入管理器 → 返回测试会话。
调用关系:多环境测试使用它;核心构建仍交给 build_with_home_and_base_url。
调用图:调用 2 个内部函数(build_with_home_and_base_url, test_env);外部调用 4 个(new, pin, new, format!)。
TestCodexBuilder::build_with_streaming_server413–431 ↗
async fn build_with_streaming_server(
&mut self,
server: &StreamingSseServer,
) -> anyhow::Result<TestCodex>
作用:用流式 SSE 测试服务器启动 Codex。SSE 是服务器持续推送文本事件的一种 HTTP 通道。
数据流:读取流式服务器地址 → 准备 home 和本地环境 → 拼出 /v1 地址并调用通用构建函数 → 返回 TestCodex。
调用关系:需要验证流式响应的测试会调用它;后续步骤和普通本地构建共用 build_with_home_and_base_url。
调用图:调用 3 个内部函数(uri, build_with_home_and_base_url, local);外部调用 4 个(new, pin, new, format!)。
TestCodexBuilder::build_with_websocket_server433–455 ↗
async fn build_with_websocket_server(
&mut self,
server: &WebSocketTestServer,
) -> anyhow::Result<TestCodex>
作用:用 WebSocket 测试服务器启动 Codex。WebSocket 是客户端和服务器可以双向持续通信的连接。
数据流:准备基础地址 → 添加配置修改器,打开 WebSocket 支持并设置实时模型 → 创建本地环境 → 调用通用构建函数。
调用关系:实时会话相关测试会用它;它先改配置,再走 build_with_home_and_base_url。
调用图:调用 2 个内部函数(build_with_home_and_base_url, local);外部调用 5 个(new, new, pin, new, format!)。
TestCodexBuilder::resume457–473 ↗
async fn resume(
&mut self,
server: &wiremock::MockServer,
home: Arc<TempDir>,
rollout_path: PathBuf,
) -> anyhow::Result<TestCodex>
作用:从已有 rollout 文件恢复一个测试 Codex 会话。rollout 可以理解为保存下来的会话历史。
数据流:传入服务器、home 和历史文件路径 → 准备本地环境和基础地址 → 调用通用构建函数,并带上恢复路径。
调用关系:恢复会话测试会调用它;真正恢复逻辑在 build_from_config 里根据 resume_from 分支完成。
调用图:调用 2 个内部函数(build_with_home_and_base_url, local);被 1 处调用(resume_until_initial_messages);外部调用 2 个(pin, format!)。
TestCodexBuilder::build_with_home_and_base_url475–530 ↗
async fn build_with_home_and_base_url(
&mut self,
base_url: String,
home: Arc<TempDir>,
resume_from: Option<PathBuf>,
test_env: TestEnv,
include_local_e
作用:这是 builder 的核心装配线,把配置、执行环境、工作区准备和线程创建串起来。
数据流:传入模型接口地址、home、可选恢复路径、测试环境和是否包含本地环境 → 准备配置、创建环境管理器、执行工作区 setup → 调用 build_from_config 生成 TestCodex。
调用关系:所有 build... 和 resume 入口最终都来到这里;它向下调用 prepare_config、执行 workspace setup,再交给 build_from_config。
调用图:调用 8 个内部函数(build_from_config, prepare_config, cwd, environment, local_cwd_temp_dir, create_for_tests, create_for_tests_with_local, new);被 6 处调用(build, build_with_remote_and_local_env, build_with_remote_env, build_with_streaming_server, build_with_websocket_server, resume);外部调用 7 个(clone, new, pin, find_codex_linux_sandbox_exe, current_exe, swap, vec!)。
TestCodexBuilder::build_from_config532–613 ↗
async fn build_from_config(
&mut self,
config: Config,
cwd: Arc<TempDir>,
home: Arc<TempDir>,
resume_from: Option<PathBuf>,
test_env: TestEnv,
e
作用:用已经准备好的配置真正启动或恢复 Codex 线程。这里把测试配置变成一个可交互的会话。
数据流:传入配置、目录、home、恢复路径、测试环境和环境管理器 → 初始化状态库、线程存储、安装 ID、用户指令来源和线程管理器 → 根据是否恢复、是否覆盖 shell 启动对应线程 → 返回 TestCodex。
调用关系:只由 build_with_home_and_base_url 调用;它把底层 ThreadManager 的启动/恢复能力包装成测试对象。
调用图:调用 4 个内部函数(auth_manager_from_auth, resume_thread_from_rollout_with_user_shell_override, start_thread_with_user_shell_override, new);被 1 处调用(build_with_home_and_base_url);外部调用 8 个(clone, new, pin, clone, init_state_db, resolve_installation_id, thread_store_from_config, clone)。
TestCodexBuilder::prepare_config615–665 ↗
async fn prepare_config(
&mut self,
base_url: String,
home: &TempDir,
cwd_override: AbsolutePathBuf,
) -> anyhow::Result<(Config, Arc<TempDir>)>
作用:准备测试用配置。它把真实配置加载和测试特有的模型地址、工作目录、二进制路径等补丁合在一起。
数据流:传入基础 URL、home 和工作目录覆盖值 → 执行构建前钩子,加载默认或云配置,设置模型提供商、cwd、自身可执行文件,再运行所有配置修改器 → 补齐测试模型目录并返回配置和备用临时目录。
调用关系:build_with_home_and_base_url 首先调用它;它最后会调用 ensure_test_model_catalog 处理特殊测试模型。
调用图:调用 1 个内部函数(ensure_test_model_catalog);被 1 处调用(build_with_home_and_base_url);外部调用 10 个(new, new, path, built_in_model_providers, cargo_bin, load_default_config_for_test, load_default_config_for_test_with_cloud_config_bundle, current_exe, swap, vec!)。
ensure_test_model_catalog668–689 ↗
fn ensure_test_model_catalog(config: &mut Config) -> Result<()>
作用:为一个特殊测试模型补一份模型目录。这个测试模型需要声明实验性工具能力,普通目录里不一定有。
数据流:检查配置当前模型是否是特殊测试模型且还没有模型目录 → 从内置模型里复制 gpt-5.2,改名并加入实验工具 → 写回配置。
调用关系:prepare_config 调用它;它只在特定测试模型缺目录时动手,其他情况直接返回。
调用图:被 1 处调用(prepare_config);外部调用 2 个(bundled_models_response, vec!)。
TestCodex::cwd_path702–704 ↗
fn cwd_path(&self) -> &Path
作用:返回测试工作目录的普通路径。测试要读写本地文件或拼路径时会用它。
数据流:读取 cwd 临时目录 → 返回它的路径引用 → 不改变状态。
调用关系:workspace_path 和一些直接检查文件的测试会调用它。
调用图:被 5 处调用(workspace_path, read_only_user_turn, read_only_text_turn_with_personality, disabled_text_turn, submit_turn_with_policies)。
TestCodex::codex_home_path706–708 ↗
fn codex_home_path(&self) -> &Path
作用:返回 Codex home 目录路径。测试要检查配置文件、历史文件或指令文件时会用它。
数据流:从配置里读取 codex_home → 返回路径引用 → 不修改配置。
调用关系:种子历史、技能脚本等测试会调用它定位 Codex 的个人数据目录。
调用图:被 2 处调用(seed_recent_thread, skill_script_command)。
TestCodex::workspace_path710–712 ↗
fn workspace_path(&self, rel: impl AsRef<Path>) -> PathBuf
作用:把相对路径拼到测试工作区下面。它避免测试手动处理目录拼接。
数据流:传入相对路径 → 调用 cwd_path 得到工作区根目录 → 拼成完整路径返回。
调用关系:需要在工作区里定位文件的测试会用它;它依赖 cwd_path 提供根目录。
调用图:调用 1 个内部函数(cwd_path);被 1 处调用(seed_recent_thread)。
TestCodex::executor_environment714–716 ↗
fn executor_environment(&self) -> &TestEnv
作用:返回底层测试执行环境。测试如果需要更底层的环境信息,可以从这里拿。
数据流:读取内部保存的 TestEnv → 返回引用 → 不改变任何东西。
调用关系:它是访问 _test_env 的安全入口,供需要查看执行环境细节的测试使用。
TestCodex::fs718–720 ↗
fn fs(&self) -> Arc<dyn ExecutorFileSystem>
作用:拿到当前测试环境的文件系统接口。这个接口可以读、写、建目录、删文件,不管背后是本地还是远程。
数据流:通过 TestEnv::environment 取得环境 → 从环境里取文件系统对象 → 返回共享指针。
调用关系:TestCodexHarness 的文件读写方法和一些工作区辅助函数都会调用它。
调用图:调用 1 个内部函数(environment);被 8 处调用(abs_path_exists, create_dir_all, read_file_text, remove_abs_path, write_file, create_workspace_directory, create_workspace_directory, write_workspace_file)。
TestCodex::submit_turn722–725 ↗
async fn submit_turn(&self, prompt: &str) -> Result<()>
作用:提交一轮最普通的用户输入。默认关闭额外权限,适合大多数对话测试。
数据流:传入提示词 → 使用 PermissionProfile::Disabled → 调用 submit_turn_with_permission_profile 并等待完成。
调用关系:TestCodexHarness::submit 会调用它;它是更复杂提交函数的最简单入口。
调用图:调用 1 个内部函数(submit_turn_with_permission_profile);被 1 处调用(submit)。
TestCodex::submit_turn_with_permission_profile727–738 ↗
async fn submit_turn_with_permission_profile(
&self,
prompt: &str,
permission_profile: PermissionProfile,
) -> Result<()>
作用:提交一轮用户输入,并指定权限档案。权限档案描述这一轮能不能读写文件、能不能联网等。
数据流:传入提示词和权限档案 → 默认审批策略设为永不询问 → 调用 submit_turn_with_approval_and_permission_profile。
调用关系:普通提交和 harness 的权限提交会走这里;再往下进入带审批参数的版本。
调用图:调用 1 个内部函数(submit_turn_with_approval_and_permission_profile);被 2 处调用(submit_turn, submit_with_permission_profile)。
TestCodex::submit_turn_with_policy740–747 ↗
async fn submit_turn_with_policy(
&self,
prompt: &str,
sandbox_policy: SandboxPolicy,
) -> Result<()>
作用:按旧沙盒策略提交一轮用户输入。它方便仍用旧权限表达方式的测试。
数据流:传入提示词和沙盒策略 → 默认审批策略为永不询问 → 调用 submit_turn_with_policies。
调用关系:TestCodexHarness::submit_with_policy 调用它;它随后把旧策略转成新权限档案。
调用图:调用 1 个内部函数(submit_turn_with_policies);被 1 处调用(submit_with_policy)。
TestCodex::submit_turn_with_service_tier749–762 ↗
async fn submit_turn_with_service_tier(
&self,
prompt: &str,
service_tier: Option<&str>,
) -> Result<()>
作用:提交一轮输入,并指定服务档位。服务档位可以理解为请求模型服务时选择的服务等级。
数据流:传入提示词和可选档位字符串 → 使用默认审批和禁用权限 → 调用 submit_turn_with_permission_profile_context。
调用关系:服务档位相关测试会用它;底层仍进入通用的 submit_turn_with_context。
调用图:调用 1 个内部函数(submit_turn_with_permission_profile_context)。
TestCodex::submit_turn_with_policies764–782 ↗
async fn submit_turn_with_policies(
&self,
prompt: &str,
approval_policy: AskForApproval,
sandbox_policy: SandboxPolicy,
) -> Result<()>
作用:用审批策略和旧沙盒策略提交一轮输入。它负责把旧沙盒策略补成新权限格式。
数据流:传入提示词、审批策略和沙盒策略 → 根据当前工作目录转成权限档案 → 调用 submit_turn_with_context。
调用关系:submit_turn_with_policy 调用它;它是旧沙盒接口通往统一提交逻辑的桥。
调用图:调用 2 个内部函数(submit_turn_with_context, from_legacy_sandbox_policy_for_cwd);被 1 处调用(submit_turn_with_policy)。
TestCodex::submit_turn_with_approval_and_permission_profile784–798 ↗
async fn submit_turn_with_approval_and_permission_profile(
&self,
prompt: &str,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
) -> Result<
作用:提交一轮输入,同时指定审批规则和权限档案。审批规则决定遇到危险操作时要不要问用户。
数据流:传入提示词、审批策略和权限档案 → 不指定服务档位和环境列表 → 调用 submit_turn_with_permission_profile_context。
调用关系:submit_turn_with_permission_profile 和一些工具轮测试会调用它;它继续把参数交给统一上下文提交。
调用图:调用 1 个内部函数(submit_turn_with_permission_profile_context);被 2 处调用(submit_turn_with_permission_profile, run_extract_turn)。
TestCodex::submit_turn_with_environments800–813 ↗
async fn submit_turn_with_environments(
&self,
prompt: &str,
environments: Option<Vec<TurnEnvironmentSelection>>,
) -> Result<()>
作用:提交一轮输入,并指定可用执行环境。它用于测试命令或文件操作是否路由到正确环境。
数据流:传入提示词和可选环境列表 → 使用默认审批和禁用权限 → 调用 submit_turn_with_permission_profile_context。
调用关系:环境路由输出测试会调用它;最终由 submit_turn_with_context 把环境选择写进线程设置。
调用图:调用 1 个内部函数(submit_turn_with_permission_profile_context);被 1 处调用(exec_command_routing_output)。
TestCodex::submit_turn_with_permission_profile_context815–831 ↗
async fn submit_turn_with_permission_profile_context(
&self,
prompt: &str,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
service_tier:
作用:把权限、服务档位、环境这些上下文参数集中转交给真正提交函数。它本身主要是参数整理层。
数据流:接收提示词、审批、权限、服务档位和环境列表 → 原样传给 submit_turn_with_context → 返回提交结果。
调用关系:多个更友好的提交入口都会调用它;真正构造协议消息和等待事件在下一层完成。
调用图:调用 1 个内部函数(submit_turn_with_context);被 3 处调用(submit_turn_with_approval_and_permission_profile, submit_turn_with_environments, submit_turn_with_service_tier)。
TestCodex::submit_turn_with_context833–890 ↗
async fn submit_turn_with_context(
&self,
prompt: &str,
approval_policy: AskForApproval,
permission_profile: PermissionProfile,
service_tier: Option<Option<Stri
作用:真正向 Codex 线程提交用户输入,并等待这一轮完成。它是所有提交辅助函数的最终落点。
数据流:传入提示词、审批策略、权限档案、服务档位和环境列表 → 转换权限字段,构造 Op::UserInput 消息并发给线程 → 等到 TurnStarted 记下轮次 ID,再等对应 TurnComplete,最后返回成功。
调用关系:submit_turn_with_permission_profile_context 和 submit_turn_with_policies 调用它;它依赖 turn_permission_fields 和事件等待工具确保测试不会在一轮没结束时继续往下跑。
调用图:调用 1 个内部函数(turn_permission_fields);被 2 处调用(submit_turn_with_permission_profile_context, submit_turn_with_policies);外部调用 4 个(default, wait_for_event_match, wait_for_event_with_timeout, vec!)。
TestCodexHarness::new899–901 ↗
async fn new() -> Result<Self>
作用:创建一个默认测试工具包。它同时准备 mock 服务器和默认 Codex 会话。
数据流:不需要输入 → 调用 test_codex 得到默认 builder → 交给 with_builder 构建完整 harness。
调用关系:测试想快速拿到一套默认环境时用它;实际搭建工作由 with_builder 做。
调用图:调用 1 个内部函数(test_codex);外部调用 1 个(with_builder)。
TestCodexHarness::with_config903–905 ↗
async fn with_config(mutator: impl FnOnce(&mut Config) + Send + 'static) -> Result<Self>
作用:用一段配置修改创建 harness。测试可以用它快速改一个配置项后启动环境。
数据流:传入配置修改器 → 从 test_codex 创建默认 builder 并加上修改器 → 调用 with_builder。
调用关系:它是 test_codex().with_config(...).build... 的快捷写法;真正启动仍交给 with_builder。
调用图:调用 1 个内部函数(test_codex);外部调用 1 个(with_builder)。
TestCodexHarness::with_builder907–911 ↗
async fn with_builder(mut builder: TestCodexBuilder) -> Result<Self>
作用:用给定 builder 创建完整 harness。它负责启动 mock 模型服务器,再启动测试 Codex。
数据流:传入 builder → 启动 mock server → 调用 builder 的 build → 返回包含 server 和 test 的 harness。
调用关系:很多工具、压缩、远程 compact 等测试会调用它;它连接了 mock 服务器和 TestCodexBuilder::build。
调用图:调用 2 个内部函数(start_mock_server, build);被 33 处调用(assert_remote_manual_compact_request_parity, auto_remote_compact_failure_stops_agent_loop, auto_remote_compact_trims_function_call_history_to_fit_context_window, remote_compact_persists_replacement_history_in_rollout, remote_compact_replaces_history_for_followups, remote_compact_rewrites_multiple_trailing_function_call_outputs, remote_compact_runs_automatically, remote_compact_trim_estimate_uses_session_base_instructions, remote_compact_trims_function_call_history_to_fit_context_window, remote_compact_v2_accepts_additional_output_items_before_compaction (+15 more))。
TestCodexHarness::with_remote_env_builder913–917 ↗
async fn with_remote_env_builder(mut builder: TestCodexBuilder) -> Result<Self>
作用:用给定 builder 创建带远程环境能力的 harness。适合测试远程执行或 apply_patch 这类场景。
数据流:传入 builder → 启动 mock server → 调用 builder 的 build_with_remote_env → 返回 harness。
调用关系:远程环境版 apply_patch harness 会调用它;它和 with_builder 类似,但走远程环境构建入口。
调用图:调用 2 个内部函数(start_mock_server, build_with_remote_env);被 1 处调用(apply_patch_harness_with)。
TestCodexHarness::server919–921 ↗
fn server(&self) -> &MockServer
作用:返回 mock 模型服务器。测试可以往这个服务器挂载预期响应,或查看收到的请求。
数据流:读取 harness 内部的 server → 返回引用 → 不改变状态。
调用关系:挂载 apply_patch、shell 响应、compact 响应等测试辅助函数会调用它。
调用图:被 6 处调用(mount_apply_patch, mount_apply_patch_model_output, mount_legacy_compact_if_needed, mount_shell_responses, mount_shell_responses_with_timeout, run_tool_turn_on_harness)。
TestCodexHarness::test923–925 ↗
fn test(&self) -> &TestCodex
作用:返回内部的 TestCodex 会话对象。需要更底层操作时,测试可以绕过 harness 的简化方法。
数据流:读取 harness 内部的 test → 返回引用 → 不改变状态。
调用关系:提交但不等待、读取 rollout 路径、运行工具轮等辅助流程会调用它。
调用图:被 3 处调用(submit_without_wait_with_turn_permissions, rollout_path, run_tool_turn_on_harness)。
TestCodexHarness::cwd927–929 ↗
fn cwd(&self) -> &Path
作用:返回当前测试工作目录路径。它是定位工作区文件的简单入口。
数据流:从内部配置读取 cwd → 转成路径引用返回 → 不修改状态。
调用关系:测试可直接用它查看当前工作区;更常见的相对路径拼接由 path 和 path_abs 完成。
TestCodexHarness::cwd_abs931–933 ↗
fn cwd_abs(&self) -> AbsolutePathBuf
作用:返回当前工作目录的绝对路径对象。需要类型安全的绝对路径时会用它。
数据流:克隆配置里的绝对 cwd → 返回这个路径对象 → 原配置不变。
调用关系:它为外部测试提供工作区根路径;后续可配合文件系统接口使用。
TestCodexHarness::path935–937 ↗
fn path(&self, rel: impl AsRef<Path>) -> PathBuf
作用:把相对路径拼到工作区下,并返回普通 PathBuf。测试写断言时用起来更方便。
数据流:传入相对路径 → 调用 path_abs 得到绝对路径 → 转成 PathBuf 返回。
调用关系:它是 path_abs 的普通路径版本;适合不需要 AbsolutePathBuf 的测试。
调用图:调用 1 个内部函数(path_abs)。
TestCodexHarness::path_abs939–941 ↗
fn path_abs(&self, rel: impl AsRef<Path>) -> AbsolutePathBuf
作用:把相对路径拼到工作区下,并保留绝对路径类型。它减少了路径拼错的机会。
数据流:传入相对路径 → 与配置里的 cwd 拼接 → 返回绝对路径对象。
调用关系:write_file、read_file_text、create_dir_all、path_exists 和 path 都依赖它定位文件。
调用图:被 5 处调用(create_dir_all, path, path_exists, read_file_text, write_file)。
TestCodexHarness::write_file943–970 ↗
async fn write_file(
&self,
rel: impl AsRef<Path>,
contents: impl AsRef<[u8]>,
) -> Result<()>
作用:在测试工作区写入一个文件。它会自动创建父目录,所以测试不用先手动 mkdir。
数据流:传入相对路径和内容 → 拼成绝对路径,必要时通过测试文件系统创建父目录 → 写入文件内容 → 返回成功或错误。
调用关系:测试准备文件夹和输入文件时调用;它通过 path_abs 定位,并通过 TestCodex::fs 操作本地或远程文件系统。
TestCodexHarness::read_file_text972–980 ↗
TestCodexHarness::create_dir_all982–994 ↗
TestCodexHarness::path_exists996–998 ↗
async fn path_exists(&self, rel: impl AsRef<Path>) -> Result<bool>
作用:检查工作区里的相对路径是否存在。测试用它做存在性断言。
数据流:传入相对路径 → 用 path_abs 转成绝对路径 → 调用 abs_path_exists → 返回布尔值。
调用关系:它是 abs_path_exists 的相对路径便利版。
调用图:调用 2 个内部函数(abs_path_exists, path_abs)。
TestCodexHarness::remove_abs_path1000–1014 ↗
async fn remove_abs_path(&self, path: &AbsolutePathBuf) -> Result<()>
作用:删除一个指定的绝对路径。这里使用强制删除,路径不存在也不会像普通删除那样麻烦。
数据流:传入绝对路径 → 转成路径 URI → 通过文件系统执行非递归、强制删除 → 返回结果。
调用关系:需要清理某个文件的测试会调用它;底层文件系统来自 TestCodex::fs。
调用图:调用 2 个内部函数(fs, from_abs_path)。
TestCodexHarness::abs_path_exists1016–1028 ↗
async fn abs_path_exists(&self, path: &AbsolutePathBuf) -> Result<bool>
作用:检查一个绝对路径是否存在。它把“找不到”错误转换成 false,其它错误仍然报告出来。
数据流:传入绝对路径 → 查询文件元数据 → 成功返回 true,NotFound 返回 false,其它错误转成失败。
调用关系:path_exists 会调用它;它通过 TestCodex::fs 兼容本地和远程文件系统。
调用图:调用 2 个内部函数(fs, from_abs_path);被 1 处调用(path_exists)。
TestCodexHarness::submit1030–1034 ↗
async fn submit(&self, prompt: &str) -> Result<()>
作用:向内部 Codex 会话提交一轮普通提示词,并等待完成。它是 harness 的最常用对话入口。
数据流:传入提示词 → 调用 TestCodex::submit_turn 并装箱异步任务 → 返回提交和等待的结果。
调用关系:测试通常通过它驱动一轮模型交互;真正构造协议和等待事件由 TestCodex 完成。
调用图:调用 1 个内部函数(submit_turn);外部调用 1 个(pin)。
TestCodexHarness::submit_with_policy1036–1044 ↗
async fn submit_with_policy(
&self,
prompt: &str,
sandbox_policy: SandboxPolicy,
) -> Result<()>
作用:按指定沙盒策略提交一轮提示词。测试权限行为时会用它。
数据流:传入提示词和沙盒策略 → 调用 TestCodex::submit_turn_with_policy → 返回结果。
调用关系:它是 harness 对 TestCodex 权限提交能力的简化转发。
调用图:调用 1 个内部函数(submit_turn_with_policy)。
TestCodexHarness::submit_with_permission_profile1046–1054 ↗
async fn submit_with_permission_profile(
&self,
prompt: &str,
permission_profile: PermissionProfile,
) -> Result<()>
作用:按指定权限档案提交一轮提示词。它用于测试新权限系统下的行为。
数据流:传入提示词和权限档案 → 调用 TestCodex::submit_turn_with_permission_profile → 返回结果。
调用关系:权限档案相关测试会用它;底层仍进入 TestCodex 的统一提交流程。
调用图:调用 1 个内部函数(submit_turn_with_permission_profile)。
TestCodexHarness::request_bodies1056–1069 ↗
async fn request_bodies(&self) -> Vec<Value>
作用:收集 mock 服务器收到的模型请求体。测试用它检查 Codex 发给模型的 JSON 是否正确。
数据流:从 mock server 取全部请求 → 只保留路径匹配 /responses 的请求 → 把每个请求体解析成 JSON 列表返回。
调用关系:function_call_output_value 和 custom_tool_call_output 会调用它,从请求历史里找工具调用结果。
调用图:被 2 处调用(custom_tool_call_output, function_call_output_value);外部调用 2 个(received_requests, path_regex)。
TestCodexHarness::function_call_output_value1071–1074 ↗
async fn function_call_output_value(&self, call_id: &str) -> Value
作用:从模型请求历史里找某个函数调用的输出 JSON。函数调用是模型要求程序执行某个内置工具后的回传结果。
数据流:传入调用 ID → 先取所有请求体 → 调用 function_call_output 找到匹配项 → 克隆并返回 JSON 值。
调用关系:function_call_stdout 会调用它;它把 harness 请求收集能力和底层查找函数连起来。
调用图:调用 2 个内部函数(request_bodies, function_call_output);被 1 处调用(function_call_stdout)。
TestCodexHarness::function_call_stdout1076–1083 ↗
async fn function_call_stdout(&self, call_id: &str) -> String
作用:取某个函数调用输出里的标准输出文本。标准输出就是命令正常打印出来的内容。
数据流:传入调用 ID → 调用 function_call_output_value → 从 JSON 的 output 字段取字符串 → 返回文本。
调用关系:命令执行类测试用它检查工具输出;找不到或不是字符串时会让测试失败。
调用图:调用 1 个内部函数(function_call_output_value)。
TestCodexHarness::custom_tool_call_output1085–1088 ↗
async fn custom_tool_call_output(&self, call_id: &str) -> String
作用:取某个自定义工具调用的输出文本。自定义工具包括 apply_patch 这类非普通函数工具。
数据流:传入调用 ID → 获取请求体列表 → 调用 custom_tool_call_output_text 解析输出 → 返回字符串。
调用关系:apply_patch_output 会调用它;它负责从模型请求历史里抽出自定义工具结果。
调用图:调用 2 个内部函数(request_bodies, custom_tool_call_output_text);被 1 处调用(apply_patch_output)。
TestCodexHarness::apply_patch_output1090–1092 ↗
async fn apply_patch_output(&self, call_id: &str) -> String
作用:取某次 apply_patch 调用的输出。apply_patch 是测试里常见的“打补丁改文件”工具。
数据流:传入调用 ID → 直接调用 custom_tool_call_output → 返回补丁工具输出文本。
调用关系:apply_patch 相关测试会用它;它只是把自定义工具输出起了更贴近场景的名字。
调用图:调用 1 个内部函数(custom_tool_call_output)。
custom_tool_call_output1095–1106 ↗
fn custom_tool_call_output(bodies: &'a [Value], call_id: &str) -> &'a Value
作用:在一批请求 JSON 里找到指定自定义工具调用的输出项。找不到会让测试立刻失败。
数据流:传入请求体列表和调用 ID → 遍历每个 body 的 input 数组 → 找 type 为 custom_tool_call_output 且 call_id 匹配的项 → 返回该 JSON 引用。
调用关系:custom_tool_call_output_text 调用它先定位 JSON 项;它不解释输出内容,只负责查找。
调用图:被 1 处调用(custom_tool_call_output_text);外部调用 2 个(iter, format!)。
custom_tool_call_output_text1108–1114 ↗
fn custom_tool_call_output_text(bodies: &[Value], call_id: &str) -> String
作用:把指定自定义工具调用的输出转成文本。它兼容输出字段可能不是简单字符串的情况。
数据流:传入请求体列表和调用 ID → 用 custom_tool_call_output 找到项,再取 output 字段 → 调用 output_value_to_text 转成字符串并返回。
调用关系:TestCodexHarness::custom_tool_call_output 和本文件单元测试会调用它;缺少输出时会 panic,让测试明确失败。
调用图:调用 2 个内部函数(output_value_to_text, custom_tool_call_output);被 2 处调用(custom_tool_call_output, custom_tool_call_output_text_panics_when_output_is_missing);外部调用 1 个(format!)。
function_call_output1116–1127 ↗
fn function_call_output(bodies: &'a [Value], call_id: &str) -> &'a Value
作用:在请求 JSON 里找到指定函数调用的输出项。它用于验证 Codex 有没有把工具结果回传给模型。
数据流:传入请求体列表和调用 ID → 遍历所有 input 项 → 找 type 为 function_call_output 且 call_id 匹配的 JSON → 返回引用,找不到就失败。
调用关系:TestCodexHarness::function_call_output_value 调用它;它是函数工具输出查找的底层函数。
调用图:被 1 处调用(function_call_output_value);外部调用 2 个(iter, format!)。
test_codex1129–1147 ↗
fn test_codex() -> TestCodexBuilder
作用:创建一个默认的 TestCodexBuilder。这是大多数测试搭建 Codex 的起点。
数据流:不需要输入 → 创建带 dummy API key、空扩展注册表、默认配置修改器的 builder,并默认关闭 Apps 功能 → 返回 builder。
调用关系:TestCodexHarness::new、with_config 和大量测试会调用它;后续可链式加配置,再调用各种 build 方法。
调用图:调用 1 个内部函数(from_api_key);被 583 处调用(fork_startup_context_then_first_turn_diff_snapshot, session_configured_reports_permission_profile_for_external_sandbox, apps_enabled_builder, search_capable_apps_builder, new, with_config, build_unified_exec_zsh_fork_test, build_zsh_fork_test, responses_stream_includes_turn_metadata_header_for_git_workspace_e2e, interrupt_long_running_tool_emits_turn_aborted (+15 more));外部调用 2 个(empty_extension_registry, vec!)。
tests::custom_tool_call_output_text_returns_output_text1156–1166 ↗
fn custom_tool_call_output_text_returns_output_text()
作用:测试 custom_tool_call_output_text 能正常返回文本输出。它防止工具输出解析的基本路径坏掉。
数据流:构造一个带 custom_tool_call_output 和 output: hello 的假请求体 → 调用解析函数 → 断言结果等于 hello。
调用关系:这是本文件内部单元测试;它直接验证 custom_tool_call_output_text 的成功情况。
调用图:外部调用 2 个(assert_eq!, vec!)。
tests::custom_tool_call_output_text_panics_when_output_is_missing1170–1179 ↗
fn custom_tool_call_output_text_panics_when_output_is_missing()
作用:测试自定义工具输出缺少 output 字段时会明确失败。这样解析错误不会悄悄被忽略。
数据流:构造一个没有 output 字段的假请求体 → 调用 custom_tool_call_output_text → 预期触发 panic,错误信息包含缺少输出。
调用关系:这是本文件内部单元测试;它覆盖 custom_tool_call_output_text 的失败路径。
调用图:调用 1 个内部函数(custom_tool_call_output_text);外部调用 1 个(vec!)。
core/tests/common/test_codex_exec.rs源码 ↗
测试命令行程序时,最怕它读到真实电脑里的配置、写坏真实文件,或者真的连到外网。这个文件就是一个测试用的小工具箱,专门用来安全地启动 codex-exec。它会为每次测试创建两个临时文件夹:一个冒充用户的 home 目录,一个作为当前工作目录。然后它构造 assert_cmd::Command(一个专门用来在测试里运行命令行程序的工具),把 CODEX_HOME、CODEX_SQLITE_HOME 和假的 API key 都塞进去。这样 codex-exec 以为自己在正常环境里运行,但其实被关在测试沙盒里。它还可以接 wiremock 的 MockServer(一个假 HTTP 服务器),把请求地址改到假服务器,方便检查程序发了什么请求,而不是访问真实服务。
TestCodexExecBuilder::cmd12–22 ↗
fn cmd(&self) -> assert_cmd::Command
作用:创建一个已经配好测试环境的 codex-exec 命令。测试用它来启动真实的 codex-exec 二进制文件,但把它关进临时目录,并给它假的登录信息。
数据流:进去的是这个 builder 里保存的临时 home 目录和临时 cwd 目录。函数先找到 codex-exec 这个可执行程序,再创建一个测试命令;接着把当前目录改成临时 cwd,并设置 CODEX_HOME、CODEX_SQLITE_HOME 和假的 API key。出来的是一个 assert_cmd::Command,测试可以继续给它加参数并运行。
调用关系:这是最基础的命令构造步骤。TestCodexExecBuilder::cmd_with_server 会先调用它拿到普通命令,再额外加上假服务器地址;很多测试间接依赖它来保证运行环境干净、可重复。
调用图:被 1 处调用(cmd_with_server);外部调用 3 个(path, new, cargo_bin)。
TestCodexExecBuilder::cmd_with_server23–29 ↗
fn cmd_with_server(&self, server: &MockServer) -> assert_cmd::Command
作用:创建一个会连接到测试假服务器的 codex-exec 命令。这样测试可以模拟 OpenAI 接口,而不用真的发网络请求。
数据流:进去的是 builder 自己和一个 MockServer。函数先调用 TestCodexExecBuilder::cmd 得到基本命令,再从假服务器取出地址,拼成带 /v1 的接口根地址,最后通过 -c 参数把 openai_base_url 配置塞给命令。出来的是一个已经指向假服务器的命令对象。
调用关系:它站在普通命令构造和网络接口测试之间:先借 TestCodexExecBuilder::cmd 搭好沙盒环境,再把请求出口改到 MockServer。需要检查请求内容或模拟服务响应的测试会用它。
TestCodexExecBuilder::cwd_path31–33 ↗
fn cwd_path(&self) -> &Path
作用:把测试用的当前工作目录路径拿出来。测试如果要提前在工作目录里放文件,或者检查程序生成了什么文件,就会用它。
数据流:进去的是 builder 自己。函数读取内部保存的临时 cwd 目录,并返回这个目录的路径引用;它不创建新目录,也不修改任何东西。
调用关系:它是给测试用例看的一个窗口。codex-exec 运行时会把这个目录当作当前目录,而测试可以在运行前后通过这个函数访问同一个地方。
调用图:外部调用 1 个(path)。
TestCodexExecBuilder::home_path34–36 ↗
fn home_path(&self) -> &Path
作用:把测试用的假 home 目录路径拿出来。测试如果要检查配置、数据库或状态文件是否写到了隔离目录,就会用它。
数据流:进去的是 builder 自己。函数读取内部保存的临时 home 目录,并返回这个目录的路径引用;它只提供地址,不改动文件系统。
调用关系:TestCodexExecBuilder::cmd 会把这个目录设置成 CODEX_HOME 和 CODEX_SQLITE_HOME。测试用例再通过 TestCodexExecBuilder::home_path 回头检查程序是否把数据写到了预期位置。
调用图:外部调用 1 个(path)。
toml_string_literal39–41 ↗
fn toml_string_literal(value: &str) -> String
作用:把普通字符串变成适合放进 TOML 配置里的字符串字面量。这里主要用来安全地写入 openai_base_url 这种配置值。
数据流:进去的是一个普通字符串,比如假服务器地址。函数用 JSON 序列化把它转成带引号、已正确转义的字符串形式;出来的是可以放到配置参数里的文本。如果转换失败,测试会直接报错。
调用关系:TestCodexExecBuilder::cmd_with_server 用它包装假服务器地址,避免地址里有特殊字符时配置格式出错。它是一个小型格式转换帮手。
调用图:外部调用 1 个(to_string)。
test_codex_exec43–48 ↗
fn test_codex_exec() -> TestCodexExecBuilder
作用:创建一个新的 TestCodexExecBuilder,作为很多 codex-exec 测试的起点。每次调用都会得到全新的临时 home 和 cwd,保证测试彼此隔离。
数据流:进去没有参数。函数新建两个临时目录:一个当假 home,一个当假当前工作目录;然后把它们装进 TestCodexExecBuilder 返回。结果是一个可继续生成命令、查看路径的测试构造器。
调用关系:这是测试入口级的辅助函数,许多测试会先调用它,再通过 builder 的 cmd、cmd_with_server、cwd_path 或 home_path 继续安排测试场景。它把重复的测试准备工作集中起来,避免每个测试自己手写一遍。
调用图:被 28 处调用(accepts_add_dir_flag, accepts_multiple_add_dir_flags, exec_includes_workspace_agents_md_in_request, exec_prefers_workspace_agents_override_md, run_exec_with_auto_review_config, exec_uses_codex_api_key_env_var, does_not_persist_rollout_file_in_ephemeral_mode, persists_rollout_file_by_default, exec_hook_trust_bypass_runs_session_start_hook, exits_non_zero_when_required_mcp_server_fails_to_initialize (+15 more));外部调用 1 个(new)。