工具、shell/exec、MCP/app、插件与运行时项套件
这一阶段像给“助手动手干活”做全套验收,属于主流程背后的安全网。它检查模型能看到哪些工具、能不能跑 shell 命令、改补丁、看图片、搜工具、装插件、调用 MCP 和 app。各测试还盯住沙盒权限、超时中断、并行执行、长输出截短、结果格式、事件顺序。简单说,就是确保工具既能用、好用,又不能越权乱动机器。
工具执行基础
这些套件确立内置 shell、exec 和 apply-patch 工具的基线测试框架、暴露规则和核心执行路径。
core/tests/suite/tools.rs源码 ↗
这个文件专门测试“模型能用哪些工具、工具调用后系统怎么回应”。可以把它想成一套安检流程:先看工具菜单里该出现谁、不该出现谁;再让假模型发起各种工具调用,检查系统回传给模型的结果是否安全、清楚、可继续对话。测试里会启动一个假的服务器,模拟真实模型通过 SSE(服务器不断推送事件的一种通信方式)返回消息或工具调用。然后测试版 Codex 把请求发过去,文件再检查请求体和工具输出。重点覆盖几类容易出事故的地方:没有选择运行环境时不能暴露依赖环境的工具;未知自定义工具要明确报错;禁止提权时不能偷偷申请更高权限;只读或按通配符禁止访问的文件不能泄密;命令超时时要带上明确的超时信息,而且即使有后台子进程占着输出管道,也不能一直挂住。
tool_names37–52 ↗
fn tool_names(body: &Value) -> Vec<String>
作用:从一次发给模型的 JSON 请求里,把工具名字提取出来,方便测试判断某个工具有没有出现在列表中。它是测试里的小帮手,不直接参与真实业务。
数据流:输入是一段 JSON 请求体 → 它查找里面的 tools 数组,再从每个工具里取 name,取不到就取 type → 输出一个字符串列表;如果请求里没有工具数组,就输出空列表,不改动原数据。
调用关系:它被 collect_tools、empty_turn_environments_omits_environment_backed_tools 和 turn_environment_selection_keeps_environment_backed_tools 调用。那些测试先让 Codex 发出请求,再把请求体交给它整理成工具名列表,最后用这个列表做断言。
调用图:被 3 处调用(collect_tools, empty_turn_environments_omits_environment_backed_tools, turn_environment_selection_keeps_environment_backed_tools);外部调用 1 个(get)。
empty_turn_environments_omits_environment_backed_tools55–93 ↗
async fn empty_turn_environments_omits_environment_backed_tools() -> Result<()>
作用:测试当一次对话明确说“没有可用运行环境”时,系统不应该把依赖本地环境的工具发给模型。这样可以避免模型调用根本无法执行、或者不该执行的本地工具。
数据流:测试先启动假服务器,并配置它返回一段普通完成消息 → 再打开 UnifiedExec 这个特性,然后提交一轮带空环境列表的对话 → 最后读取发给假服务器的请求体,检查 update_plan 还在,但 exec_command、write_stdin、apply_patch、view_image 这些依赖环境的工具都不在。
调用关系:它通过 start_mock_server 和 mount_sse_once 搭好假模型服务,通过 test_codex 创建测试用 Codex,再用 tool_names 提取工具清单。它验证的是工具清单生成流程在“环境列表为空”这个入口条件下是否正确收缩。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, tool_names);外部调用 3 个(assert!, skip_if_no_network!, vec!)。
turn_environment_selection_keeps_environment_backed_tools96–131 ↗
async fn turn_environment_selection_keeps_environment_backed_tools() -> Result<()>
作用:测试当一次对话选择了本地运行环境时,依赖环境的工具应该继续提供给模型。否则模型就算被允许操作本地环境,也看不到能执行命令的工具。
数据流:测试先启动假服务器并准备一个完成响应 → 打开 UnifiedExec 特性 → 提交一轮带本地环境的对话 → 读取请求体里的工具列表,确认 exec_command 这种环境工具确实存在。
调用关系:它和前一个测试形成一正一反的对照:同样调用 start_mock_server、mount_sse_once、test_codex 和 tool_names,但这里传入 local 环境,用来证明工具不是一概隐藏,而是按本轮选择的环境决定。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, tool_names);外部调用 3 个(assert!, skip_if_no_network!, vec!)。
custom_tool_unknown_returns_custom_output_error134–178 ↗
async fn custom_tool_unknown_returns_custom_output_error() -> Result<()>
作用:测试模型如果调用一个系统不认识的自定义工具,Codex 会把明确的错误结果回传给模型。这样对话不会莫名中断,模型也能知道自己调用错了工具名。
数据流:测试先让假服务器在第一轮返回一个名为 unsupported_tool 的自定义工具调用 → Codex 收到后发现不支持这个工具 → 第二次请求会带上这个工具调用的输出,测试读取该输出,确认内容是“unsupported custom tool call: unsupported_tool”。
调用关系:它用 mount_sse_once 连续挂两段假响应:第一段制造未知工具调用,第二段接收 Codex 回传的工具结果。它检查的是工具分发流程遇到陌生工具时的兜底行为。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 4 个(assert_eq!, format!, skip_if_no_network!, vec!)。
shell_command_escalated_permissions_rejected_then_ok181–272 ↗
async fn shell_command_escalated_permissions_rejected_then_ok() -> Result<()>
作用:测试当审批策略是“永不批准”时,模型请求 shell_command 使用更高权限会被拒绝;但普通权限的同一命令仍然可以执行。这样既守住权限边界,也不误伤正常命令。
数据流:输入是模型先后发来的两次 shell_command 调用:第一次带 RequireEscalated,表示要求提权;第二次不提权 → Codex 先拒绝第一次,并把拒绝原因回传 → 再执行第二次 echo 命令 → 测试分别检查第一次输出是权限策略拒绝信息,第二次输出包含退出码 0 和 shell ok。
调用关系:它通过假服务器安排三段交互:提权调用、普通调用、最终助手消息。它把 shell_command 的权限判断和实际执行串起来验证,确保被拒的是提权请求本身,而不是整个对话流程。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 6 个(assert_eq!, assert_regex_match, format!, json!, skip_if_no_network!, vec!)。
sandbox_denied_shell_command_returns_original_output275–362 ↗
async fn sandbox_denied_shell_command_returns_original_output() -> Result<()>
作用:测试命令在只读沙箱里写文件被系统拒绝时,返回给模型的是原始命令输出和系统拒绝细节,而不是一个笼统的“沙箱失败”。这能帮助模型知道到底哪个路径、因为什么被挡住。
数据流:测试构造一个命令:先打印一段标记文字,再尝试写入工作区文件 → 用只读权限提交给 Codex → 沙箱阻止写入 → 测试读取工具输出,确认里面有非零退出码、有权限拒绝字样、有标记文字、有被拒路径,并且没有被替换成泛泛的 fallback 错误。
调用关系:它用 mount_sse_sequence 安排模型先调用 shell_command、再结束对话。这个测试站在 shell 命令执行链路的错误出口处,确认沙箱底层返回的信息没有被上层吞掉或改坏。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, read_only);外部调用 6 个(assert!, assert_ne!, format!, json!, skip_if_no_network!, vec!)。
shell_command_enforces_glob_deny_read_policy365–467 ↗
async fn shell_command_enforces_glob_deny_read_policy() -> Result<()>
作用:测试文件访问规则里的“通配符禁止读取”真的会生效。例如禁止读取所有 .env 文件时,命令不能把 secret.env 的内容泄露给模型。
数据流:测试先配置一个文件系统沙箱规则:当前目录下所有 .env 文件都拒绝访问 → 再创建一个被禁止的 secret.env 和一个允许读取的 notes.txt → 让 shell_command 同时 cat 两个文件 → 输出应包含允许文件内容,不包含秘密内容,并带有权限拒绝信息和非零退出码。
调用关系:它先通过 test_codex 的配置闭包改权限策略,再用真实文件准备测试现场,最后用假服务器触发 shell_command。它验证的是权限配置、沙箱执行和输出过滤三者能一起守住读文件边界。
调用图:调用 3 个内部函数(mount_sse_sequence, start_mock_server, test_codex);外部调用 9 个(assert!, assert_ne!, format!, create_dir_all, write, json!, skip_if_no_network!, skip_if_sandbox!, vec!)。
collect_tools469–503 ↗
async fn collect_tools(use_unified_exec: bool) -> Result<Vec<String>>
作用:按指定开关启动一轮测试对话,并收集 Codex 发给模型的工具列表。它让多个测试不用重复写“启动假服务器、提交对话、解析工具名”这些步骤。
数据流:输入是 use_unified_exec 这个布尔值,表示是否开启 UnifiedExec → 它启动假服务器,按输入打开或关闭该特性,提交一轮普通对话 → 从第一次请求体里提取工具名并返回字符串列表。
调用关系:它被 unified_exec_spec_toggle_end_to_end 调用。内部会调用 start_mock_server、mount_sse_sequence、test_codex 和 tool_names,把环境搭建、发起请求、提取工具清单这一整段流程包装成一个可复用的小流程。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, tool_names);被 1 处调用(unified_exec_spec_toggle_end_to_end);外部调用 1 个(vec!)。
unified_exec_spec_toggle_end_to_end506–530 ↗
async fn unified_exec_spec_toggle_end_to_end() -> Result<()>
作用:测试 UnifiedExec 这个功能开关是否真正控制 exec_command 和 write_stdin 这两个工具的出现。也就是说,开关关掉时工具不出现,打开时工具出现。
数据流:它先调用 collect_tools(false) 得到关闭特性时的工具列表,并确认没有 exec_command 和 write_stdin → 再调用 collect_tools(true) 得到开启特性时的工具列表,并确认这两个工具出现 → 不改动外部状态,只用断言判断结果。
调用关系:它是 collect_tools 的上层验证者。collect_tools 负责跑一轮对话并拿到清单,这个函数负责比较两个清单,确认功能开关从配置一路影响到了最终发给模型的工具规格。
调用图:调用 1 个内部函数(collect_tools);外部调用 2 个(assert!, skip_if_no_network!)。
shell_command_timeout_includes_timeout_prefix_and_metadata533–616 ↗
async fn shell_command_timeout_includes_timeout_prefix_and_metadata() -> Result<()>
作用:测试 shell_command 超时时,返回内容要能清楚告诉模型“命令超时了”,并尽量带上退出码等结构化信息。这样模型不会把超时误解成普通输出或未知崩溃。
数据流:测试让模型调用一个会先输出很多行、再睡眠超过限制的命令,并设置很短的 timeout_ms → Codex 执行到超时后返回工具输出 → 测试接受两种可能格式:一种是 JSON,里面 metadata.exit_code 为 124 且输出含 command timed out;另一种是普通文本,但必须匹配超时格式;如果底层先报告信号中止,也接受包含 signal 的错误。
调用关系:它用 mount_sse_once 安排一次 shell_command 和一次后续响应。它处在执行命令的超时路径上,重点不是命令本身,而是检查超时被包装成模型能理解、测试能稳定识别的输出。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 7 个(new, assert!, assert_eq!, assert_regex_match, json!, skip_if_no_network!, vec!)。
shell_command_timeout_handles_background_grandchild_stdout619–713 ↗
async fn shell_command_timeout_handles_background_grandchild_stdout() -> Result<()>
作用:测试命令超时后,即使它启动的后台孙进程还占着标准输出管道,Codex 也不能一直等下去。简单说,就是防止一个“赖着不关门的子进程”把整个工具调用卡死。
数据流:测试先写一个 Python 脚本,脚本会启动一个脱离的后台进程并睡很久,同时记录后台进程号 → 让 shell_command 执行这个脚本,并设置 200 毫秒超时 → 用 10 秒总限时包住整轮对话,拿到输出后确认它表示超时,且实际耗时小于 9 秒 → 最后如果读到后台进程号,就发送 SIGKILL 把它清掉。
调用关系:它通过假服务器触发 shell_command,通过 tokio::time::timeout 给整个测试再加一道保险。它验证的是命令执行器在复杂进程树场景下的收尾能力:到点要返回结果,不能被仍然活着的孙进程拖住。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 13 个(from_secs, now, assert!, assert_eq!, assert_regex_match, format!, read_to_string, write, json!, kill (+3 more))。
core/tests/suite/tool_harness.rs源码 ↗
这个文件像一套“工具使用验收清单”。测试不会真的去请求真实模型,而是启动一个假服务器,模拟模型通过 SSE(服务器持续推送消息的一种方式)发来“请调用某个工具”的指令。然后测试把用户输入交给 Codex,看 Codex 是否按要求执行:比如运行一条命令并把输出发回去,收到计划更新后是否通知界面,应用补丁后文件是否真的被改掉。它还专门测坏情况:计划参数少了、补丁格式错了时,系统不能装作成功,而要把错误信息回传,并尽量标记失败。文件顶部限制了不在 Windows 上跑,因为这些测试依赖类 Unix 环境里的命令和文件行为。
call_output32–44 ↗
fn call_output(req: &ResponsesRequest, call_id: &str) -> (String, Option<bool>)
作用:这个小帮手从一次发给假服务器的请求里,取出普通函数工具的执行结果。它还顺手检查结果里的 call_id 是否和预期一致,避免测试拿错了工具调用的回执。
数据流:输入是一份 ResponsesRequest 请求记录和一个 call_id → 它先按 call_id 找到 function_call_output,再确认里面写的 call_id 没串号,然后取出文本内容和可选的成功标记 → 输出是工具返回的文字内容,以及一个可能存在的 success 布尔值。
调用关系:它被 shell 命令工具和 update_plan 工具相关测试调用。那些测试先让 Codex 执行工具,再用这个函数从第二次模型请求里抽出“工具执行结果”,方便断言内容是否正确。
调用图:调用 2 个内部函数(function_call_output, function_call_output_content_and_success);被 3 处调用(shell_command_tool_executes_command_and_streams_output, update_plan_tool_emits_plan_update_event, update_plan_tool_rejects_malformed_payload);外部调用 1 个(assert_eq!)。
custom_call_output46–58 ↗
fn custom_call_output(req: &ResponsesRequest, call_id: &str) -> (String, Option<bool>)
作用:这个小帮手专门读取自定义工具的执行结果,比如 apply_patch 这种不是普通函数调用格式的工具。它的作用是确认测试拿到的是指定 call_id 对应的补丁工具回执。
数据流:输入是一份 ResponsesRequest 请求记录和一个 call_id → 它找到 custom_tool_call_output,检查 call_id 是否匹配,再取出输出文本和可选成功标记 → 输出是补丁工具返回的文字,以及可能有的 success 状态。
调用关系:它被两个 apply_patch 测试使用。测试让假服务器要求 Codex 应用补丁后,会通过这个函数读取 Codex 回传给模型的补丁执行结果。
调用图:调用 2 个内部函数(custom_tool_call_output, custom_tool_call_output_content_and_success);被 2 处调用(apply_patch_reports_parse_diagnostics, apply_patch_tool_executes_and_emits_patch_events);外部调用 1 个(assert_eq!)。
shell_command_tool_executes_command_and_streams_output61–135 ↗
async fn shell_command_tool_executes_command_and_streams_output() -> anyhow::Result<()>
作用:这个测试确认模型要求调用 shell_command 时,Codex 会真的运行命令,并把退出码、耗时和命令输出整理后发回模型。简单说,就是检查“让助手跑命令”这条链路有没有通。
数据流:测试先启动假服务器和临时 Codex 会话,再让假服务器第一次回复一个 shell_command 调用,命令是 echo tool harness → Codex 收到用户输入后执行该命令,并把执行结果作为下一次请求发回服务器 → 测试读取这次请求,确认输出里有 Exit code: 0、Wall time 和 tool harness。
调用关系:它会调用 start_mock_server、test_codex、mount_sse_once、sse 等测试设施搭场景,用 turn_permission_fields 和 local_selections 配好当前目录与权限,最后交给 call_output 读取工具结果。它验证的是普通函数工具调用流程里最基础的一条:模型要运行命令,Codex 执行并回报。
调用图:调用 7 个内部函数(mount_sse_once, sse, start_mock_server, local_selections, test_codex, turn_permission_fields, call_output);外部调用 6 个(default, assert_regex_match, wait_for_event, json!, skip_if_no_network!, vec!)。
update_plan_tool_emits_plan_update_event138–230 ↗
async fn update_plan_tool_emits_plan_update_event() -> anyhow::Result<()>
作用:这个测试确认模型调用 update_plan 后,Codex 不只是回一句“计划已更新”,还会向外发出 PlanUpdate 事件,让界面或上层系统能看到新的计划步骤。
数据流:测试准备一个包含说明和两步计划的 JSON 参数 → 假服务器把它作为 update_plan 工具调用发给 Codex → Codex 解析计划,发出 PlanUpdate 事件,并把“Plan updated”作为工具输出发回服务器 → 测试检查事件里的说明、步骤文字和状态都正确。
调用关系:它通过假服务器模拟模型发起 update_plan 调用,通过 wait_for_event 等待 Codex 内部事件流,再用 call_output 检查回传给模型的工具结果。它证明计划工具既会通知本地界面,也会告诉模型工具已经完成。
调用图:调用 7 个内部函数(mount_sse_once, sse, start_mock_server, local_selections, test_codex, turn_permission_fields, call_output);外部调用 7 个(default, assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, vec!)。
update_plan_tool_rejects_malformed_payload233–327 ↗
async fn update_plan_tool_rejects_malformed_payload() -> anyhow::Result<()>
作用:这个测试确认 update_plan 收到坏参数时不会误报成功。比如模型只给了说明,却没给真正的 plan 数组,Codex 应该返回解析错误,并且不发出计划更新事件。
数据流:测试把缺少 plan 字段的 JSON 作为 update_plan 参数交给假服务器 → Codex 收到后尝试解析,发现格式不对 → 它不产生 PlanUpdate 事件,而是把包含“failed to parse function arguments”的错误文本发回服务器,并可能把 success 标成 false。
调用关系:它和正常计划更新测试是一正一反的搭配。流程同样依赖 start_mock_server、test_codex、mount_sse_once、wait_for_event 和 call_output,但这里重点看错误路径:坏输入不能污染计划状态。
调用图:调用 7 个内部函数(mount_sse_once, sse, start_mock_server, local_selections, test_codex, turn_permission_fields, call_output);外部调用 6 个(default, assert!, wait_for_event, json!, skip_if_no_network!, vec!)。
apply_patch_tool_executes_and_emits_patch_events330–470 ↗
async fn apply_patch_tool_executes_and_emits_patch_events() -> anyhow::Result<()>
作用:这个测试确认 apply_patch 工具能真的修改文件,并且在修改开始、结束时发出对应事件。它保证“模型给补丁,Codex 改工作区文件”这件危险但重要的事是可观察、可验证的。
数据流:测试先准备一个临时目录和一段新增 notes.txt 的补丁文本 → 假服务器把补丁作为自定义工具调用发给 Codex → Codex 应用补丁,创建文件,发出 FileChange、PatchApplyBegin、PatchApplyEnd 等事件,并把执行摘要回传给服务器 → 测试检查事件、工具输出和磁盘上的文件内容都符合预期。
调用关系:它搭好假模型响应后,把用户请求提交给 Codex,并一路监听事件流。自定义工具的回执由 custom_call_output 读取,文件内容则直接从临时目录读取。这个测试覆盖 apply_patch 成功执行时从模型指令到磁盘变化的完整链路。
调用图:调用 7 个内部函数(mount_sse_once, sse, start_mock_server, local_selections, test_codex, turn_permission_fields, custom_call_output);外部调用 9 个(default, assert!, assert_eq!, assert_regex_match, wait_for_event, format!, read_to_string, skip_if_no_network!, vec!)。
apply_patch_reports_parse_diagnostics473–558 ↗
async fn apply_patch_reports_parse_diagnostics() -> anyhow::Result<()>
作用:这个测试确认 apply_patch 收到格式不完整或无法解析的补丁时,会把具体诊断信息返回给模型,而不是只说一个模糊的失败。这样模型或用户才知道哪里写错了。
数据流:测试构造一段缺少有效 hunk 内容的补丁 → 假服务器要求 Codex 应用它 → Codex 尝试解析补丁并失败 → 它把包含“apply_patch verification failed”和“invalid hunk”的错误文本回传给服务器,并可能把 success 标成 false。
调用关系:它是 apply_patch 成功测试的错误版本。它同样通过假服务器和 test_codex 建立会话,通过 custom_call_output 读取自定义工具输出,重点确认错误信息足够明确,且失败不会被当成成功。
调用图:调用 7 个内部函数(mount_sse_once, sse, start_mock_server, local_selections, test_codex, turn_permission_fields, custom_call_output);外部调用 5 个(default, assert!, wait_for_event, skip_if_no_network!, vec!)。
core/tests/suite/exec.rs源码 ↗
这个文件像是在给“执行外部命令”这台机器做体检。项目里有一套沙箱机制,沙箱可以理解成一个安全隔离房间:命令可以在里面运行,但不能随便碰外面的东西。这里专门测试 macOS 的 Seatbelt 沙箱,也就是苹果系统提供的限制机制。测试开始前会先看当前环境是不是已经在 Seatbelt 沙箱里,如果是,就跳过,避免“沙箱套沙箱”导致结果不可靠。核心帮手是 run_test_cmd,它会创建执行参数,把临时目录当作工作目录,用只读权限配置去运行命令,然后拿回标准输出、标准错误和错误结果。后面的测试分别验证 echo、seq、大量字节输出、找不到命令、伪终端 openpty,以及写文件被拒绝这些场景。这样可以保证执行工具既能正常干活,又不会突破安全边界。
skip_test18–25 ↗
fn skip_test() -> bool
作用:这个函数决定当前测试要不要跳过。它主要防止测试在已经启用 Seatbelt 沙箱的环境里再次运行,因为那样结果可能不是代码本身的问题,而是外层环境干扰。
数据流:进去的是当前进程的环境变量信息,尤其是 CODEX_SANDBOX_ENV_VAR。它读取这个变量,如果值正好是 seatbelt,就打印一条提示并返回 true;否则返回 false,不改动其他状态。
调用关系:每个具体测试一开始都会先问 skip_test:“现在适合跑吗?”如果它说要跳过,测试就直接结束;如果不跳过,测试才继续创建临时目录并执行命令。它只做开关判断,不负责真正运行命令。
调用图:被 6 处调用(exit_code_0_succeeds, exit_command_not_found_is_ok, openpty_works_under_real_exec_seatbelt_path, truncates_output_bytes, truncates_output_lines, write_file_fails_as_sandbox_error);外部调用 2 个(eprintln!, var)。
run_test_cmd27–61 ↗
async fn run_test_cmd(tmp: TempDir, command: I) -> Result<ExecToolCallOutput>
作用:这个函数是本文件里反复使用的测试执行器。测试只要给它一个临时目录和一条命令,它就按真实的沙箱执行路径把命令跑起来,并把执行结果带回来。
数据流:进去的是一个临时目录和命令参数列表。它先确认当前平台应该使用 macOS Seatbelt 沙箱,再把临时目录转成绝对路径,组装 ExecParams,也就是“怎么运行这条命令”的说明书;然后用只读权限调用 process_exec_tool_call。出来的是 ExecToolCallOutput,里面有标准输出、标准错误等信息;如果沙箱或执行过程出错,就返回错误。
调用关系:它是各个测试共用的中间层。exit_code_0_succeeds、truncates_output_lines、truncates_output_bytes、exit_command_not_found_is_ok、openpty_works_under_real_exec_seatbelt_path 等测试都把具体命令交给它;它再把真正执行工作交给核心函数 process_exec_tool_call。
调用图:调用 2 个内部函数(process_exec_tool_call, read_only);被 5 处调用(exit_code_0_succeeds, exit_command_not_found_is_ok, openpty_works_under_real_exec_seatbelt_path, truncates_output_bytes, truncates_output_lines);外部调用 6 个(new, into_iter, path, assert_eq!, get_platform_sandbox, from_ref)。
exit_code_0_succeeds65–77 ↗
async fn exit_code_0_succeeds()
作用:这个测试确认最普通的成功命令可以正常运行。它用 echo hello 这种简单命令验证:沙箱没有把正常执行误拦住。
数据流:进去没有外部输入。它先用 skip_test 判断是否跳过,然后创建临时目录,准备 echo hello 命令,交给 run_test_cmd 执行。结果回来后,它检查标准输出必须是 hello 加换行,标准错误必须为空,并且输出没有被截断。
调用关系:这是基础冒烟测试,像先确认机器能开机。它依赖 skip_test 做环境判断,依赖 run_test_cmd 走真实执行流程,最后用断言检查返回内容是否符合预期。
调用图:调用 2 个内部函数(run_test_cmd, skip_test);外部调用 3 个(new, assert_eq!, vec!)。
truncates_output_lines81–97 ↗
async fn truncates_output_lines()
作用:这个测试确认多行输出可以被完整收集。它用 seq 300 生成 300 行数字,检查执行工具没有错误地把这些行砍掉。
数据流:进去没有外部输入。它先判断是否跳过,再创建临时目录,运行 seq 300。拿到输出后,它自己拼出 1 到 300 每行一个数字的期望文本,然后和实际标准输出比较,并确认没有记录“按行截断”。
调用关系:它同样通过 skip_test 和 run_test_cmd 进入统一测试路径。和基础 echo 测试相比,它更关注输出收集能力,特别是很多行文本时执行工具是否仍然可靠。
调用图:调用 2 个内部函数(run_test_cmd, skip_test);外部调用 3 个(new, assert_eq!, vec!)。
truncates_output_bytes101–114 ↗
async fn truncates_output_bytes()
作用:这个测试确认较大的文本输出也能被收下来。它生成很多长行,验证执行工具不会因为字节数比较多就异常截断。
数据流:进去没有外部输入。它先决定是否跳过,创建临时目录,然后运行一段 bash 命令:生成 15 行,每行大约 1000 字节。执行完成后,它检查标准输出长度至少达到 15000,并确认没有按行截断的标记。
调用关系:它使用 run_test_cmd 走和其他测试一样的真实沙箱执行通道。它补充覆盖“输出很大”的情况,帮助发现只在大量文本时才出现的问题。
调用图:调用 2 个内部函数(run_test_cmd, skip_test);外部调用 4 个(new, assert!, assert_eq!, vec!)。
exit_command_not_found_is_ok118–126 ↗
async fn exit_command_not_found_is_ok()
作用:这个测试确认“命令不存在”只是普通的命令执行失败,不应该被误判成沙箱故障。这样用户输错命令时,系统不会给出错误方向的诊断。
数据流:进去没有外部输入。它先检查是否跳过,再创建临时目录,运行 /bin/bash -c nonexistent_command_12345。这个命令会返回找不到命令的退出码,但测试期望 run_test_cmd 本身不要返回沙箱错误。
调用关系:它通过 skip_test 控制是否执行,通过 run_test_cmd 使用统一执行流程。它关心的是错误分类:命令自身失败可以接受,但不应被包装成安全沙箱出错。
调用图:调用 2 个内部函数(run_test_cmd, skip_test);外部调用 2 个(new, vec!)。
openpty_works_under_real_exec_seatbelt_path129–157 ↗
async fn openpty_works_under_real_exec_seatbelt_path()
作用:这个测试确认在真实 Seatbelt 执行路径下,程序仍然可以使用 openpty。openpty 可以简单理解成创建一对“虚拟终端管道”,很多交互式程序会用到它。
数据流:进去没有外部输入。它先判断是否跳过,再查找系统里有没有 python3;如果没有,就打印提示并跳过。找到后,它创建临时目录,运行一小段 Python:打开伪终端,把 ping 写到一端,再从另一端读回来。最后检查标准输出和标准错误都为空,说明脚本内部断言通过且没有报错。
调用关系:它先用 which 找 python3,这是这个测试额外需要的工具;再把 Python 命令交给 run_test_cmd。它覆盖的是更贴近真实程序的系统能力,防止沙箱规则太严,误伤需要伪终端的命令。
调用图:调用 2 个内部函数(run_test_cmd, skip_test);外部调用 5 个(new, assert_eq!, eprintln!, vec!, which)。
write_file_fails_as_sandbox_error161–174 ↗
async fn write_file_fails_as_sandbox_error()
作用:这个测试确认只读沙箱真的会阻止写文件。它故意尝试在临时目录里 touch 一个文件,并期望这件事失败,而且被当作沙箱错误。
数据流:进去没有外部输入。它先检查是否跳过,再创建临时目录,拼出 test.txt 的路径,准备 /usr/bin/touch 这条写文件命令。随后执行这条命令,并断言结果是错误,也就是写入被沙箱挡住了。
调用关系:它和前面的成功类测试形成对照:前面证明该放行的命令能跑,这里证明不该放行的写操作会被拦。它依赖 skip_test 避免环境干扰,并走同一套执行测试路径来验证权限边界。
core/tests/suite/shell_command.rs源码 ↗
这个文件像一套体检表,给 shell_command 功能逐项做检查。shell 是电脑里执行命令的程序,比如运行 echo、sleep、管道命令。测试先搭好一个 TestCodexHarness,也就是测试用的小环境;再把假模型回复挂到测试服务器上,回复里会要求调用 shell_command;然后提交一句用户请求,等系统真的去跑命令;最后读取这次函数调用的标准输出,确认格式和内容都对。它特别关心几类坑:登录 shell 开关是否影响输出、跨平台换行是否一致、Windows 上的限制、命令超时是否变成 124 退出码,以及带重音符号的 UTF-8 文本会不会乱码。
shell_responses_with_timeout29–54 ↗
fn shell_responses_with_timeout(
call_id: &str,
command: &str,
login: Option<bool>,
timeout_ms: i64,
) -> Vec<String>
作用:生成一组假的服务器事件,告诉测试里的模型“请调用 shell_command 执行这个命令”,并指定超时时间。有人写测试时用它来精确控制命令、登录 shell 选项和等待多久。
数据流:输入是调用编号、命令文本、是否使用登录 shell,以及超时时间毫秒数。它把这些打包成 JSON 参数,再拼成两段 SSE 事件流;SSE 可以理解成服务器一条条推送消息。输出是一串字符串,后面会挂到假服务器上。
调用关系:它是测试数据的源头。shell_responses 用它生成默认超时版本,mount_shell_responses_with_timeout 也直接用它,然后把生成的事件交给 mount_sse_sequence 挂到测试服务器。
调用图:被 2 处调用(mount_shell_responses_with_timeout, shell_responses);外部调用 3 个(json!, to_string, vec!)。
shell_responses56–58 ↗
fn shell_responses(call_id: &str, command: &str, login: Option<bool>) -> Vec<String>
作用:生成带默认超时时间的 shell_command 假回复。大多数测试不关心具体超时时间时,就用这个更省事的包装函数。
数据流:输入是调用编号、命令文本和登录 shell 选项。它补上当前平台的默认超时时间,再交给 shell_responses_with_timeout;输出是可挂载到假服务器的 SSE 字符串列表。
调用关系:它夹在普通测试和更底层的 shell_responses_with_timeout 之间。mount_shell_responses 调用它,普通成功场景测试因此不用重复写超时参数。
调用图:调用 1 个内部函数(shell_responses_with_timeout);被 1 处调用(mount_shell_responses)。
shell_command_harness_with60–65 ↗
async fn shell_command_harness_with(
configure: impl FnOnce(TestCodexBuilder) -> TestCodexBuilder,
) -> Result<TestCodexHarness>
作用:创建一个可配置的 Codex 测试环境。测试用它来启动假服务器、客户端和必要的上下文,好像真的跑了一次助手会话。
数据流:输入是一个配置函数,它会拿到默认的 TestCodexBuilder 并修改它,比如指定模型名。函数先创建默认 builder,再应用配置,最后异步启动 TestCodexHarness;输出是可用于提交消息和读取结果的测试工具箱。
调用关系:几乎每个测试开头都会调用它。它调用 test_codex 取得默认配置,再调用 with_builder 建出 harness;后续测试会把这个 harness 交给挂载回复和提交请求的步骤。
调用图:调用 2 个内部函数(with_builder, test_codex);被 9 处调用(multi_line_output_with_login, output_with_login, output_without_login, pipe_output_with_login, pipe_output_without_login, shell_command_times_out_with_timeout_ms, shell_command_works, unicode_output, unicode_output_with_newlines)。
mount_shell_responses67–74 ↗
async fn mount_shell_responses(
harness: &TestCodexHarness,
call_id: &str,
command: &str,
login: Option<bool>,
)
作用:把一组默认超时的假模型回复装到测试服务器上。这样系统收到用户请求时,就会按预设路线去调用 shell_command。
数据流:输入是测试环境、调用编号、命令文本和登录 shell 选项。它先用 shell_responses 造出假 SSE 回复,再从 harness 取出服务器,最后把回复序列挂上去;结果是服务器之后会按这些事件回应。
调用关系:它服务于普通成功类测试。shell_command_works、登录开关、多行输出和管道输出测试都会先调用它,再让 harness.submit 触发整条流程。
调用图:调用 3 个内部函数(mount_sse_sequence, server, shell_responses);被 6 处调用(multi_line_output_with_login, output_with_login, output_without_login, pipe_output_with_login, pipe_output_without_login, shell_command_works)。
mount_shell_responses_with_timeout76–88 ↗
async fn mount_shell_responses_with_timeout(
harness: &TestCodexHarness,
call_id: &str,
command: &str,
login: Option<bool>,
timeout: Duration,
)
作用:把带自定义超时时间的假模型回复装到测试服务器上。需要测试超时或较慢命令时,会用这个版本。
数据流:输入是测试环境、调用编号、命令、登录 shell 选项和 Duration 时间对象。它把时间转成毫秒,生成带 timeout_ms 的 SSE 回复,再挂到 harness 的服务器上;之后服务器会要求系统按这个超时执行命令。
调用关系:它被超时测试和 Unicode 输出测试使用。它把具体事件生成工作交给 shell_responses_with_timeout,把挂载工作交给 mount_sse_sequence。
调用图:调用 3 个内部函数(mount_sse_sequence, server, shell_responses_with_timeout);被 3 处调用(shell_command_times_out_with_timeout_ms, unicode_output, unicode_output_with_newlines);外部调用 1 个(as_millis)。
assert_shell_command_output90–103 ↗
fn assert_shell_command_output(output: &str, expected: &str) -> Result<()>
作用:检查 shell_command 的输出是否符合预期格式,并且命令内容正确。它不只看文本,还确认退出码、耗时行和 Output 标题都在。
数据流:输入是实际输出和期望看到的命令输出。它先把 Windows 和 Unix 的换行统一成 \n,再去掉末尾多余换行,然后用正则表达式匹配“Exit code: 0、Wall time、Output、期望文本”这套格式;成功返回 Ok,失败则让测试报错。
调用关系:多数成功测试最后都会调用它。它把格式检查集中在一处,避免每个测试重复写同样的判断,也让输出格式一变就能统一暴露问题。
调用图:被 8 处调用(multi_line_output_with_login, output_with_login, output_without_login, pipe_output_with_login, pipe_output_without_login, shell_command_works, unicode_output, unicode_output_with_newlines);外部调用 2 个(assert_regex_match, format!)。
shell_command_works106–125 ↗
async fn shell_command_works() -> anyhow::Result<()>
作用:验证最基本的场景:模型要求执行 echo 命令时,系统真的能跑命令并拿到 hello, world。它是 shell_command 功能的冒烟测试,也就是先确认最核心路径没坏。
数据流:测试先在没有网络时跳过,然后创建测试环境,挂上会调用 echo 的假模型回复,提交“运行 echo 命令”的请求。之后它读取指定调用编号的标准输出,并用 assert_shell_command_output 确认输出是 hello, world。
调用关系:它串起了本文件的主要小工具:shell_command_harness_with 建环境,mount_shell_responses 放入假回复,harness.submit 触发流程,assert_shell_command_output 做最终验收。
调用图:调用 3 个内部函数(assert_shell_command_output, mount_shell_responses, shell_command_harness_with);外部调用 1 个(skip_if_no_network!)。
output_with_login128–141 ↗
async fn output_with_login() -> anyhow::Result<()>
作用:验证 login=true 时,shell_command 仍然能正常输出结果。login shell 可以理解成“像用户刚登录终端那样启动的 shell”,配置加载方式可能不同,所以需要单独测。
数据流:输入主要来自测试里写死的命令 echo 'hello, world' 和 login=true。测试创建环境、挂载假回复、提交请求,然后读取这次函数调用的输出;最后确认输出格式正确、内容是 hello, world。
调用关系:它复用 shell_command_harness_with、mount_shell_responses 和 assert_shell_command_output。它和 output_without_login 成对出现,用来比较登录 shell 开关两边都不坏。
调用图:调用 3 个内部函数(assert_shell_command_output, mount_shell_responses, shell_command_harness_with);外部调用 1 个(skip_if_no_network!)。
output_without_login144–157 ↗
async fn output_without_login() -> anyhow::Result<()>
作用:验证 login=false 时,shell_command 也能正常执行命令并返回输出。这个测试保证不使用登录 shell 的路径同样可用。
数据流:测试把 echo 命令和 login=false 放进假模型回复,启动测试环境并提交请求。系统执行后,测试读取标准输出,再确认它包含成功退出码、耗时信息和 hello, world。
调用关系:它和 output_with_login 一起覆盖登录 shell 的两个明确取值。它调用同一套挂载、提交和断言工具,只改变 login 参数。
调用图:调用 3 个内部函数(assert_shell_command_output, mount_shell_responses, shell_command_harness_with);外部调用 1 个(skip_if_no_network!)。
multi_line_output_with_login160–179 ↗
async fn multi_line_output_with_login() -> anyhow::Result<()>
作用:验证命令输出多行文本时不会被截断、合并或弄乱。多行输出很常见,比如日志和文件内容,所以这是一个重要场景。
数据流:测试准备一个 echo 命令,让它输出 first line 和 second line 两行,并设置 login=true。提交请求后,它取回函数调用的标准输出,用统一断言确认两行内容按顺序保留下来。
调用关系:它沿用普通成功路径的工具函数。和单行输出测试相比,它主要检查 assert_shell_command_output 和实际 shell_command 输出对换行的处理是否正确。
调用图:调用 3 个内部函数(assert_shell_command_output, mount_shell_responses, shell_command_harness_with);外部调用 1 个(skip_if_no_network!)。
pipe_output_with_login182–202 ↗
async fn pipe_output_with_login() -> anyhow::Result<()>
作用:验证带管道符的命令可以执行。管道符就是把前一个命令的输出交给后一个命令,比如 echo ... | cat。
数据流:测试在没有网络时跳过,在 Windows 上也跳过,因为这个命令形式按 Unix shell 来测。它创建环境,挂载带管道命令的假回复,提交请求,读取输出,最后确认仍然得到 hello, world。
调用关系:它使用 mount_shell_responses 走默认超时路径,并用 assert_shell_command_output 验收。它和 pipe_output_without_login 一起覆盖管道命令在不同登录设置下的表现。
调用图:调用 3 个内部函数(assert_shell_command_output, mount_shell_responses, shell_command_harness_with);外部调用 2 个(skip_if_no_network!, skip_if_windows!)。
pipe_output_without_login205–219 ↗
async fn pipe_output_without_login() -> anyhow::Result<()>
作用:验证 login=false 时,带管道符的命令仍然可以执行。这样可以发现非登录 shell 路径里解析管道命令的问题。
数据流:测试先跳过无网络和 Windows 环境,然后准备 echo 'hello, world' | cat,并把 login 设置为 false。提交请求后取回输出,确认系统成功执行且输出 hello, world。
调用关系:它和 pipe_output_with_login 是一组互补测试。两者都依赖 shell_command_harness_with 搭环境、mount_shell_responses 挂假回复、assert_shell_command_output 检查结果。
调用图:调用 3 个内部函数(assert_shell_command_output, mount_shell_responses, shell_command_harness_with);外部调用 2 个(skip_if_no_network!, skip_if_windows!)。
shell_command_times_out_with_timeout_ms222–254 ↗
async fn shell_command_times_out_with_timeout_ms() -> anyhow::Result<()>
作用:验证 timeout_ms 参数真的会让长时间运行的命令停止。没有这个测试,命令可能卡住测试或真实用户会话,像水龙头关不上。
数据流:测试按平台选择长命令:Windows 用 timeout /t 5,其他系统用 sleep 5。它挂载一个只有 200 毫秒超时的假回复,提交请求后读取输出;最后用正则确认退出码是 124,并提示命令在若干毫秒后超时。
调用关系:它走自定义超时路径,调用 mount_shell_responses_with_timeout 而不是默认挂载函数。最后它不用通用成功断言,因为这里期待的是超时失败格式,而不是 Exit code: 0。
调用图:调用 2 个内部函数(mount_shell_responses_with_timeout, shell_command_harness_with);外部调用 4 个(from_millis, cfg!, assert_regex_match, skip_if_no_network!)。
unicode_output262–284 ↗
async fn unicode_output(login: bool) -> anyhow::Result<()>
作用:验证 shell_command 能正确输出带重音符号的 Unicode 文本,比如 naïve_café。Unicode 是“世界各种文字和符号的统一编码”,处理不好就会乱码。
数据流:测试按平台选择命令:Windows 用 cmd.exe 输出文本,其他系统用 echo。它按参数分别测试 login=true 和 login=false,挂载较长超时的假回复,提交请求后读取输出,并确认文本没有乱码。
调用关系:它使用 test_case 跑两遍,覆盖登录和非登录两种 shell。它调用 mount_shell_responses_with_timeout 是为了给 Windows/PowerShell 这类环境更多时间,同时最后仍用 assert_shell_command_output 做成功格式检查。
调用图:调用 3 个内部函数(assert_shell_command_output, mount_shell_responses_with_timeout, shell_command_harness_with);外部调用 2 个(cfg!, skip_if_no_network!)。
unicode_output_with_newlines289–309 ↗
async fn unicode_output_with_newlines(login: bool) -> anyhow::Result<()>
作用:验证带 Unicode 字符的多行输出也能正确处理。它同时覆盖“非英文字符”和“换行”两个容易出错的点。
数据流:测试准备一个会输出 line1、naïve café、line3 的命令,并分别用 login=true 和 login=false 跑。它挂载带中等超时的假回复,提交请求后取回输出,确认结果里保留了预期的换行表示和 Unicode 字符。
调用关系:它和 unicode_output 类似,也是用 test_case 覆盖两种登录设置。它把假回复交给 mount_shell_responses_with_timeout 设置,最终交给 assert_shell_command_output 验证内容。
调用图:调用 3 个内部函数(assert_shell_command_output, mount_shell_responses_with_timeout, shell_command_harness_with);外部调用 1 个(skip_if_no_network!)。
core/tests/suite/unified_exec.rs源码 ↗
这里测试的是 unified exec,也就是把模型发来的 exec_command 和 write_stdin 统一变成真实终端/进程操作的功能。可以把它想成一个“远程帮手”:模型说“运行这条命令”,系统就启动进程;模型说“往上次那个进程里输入几行”,系统就继续操作同一个进程。这个文件搭了假服务器,伪装成模型返回工具调用,然后检查 Codex 发出的事件和回传给模型的工具结果。它覆盖了很多容易出事故的地方:apply_patch 要被特殊拦截,工作目录要算对,开始/结束事件只能发对,长时间运行的进程不能被误杀,输出太大要截断但不能丢关键信息,沙箱和网络权限要真的生效,TTY(像真实终端一样的输入输出环境)和普通管道要区分。辅助函数主要负责提交一次测试对话、解析工具输出、从请求日志里收集结果。
extract_output_text53–59 ↗
fn extract_output_text(item: &Value) -> Option<&str>
作用:从一段 JSON 工具结果里拿出真正的输出文字。它兼容两种格式:输出直接是字符串,或者藏在 output.content 里。
数据流:进去的是一个 JSON 项 → 它查看 output 字段,如果是字符串就直接取出,如果是对象就继续找 content → 出来的是可能存在的一段文本,不改动任何数据。
调用关系:它是 collect_tool_outputs 的小帮手;收集工具输出时先靠它把原始 JSON 里的文字抽出来,再交给解析函数理解里面的执行元数据。
调用图:被 1 处调用(collect_tool_outputs);外部调用 1 个(get)。
parse_unified_exec_output71–140 ↗
fn parse_unified_exec_output(raw: &str) -> Result<ParsedUnifiedExecOutput>
作用:把 unified exec 返回给模型的一整段文字拆成结构化信息,比如耗时、退出码、进程编号、输出内容。这样测试不用靠肉眼读字符串。
数据流:进去的是原始工具输出字符串 → 它用正则表达式(一种按模板匹配文字的工具)找出 Chunk ID、Wall time、exit code、session ID、token 数和 Output 段 → 出来的是 ParsedUnifiedExecOutput 结构,解析失败就返回错误。
调用关系:很多测试都要验证命令结果,它被 collect_tool_outputs、wait_for_raw_unified_exec_output 和少数直接读取输出的测试调用,是把“模型可见文本”翻译成“测试可断言数据”的关键入口。
调用图:被 4 处调用(collect_tool_outputs, unified_exec_prunes_exited_sessions_first, wait_for_raw_unified_exec_output, write_stdin_ctrl_c_reports_unsupported_interrupt_to_model_on_windows);外部调用 1 个(new)。
collect_tool_outputs142–166 ↗
fn collect_tool_outputs(bodies: &[Value]) -> Result<HashMap<String, ParsedUnifiedExecOutput>>
作用:从假模型服务器收到的所有请求体里,集中找出每次工具调用的输出,并按 call_id 建成一张表。测试后面就能按名字查结果。
数据流:进去的是一组 JSON 请求体 → 它逐个寻找 input 数组里的 function_call_output,拿 call_id 和输出文字,跳过空输出,再解析 unified exec 格式 → 出来的是 call_id 到 ParsedUnifiedExecOutput 的映射表。
调用关系:大量测试在回合结束后调用它检查工具回传内容;它内部先用 extract_output_text 取文本,再用 parse_unified_exec_output 解析。
调用图:调用 2 个内部函数(extract_output_text, parse_unified_exec_output);被 14 处调用(assert_write_stdin_ctrl_c_interrupts_non_tty_session, exec_command_reports_chunk_and_exit_metadata, unified_exec_can_enable_tty, unified_exec_defaults_to_pipe, unified_exec_enforces_glob_deny_read_policy, unified_exec_formats_large_output_summary, unified_exec_python_prompt_under_seatbelt, unified_exec_respects_early_exit_notifications, unified_exec_reuses_session_via_stdin, unified_exec_runs_on_all_platforms (+4 more));外部调用 1 个(new)。
wait_for_raw_unified_exec_output168–187 ↗
async fn wait_for_raw_unified_exec_output(
test: &TestCodex,
call_id: &str,
) -> Result<ParsedUnifiedExecOutput>
作用:等待事件流里出现某个工具调用的原始输出,并把它解析出来。适合不想等请求日志、而是直接监听运行中事件的测试。
数据流:进去的是测试环境和 call_id → 它一直等 RawResponseItem 事件里匹配这个 call_id 的 FunctionCallOutput → 出来的是解析后的 ParsedUnifiedExecOutput,解析失败则报错。
调用关系:输出截断策略相关测试会调用它;它把等待事件的活交给 wait_for_event_match,拿到文字后交给 parse_unified_exec_output。
调用图:调用 1 个内部函数(parse_unified_exec_output);被 2 处调用(exec_command_clamps_model_requested_max_output_tokens_to_policy, write_stdin_clamps_model_requested_max_output_tokens_to_policy);外部调用 1 个(wait_for_event_match)。
submit_unified_exec_turn189–225 ↗
async fn submit_unified_exec_turn(
test: &TestCodex,
prompt: &str,
permission_profile: PermissionProfile,
) -> Result<()>
作用:向测试里的 Codex 提交一次用户输入,并打开 unified exec 所需的权限、沙箱和模型设置。它减少了每个测试重复写一大段提交代码。
数据流:进去的是测试环境、提示词和权限配置 → 它根据当前工作目录算出沙箱策略和权限字段,组装 UserInput 操作并提交 → 出来是提交成功或失败的结果,同时 Codex 开始处理这一轮对话。
调用关系:绝大多数测试都靠它启动一次测试回合;它调用 turn_permission_fields 准备权限,然后把操作交给 codex.submit。
调用图:调用 1 个内部函数(turn_permission_fields);被 27 处调用(assert_write_stdin_ctrl_c_interrupts_non_tty_session, exec_command_clamps_model_requested_max_output_tokens_to_policy, exec_command_reports_chunk_and_exit_metadata, unified_exec_can_enable_tty, unified_exec_defaults_to_pipe, unified_exec_emits_end_event_when_session_dies_via_stdin, unified_exec_emits_exec_command_begin_event, unified_exec_emits_exec_command_end_event, unified_exec_emits_one_begin_and_one_end_event, unified_exec_emits_output_delta_for_exec_command (+15 more));外部调用 2 个(default, vec!)。
create_workspace_directory227–241 ↗
async fn create_workspace_directory(
test: &TestCodex,
rel_path: impl AsRef<std::path::Path>,
) -> Result<std::path::PathBuf>
作用:在测试工作区里创建一个目录,并返回它的真实路径。它用于测试 workdir,也就是命令应该在哪个目录里运行。
数据流:进去的是测试环境和相对路径 → 它把相对路径拼到测试工作目录下,转成路径 URI,再通过测试文件系统创建目录 → 出来的是创建好的绝对路径。
调用关系:工作目录相关测试调用它;它把磁盘创建动作交给 test.fs().create_directory。
调用图:调用 2 个内部函数(fs, from_path);被 2 处调用(unified_exec_resolves_relative_workdir, unified_exec_respects_workdir_override);外部调用 1 个(as_ref)。
unified_exec_intercepts_apply_patch_exec_command244–384 ↗
async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()>
作用:确认模型用 exec_command 调 apply_patch 时,系统会把它识别成补丁操作,而不是当普通 shell 命令直接跑。这样补丁事件和文件修改记录才准确。
数据流:进去的是假模型返回的一次 apply_patch 命令 → 测试提交用户输入,监听补丁开始/结束和命令开始/结束事件 → 出来是断言补丁成功、文件被创建、且没有普通 ExecCommand 事件。
调用关系:这是 apply_patch 特例的回归测试;它使用 mount_sse_sequence 准备假响应,用 turn_permission_fields 和 local_selections 配好环境。
调用图:调用 5 个内部函数(mount_sse_sequence, with_builder, local_selections, test_codex, turn_permission_fields);外部调用 10 个(default, assert!, assert_eq!, wait_for_event, format!, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, vec!)。
unified_exec_emits_exec_command_begin_event387–448 ↗
async fn unified_exec_emits_exec_command_begin_event() -> Result<()>
作用:确认启动命令时会发出 ExecCommandBegin 事件。这个事件让前端或日志知道“命令已经开始跑了”。
数据流:进去的是一条 echo 命令的假工具调用 → 测试提交回合并等待 begin 事件 → 出来是断言命令数组、工作目录都正确,最后等回合完成。
调用关系:它通过 submit_unified_exec_turn 启动流程,通过 assert_command 检查 bash 命令格式,并用 wait_for_event_match 抓指定事件。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, assert_command, submit_unified_exec_turn);外部调用 9 个(assert_eq!, wait_for_event, wait_for_event_match, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_resolves_relative_workdir451–521 ↗
async fn unified_exec_resolves_relative_workdir() -> Result<()>
作用:确认模型传相对工作目录时,系统会把它按当前工作区解析成正确的绝对目录。否则命令可能跑到意外位置。
数据流:进去的是一个相对目录名和 pwd 命令 → 测试先创建目录,再提交带 workdir 的工具调用 → 出来是 begin 事件里的 cwd 等于创建出来的目录。
调用关系:它依赖 create_workspace_directory 准备目录,使用 submit_unified_exec_turn 触发命令,再监听 ExecCommandBegin 验证结果。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, create_workspace_directory, submit_unified_exec_turn);外部调用 10 个(assert_eq!, wait_for_event, wait_for_event_match, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, from, vec!)。
unified_exec_respects_workdir_override524–586 ↗
async fn unified_exec_respects_workdir_override() -> Result<()>
作用:确认模型明确指定绝对 workdir 时,命令真的在那个目录里启动。这个测试防止系统忽略模型给的工作目录。
数据流:进去的是一个已创建的工作目录和 pwd 命令 → 系统执行工具调用 → 出来是 begin 事件显示 cwd 正是指定目录,并且假服务器确实收到请求。
调用关系:它和相对路径测试互补;同样使用 create_workspace_directory、mount_sse_sequence 和 submit_unified_exec_turn。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, create_workspace_directory, submit_unified_exec_turn);外部调用 9 个(assert!, assert_eq!, wait_for_event, wait_for_event_match, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, vec!)。
unified_exec_emits_exec_command_end_event589–661 ↗
async fn unified_exec_emits_exec_command_end_event() -> Result<()>
作用:确认命令结束时会发 ExecCommandEnd 事件,并带上退出码和汇总输出。没有这个事件,界面就不知道命令已经收尾。
数据流:进去的是 echo 命令和后续一次空轮询 → 测试等待结束事件 → 出来是断言退出码为 0,汇总输出包含 END-EVENT。
调用关系:它让假模型先调用 exec_command 再调用 write_stdin 轮询;通过 submit_unified_exec_turn 启动,通过 wait_for_event_match 等结束事件。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, submit_unified_exec_turn);外部调用 10 个(assert!, assert_eq!, wait_for_event, wait_for_event_match, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_emits_output_delta_for_exec_command664–721 ↗
async fn unified_exec_emits_output_delta_for_exec_command() -> Result<()>
作用:确认命令输出会通过事件流传出来。这样用户不用等整个命令完事,能看到实时输出片段。
数据流:进去的是一个 printf 命令 → 系统执行并发出结束事件 → 测试从事件里的 stdout 检查是否包含 HELLO-UEXEC。
调用关系:它用 submit_unified_exec_turn 启动工具调用,主要观察 ExecCommandEnd 里累积到的 stdout。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, submit_unified_exec_turn);外部调用 9 个(assert!, wait_for_event, wait_for_event_match, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_full_lifecycle_with_background_end_event724–818 ↗
async fn unified_exec_full_lifecycle_with_background_end_event() -> Result<()>
作用:测试一个稍慢的命令从开始、后台继续跑、到最终结束的完整生命周期。重点是结束事件即使来自后台观察者也不能丢。
数据流:进去的是 sleep 后输出标记的命令 → 测试循环收集 begin、end、TurnComplete → 出来是断言 begin/end 都有进程编号,退出码为 0,汇总输出包含完整标记。
调用关系:它覆盖长生命周期路径;submit_unified_exec_turn 触发,wait_for_event 负责持续消费事件直到回合和后台结束都出现。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, submit_unified_exec_turn);外部调用 9 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_network_denial_emits_failed_background_end_event821–863 ↗
async fn unified_exec_network_denial_emits_failed_background_end_event() -> Result<()>
作用:确认长一点的命令如果被网络沙箱拒绝,会发失败的结束事件。网络沙箱就是限制程序访问外网的一层保护。
数据流:进去的是会访问被拒绝域名的 Python 命令 → 测试用受限网络配置运行它 → 出来是结束事件状态 Failed、退出码 -1、输出里有网络拒绝信息。
调用关系:它用 unified_exec_network_denial_test 搭权限环境,用 mount_unified_exec_network_denial_responses 准备假响应,再用 wait_for_unified_exec_end 等待失败结束。
调用图:调用 5 个内部函数(start_mock_server, mount_unified_exec_network_denial_responses, submit_unified_exec_turn, unified_exec_network_denial_test, wait_for_unified_exec_end);外部调用 8 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!)。
unified_exec_short_lived_network_denial_emits_failed_end_event866–908 ↗
async fn unified_exec_short_lived_network_denial_emits_failed_end_event() -> Result<()>
作用:确认很快触发网络拒绝的短命令也会报告失败结束事件。这样失败不会因为进程很快退出而被漏掉。
数据流:进去的是立即尝试访问被拒绝网络的 Python 命令 → 系统运行并被沙箱拦下 → 出来是失败状态、退出码 -1、带网络拒绝文字和进程编号的结束事件。
调用关系:它和长命令网络拒绝测试共用 unified_exec_network_denial_test、mount_unified_exec_network_denial_responses 和 wait_for_unified_exec_end。
调用图:调用 5 个内部函数(start_mock_server, mount_unified_exec_network_denial_responses, submit_unified_exec_turn, unified_exec_network_denial_test, wait_for_unified_exec_end);外部调用 8 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!)。
unified_exec_network_denial_test910–960 ↗
async fn unified_exec_network_denial_test(
server: &wiremock::MockServer,
) -> Result<(TestCodex, PermissionProfile)>
作用:搭建专门用于网络拒绝测试的 Codex 环境。它写入一份权限配置,让网络走受控代理并可触发拒绝。
数据流:进去的是假服务器 → 它创建临时 home、写 config.toml、设置 workspace 权限和网络策略,启用 UnifiedExec 特性 → 出来是 TestCodex 和对应权限配置。
调用关系:两个网络拒绝测试都先调用它;它把环境搭建交给 test_codex,并用 managed_network_requirements_loader 提供网络代理需求。
调用图:调用 2 个内部函数(test_codex, workspace_write_with);被 2 处调用(unified_exec_network_denial_emits_failed_background_end_event, unified_exec_short_lived_network_denial_emits_failed_end_event);外部调用 5 个(new, new, assert!, managed_network_requirements_loader, write)。
mount_unified_exec_network_denial_responses962–980 ↗
async fn mount_unified_exec_network_denial_responses(
server: &wiremock::MockServer,
call_id: &str,
args: &Value,
) -> Result<core_test_support::responses::ResponseMock>
作用:为网络拒绝测试挂载一组标准假模型响应:先要求执行命令,再结束对话。
数据流:进去的是假服务器、call_id 和工具参数 → 它拼出两段 SSE 响应流 → 出来是 ResponseMock,可用来查看服务器后来收到了多少请求。
调用关系:网络拒绝两个测试都调用它;它内部把响应安装动作交给 mount_sse_sequence。
调用图:调用 1 个内部函数(mount_sse_sequence);被 2 处调用(unified_exec_network_denial_emits_failed_background_end_event, unified_exec_short_lived_network_denial_emits_failed_end_event);外部调用 1 个(vec!)。
wait_for_unified_exec_end982–1018 ↗
async fn wait_for_unified_exec_end(
test: &TestCodex,
call_id: &str,
response_mock: &core_test_support::responses::ResponseMock,
) -> (codex_protocol::protocol::ExecCommandEndEvent, bool)
作用:在有限时间内等待某个 unified exec 命令的结束事件,同时记录是否已经看到整轮对话完成。
数据流:进去的是测试环境、call_id 和响应记录器 → 它最多等 15 秒,不断读事件,记录已观察事件,找到匹配的 ExecCommandEnd 就返回 → 出来是结束事件和 TurnComplete 是否已出现。
调用关系:网络拒绝测试用它避免无限卡住;超时时它会把已看到事件和请求数量放进报错信息,方便排查。
调用图:被 2 处调用(unified_exec_network_denial_emits_failed_background_end_event, unified_exec_short_lived_network_denial_emits_failed_end_event);外部调用 7 个(new, format!, matches!, panic!, from_secs, now, timeout)。
unified_exec_emits_terminal_interaction_for_write_stdin1021–1103 ↗
async fn unified_exec_emits_terminal_interaction_for_write_stdin() -> Result<()>
作用:确认对正在运行的终端进程写入 stdin 时,会发 TerminalInteraction 事件。stdin 就是程序的输入通道。
数据流:进去的是先开 bash、再输入 echo 命令的两次工具调用 → 测试监听 TerminalInteraction → 出来是断言事件里的进程编号和输入内容正确。
调用关系:它验证 write_stdin 的可观察性;通过 submit_unified_exec_turn 触发整个交互流程。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, submit_unified_exec_turn);外部调用 8 个(assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_terminal_interaction_captures_delayed_output1106–1284 ↗
async fn unified_exec_terminal_interaction_captures_delayed_output() -> Result<()>
作用:确认终端进程延迟输出时,多次轮询也能捕获后续输出,不会只看到开头。适合测试慢程序。
数据流:进去的是一个分两次延迟打印 MARKER 的命令和三次 write_stdin 轮询 → 测试收集 begin、输出片段、交互事件和 end → 出来是断言三个交互都出现,两个标记都在流式输出和汇总输出里。
调用关系:它综合测试 exec_command、write_stdin、ExecCommandOutputDelta、TerminalInteraction 和 ExecCommandEnd 的配合。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, submit_unified_exec_turn);外部调用 12 个(from_utf8_lossy, new, new, assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows! (+2 more))。
unified_exec_emits_one_begin_and_one_end_event1287–1407 ↗
async fn unified_exec_emits_one_begin_and_one_end_event() -> Result<()>
作用:确认一个短会话只发一次开始和一次结束事件,空轮询不会制造多余交互事件。这样前端不会显示重复命令。
数据流:进去的是启动 sleep 和一次空 write_stdin 轮询 → 测试统计 begin、end、terminal interaction 数量 → 出来是 begin=1、end=1、terminal interaction=0,并检查命令来源。
调用关系:它用 assert_command 检查启动命令格式,用 submit_unified_exec_turn 触发事件流。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, assert_command, submit_unified_exec_turn);外部调用 9 个(new, assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, vec!)。
exec_command_reports_chunk_and_exit_metadata1410–1502 ↗
async fn exec_command_reports_chunk_and_exit_metadata() -> Result<()>
作用:确认普通 exec_command 的工具输出里包含必要元数据:块编号、耗时、退出码、截断信息等。元数据让模型和测试都能理解这次命令发生了什么。
数据流:进去的是一个会产生较多文字并限制输出 token 的命令 → 回合结束后收集工具输出 → 出来是断言 chunk_id 是十六进制、耗时非负、退出码 0、输出被截断并记录原 token 数。
调用关系:它使用 collect_tool_outputs 解析请求日志,是统一输出格式的核心覆盖测试。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 9 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
exec_command_clamps_model_requested_max_output_tokens_to_policy1505–1566 ↗
async fn exec_command_clamps_model_requested_max_output_tokens_to_policy() -> Result<()>
作用:确认模型请求特别大的输出上限时,系统会按配置里的安全上限压住。这样模型不能绕过输出限额把海量内容塞回来。
数据流:进去的是 999 行输出、模型要求 70000 token、配置只允许 50 token → 系统运行并截断输出 → 出来是原 token 数和截断格式符合预期。
调用关系:它通过 wait_for_raw_unified_exec_output 直接等工具输出,用 assert_regex_match 检查截断文本形状。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, submit_unified_exec_turn, wait_for_raw_unified_exec_output);外部调用 9 个(assert_eq!, assert_regex_match, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
write_stdin_clamps_model_requested_max_output_tokens_to_policy1569–1657 ↗
async fn write_stdin_clamps_model_requested_max_output_tokens_to_policy() -> Result<()>
作用:确认 write_stdin 产生的大量输出也会被同样的策略限住。不能只限制首次命令,后续输入也要受控。
数据流:进去的是先启动等待输入的 TTY 进程,再发 go 触发 999 行输出 → 系统按策略截断 → 出来是断言原 token 数和截断文本符合预期。
调用关系:它先用 wait_for_raw_unified_exec_output 确认启动命令留下进程,再对 stdin 输出做同样检查。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, submit_unified_exec_turn, wait_for_raw_unified_exec_output);外部调用 10 个(assert!, assert_eq!, assert_regex_match, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_defaults_to_pipe1660–1728 ↗
async fn unified_exec_defaults_to_pipe() -> Result<()>
作用:确认默认情况下 unified exec 使用普通管道,而不是 TTY。普通管道更适合非交互命令,也更可预测。
数据流:进去的是检查 sys.stdin.isatty() 的 Python 命令 → 系统默认运行 → 出来是输出包含 False,退出码为 0。
调用关系:它通过 collect_tool_outputs 从请求日志里拿结果;和 unified_exec_can_enable_tty 形成对照。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 9 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_can_enable_tty1731–1796 ↗
async fn unified_exec_can_enable_tty() -> Result<()>
作用:确认模型传 tty=true 时,命令会运行在像真实终端一样的环境里。交互程序通常需要这种模式。
数据流:进去的是同样检查 stdin 是否为 TTY 的 Python 命令,但参数带 tty=true → 系统运行 → 出来是输出包含 True、退出码 0、进程已退出。
调用关系:它和默认管道测试一起证明 tty 参数确实生效,使用 collect_tool_outputs 检查工具结果。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 9 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_respects_early_exit_notifications1799–1881 ↗
async fn unified_exec_respects_early_exit_notifications() -> Result<()>
作用:确认短命令提前结束时,不会傻等完整 yield_time_ms。yield_time_ms 是模型愿意等输出的毫秒数。
数据流:进去的是 sleep 0.05 但 yield_time_ms 很大的命令 → 系统运行并收到提前退出通知 → 出来是没有进程编号、退出码 0、耗时小于 0.75 秒、输出为空。
调用关系:它用 collect_tool_outputs 检查最终工具输出,验证执行层能及时感知进程结束。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 9 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
write_stdin_returns_exit_metadata_and_clears_session1884–2036 ↗
async fn write_stdin_returns_exit_metadata_and_clears_session() -> Result<()>
作用:确认给交互进程发送 EOF 让它退出后,write_stdin 会返回退出码,并从会话表里清掉这个进程。EOF 可以理解成“输入结束”。
数据流:进去的是启动 cat、发送一行文字、再发送 EOF 的三步调用 → 系统先保留进程,再回显文字,最后退出并清理 → 出来是启动/发送阶段有 process_id,退出阶段无 process_id 但有退出码 0 和 chunk_id。
调用关系:它用 collect_tool_outputs 检查三次工具调用结果,覆盖会话复用和清理流程。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 9 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
write_stdin_ctrl_c_interrupts_non_tty_session2039–2049 ↗
async fn write_stdin_ctrl_c_interrupts_non_tty_session() -> Result<()>
作用:测试非 TTY 会话收到 Ctrl+C 时,能触发程序自定义的中断处理。Ctrl+C 在 Unix 上通常会发送 SIGINT 中断信号。
数据流:进去的是带 trap 的 shell 命令、期望退出码 42 和中断输出 INT-TRAP → 它把具体测试交给公共断言函数 → 出来是该场景通过或失败。
调用关系:这是 assert_write_stdin_ctrl_c_interrupts_non_tty_session 的一个包装用例,专门测自定义 SIGINT trap。
调用图:调用 1 个内部函数(assert_write_stdin_ctrl_c_interrupts_non_tty_session);外部调用 1 个(skip_if_wine_exec!)。
write_stdin_ctrl_c_default_interrupt_reports_130_for_non_tty_session2052–2062 ↗
async fn write_stdin_ctrl_c_default_interrupt_reports_130_for_non_tty_session() -> Result<()>
作用:测试非 TTY 会话默认收到 Ctrl+C 时,会以常见的 130 退出码报告。这样模型能知道进程是被中断的。
数据流:进去的是普通 sleep 命令和期望退出码 130 → 它调用公共断言函数执行完整流程 → 出来是确认没有自定义中断输出也能正确报告退出。
调用关系:它和 trap 版本共享 assert_write_stdin_ctrl_c_interrupts_non_tty_session,只换命令和期望。
调用图:调用 1 个内部函数(assert_write_stdin_ctrl_c_interrupts_non_tty_session);外部调用 1 个(skip_if_wine_exec!)。
assert_write_stdin_ctrl_c_interrupts_non_tty_session2064–2184 ↗
async fn assert_write_stdin_ctrl_c_interrupts_non_tty_session(
test_name: &str,
command: &str,
expected_exit_code: i32,
expected_interrupt_output: Option<&str>,
) -> Result<()>
作用:公共测试逻辑:启动一个非 TTY 长运行进程,然后通过 write_stdin 发送 Ctrl+C,并检查退出码和输出。避免两个 Ctrl+C 测试复制代码。
数据流:进去的是测试名、命令、期望退出码和可选期望输出 → 它搭假响应、提交回合、收集工具输出 → 出来是断言启动时进程还活着,Ctrl+C 后进程被清理且退出码符合预期。
调用关系:两个非 Windows Ctrl+C 测试都调用它;它内部使用 submit_unified_exec_turn 和 collect_tool_outputs 完成流程。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);被 2 处调用(write_stdin_ctrl_c_default_interrupt_reports_130_for_non_tty_session, write_stdin_ctrl_c_interrupts_non_tty_session);外部调用 9 个(assert!, assert_eq!, wait_for_event, format!, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, vec!)。
write_stdin_ctrl_c_reports_unsupported_interrupt_to_model_on_windows2188–2284 ↗
async fn write_stdin_ctrl_c_reports_unsupported_interrupt_to_model_on_windows() -> Result<()>
作用:Windows 专用测试:确认当前后端不支持某种进程中断时,会把清楚的失败信息返回给模型,而不是假装成功。
数据流:进去的是 Windows cmd 长命令和 Ctrl+C 输入 → 系统尝试中断 → 出来是启动输出有进程编号和 READY,随后 write_stdin 输出包含失败和“不支持中断”的说明。
调用关系:它直接从 request_log 取工具输出,并用 parse_unified_exec_output 解析启动结果;只在 Windows 配置下运行。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, parse_unified_exec_output, submit_unified_exec_turn);外部调用 7 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, vec!)。
unified_exec_emits_end_event_when_session_dies_via_stdin2287–2378 ↗
async fn unified_exec_emits_end_event_when_session_dies_via_stdin() -> Result<()>
作用:确认一个会话因为 write_stdin 发送 EOF 而退出时,结束事件仍然归到最初启动它的 exec_command call_id 上。
数据流:进去的是启动 cat、发送 bye、再发送 EOF 的调用序列 → 系统让会话退出 → 出来是等到 start_call_id 对应的 ExecCommandEnd,退出码为 0。
调用关系:它测试事件归属关系;虽然退出由 write_stdin 触发,但 wait_for_event_match 期待的是初始调用的结束事件。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, submit_unified_exec_turn);外部调用 9 个(assert_eq!, wait_for_event, wait_for_event_match, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_keeps_long_running_session_after_turn_end2381–2488 ↗
async fn unified_exec_keeps_long_running_session_after_turn_end() -> Result<()>
作用:确认长时间运行的 unified exec 进程在一轮对话结束后仍然活着,直到系统关机时才被清理。这样模型下一轮还有机会继续操作它。
数据流:进去的是写入 pid 文件后 sleep 很久的命令 → 回合完成后测试读取 pid 并检查进程仍在 → 再提交 Shutdown → 出来是进程最终退出。
调用关系:它不用通用 submit_unified_exec_turn,而是手动提交带本地环境的 UserInput;用 wait_for_pid_file、process_is_alive、wait_for_process_exit 检查真实进程生命周期。
调用图:调用 7 个内部函数(wait_for_pid_file, wait_for_process_exit, mount_sse_sequence, start_mock_server, local_selections, test_codex, turn_permission_fields);外部调用 11 个(default, assert!, wait_for_event, wait_for_event_match, format!, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, tempdir (+1 more))。
unified_exec_interrupt_preserves_long_running_session2491–2586 ↗
async fn unified_exec_interrupt_preserves_long_running_session() -> Result<()>
作用:确认用户中断当前回合时,不会顺手杀掉已经启动的长运行 unified exec 进程。中断只是停掉这轮对话,不等于清理后台终端。
数据流:进去的是一个长 sleep 命令 → 测试等进程启动并写 pid,然后提交 Interrupt → 出来是 TurnAborted 后进程仍活着,最后 CleanBackgroundTerminals 才让它退出。
调用关系:它手动提交 UserInput 和 Interrupt/CleanBackgroundTerminals 操作,验证回合控制和后台进程清理是分开的。
调用图:调用 7 个内部函数(wait_for_pid_file, wait_for_process_exit, mount_sse_sequence, start_mock_server, local_selections, test_codex, turn_permission_fields);外部调用 11 个(default, assert!, wait_for_event, wait_for_event_match, format!, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, tempdir (+1 more))。
unified_exec_reuses_session_via_stdin2589–2686 ↗
async fn unified_exec_reuses_session_via_stdin() -> Result<()>
作用:确认 write_stdin 能复用 exec_command 创建的会话,而不是每次都开新进程。交互式程序必须靠这个能力连续对话。
数据流:进去的是启动 cat 和随后写入一行文字 → 系统返回同一个 process_id,并回显输入 → 出来是断言两次工具输出里的进程编号一致且包含回显。
调用关系:它使用 collect_tool_outputs 检查请求日志,是基础会话复用测试。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 9 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_streams_after_lagged_output2689–2804 ↗
async fn unified_exec_streams_after_lagged_output() -> Result<()>
作用:确认前面有大量滞后输出、触发截断后,后续尾部输出仍能被轮询拿到。避免大输出把后面的重要信息堵住。
数据流:进去的是先吐大量字节、再慢慢打印 TAIL-MARKER 的脚本,以及一次后续空轮询 → 系统先建立会话,再轮询输出 → 出来是第二次输出里包含 TAIL-MARKER。
调用关系:它使用 wait_for_event_with_timeout 给 CI 更长等待时间,再用 collect_tool_outputs 检查两次调用结果。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 8 个(assert!, wait_for_event_with_timeout, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_timeout_and_followup_poll2807–2893 ↗
async fn unified_exec_timeout_and_followup_poll() -> Result<()>
作用:确认命令在短等待时间内没结束时,会留下会话,后续 write_stdin 轮询能拿到后来出现的输出。
数据流:进去的是 sleep 后 echo ready 的命令,第一次只等 10 毫秒,再第二次轮询 → 第一次输出为空但有 process_id,第二次拿到 ready → 出来是对应断言通过。
调用关系:它测试 yield_time_ms 超时后的正常续接,使用 collect_tool_outputs 查看两次工具输出。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 8 个(assert!, matches!, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_formats_large_output_summary2898–2971 ↗
async fn unified_exec_formats_large_output_summary() -> Result<()>
作用:确认超大输出被截断时,格式里有警告、总行数、前后片段和省略 token 数。这样模型既不会被淹没,也能知道内容被省略了。
数据流:进去的是打印大量 token 的 Python 脚本和 max_output_tokens=100 → 系统截断输出 → 出来是断言输出匹配预期摘要格式,并记录原 token 数。
调用关系:它通过 collect_tool_outputs 解析结果,用 assert_regex_match 检查摘要形状。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 9 个(assert!, assert_regex_match, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, skip_if_wine_exec!, vec!)。
unified_exec_runs_under_sandbox2974–3060 ↗
async fn unified_exec_runs_under_sandbox() -> Result<()>
作用:确认 unified exec 在只读沙箱下仍能运行允许的简单命令。沙箱是限制命令能读写什么的保护层。
数据流:进去的是 read_only 权限下的 echo 命令 → 系统在受限环境里执行 → 出来是工具输出包含 hello。
调用关系:它手动提交带 local_selections 和 turn_permission_fields 的请求,随后用 collect_tool_outputs 检查结果。
调用图:调用 7 个内部函数(mount_sse_sequence, start_mock_server, local_selections, test_codex, turn_permission_fields, collect_tool_outputs, read_only);外部调用 9 个(default, assert!, assert_regex_match, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, vec!)。
unified_exec_enforces_glob_deny_read_policy3064–3201 ↗
async fn unified_exec_enforces_glob_deny_read_policy() -> Result<()>
作用:确认按通配符禁止读取的文件真的不会被 unified exec 读出。通配符规则像“所有 .env 文件都禁止读”。
数据流:进去的是一个被禁止的 secret.env 和一个允许的 notes.txt,以及 cat 两个文件的命令 → 沙箱阻止 secret.env,允许 notes.txt → 出来是退出码非零、输出含允许内容、不含秘密内容,并有权限拒绝提示。
调用关系:它只在 Unix 下运行,自己配置文件系统沙箱规则,再通过 collect_tool_outputs 验证没有泄漏秘密。
调用图:调用 7 个内部函数(mount_sse_sequence, start_mock_server, local_selections, test_codex, turn_permission_fields, collect_tool_outputs, read_only);外部调用 10 个(default, assert!, wait_for_event, format!, create_dir_all, write, json!, skip_if_no_network!, skip_if_sandbox!, vec!)。
unified_exec_python_prompt_under_seatbelt3205–3343 ↗
async fn unified_exec_python_prompt_under_seatbelt() -> Result<()>
作用:macOS 专用测试:确认在 Seatbelt 沙箱下启动交互式 Python 仍能显示提示符并接收退出命令。Seatbelt 是 macOS 的沙箱机制。
数据流:进去的是 python -i 的 TTY 启动命令和后续 exit() 输入 → 系统在只读权限下运行 → 出来是启动输出包含 >>> 提示符、进程保持为 1000,退出输出退出码为 0。
调用关系:它先查找 python 可执行文件,再手动提交带沙箱权限的回合,最后用 collect_tool_outputs 检查启动和退出两段结果。
调用图:调用 7 个内部函数(mount_sse_sequence, start_mock_server, local_selections, test_codex, turn_permission_fields, collect_tool_outputs, read_only);外部调用 9 个(default, assert!, assert_eq!, wait_for_event, eprintln!, json!, skip_if_no_network!, vec!, which)。
unified_exec_runs_on_all_platforms3346–3404 ↗
async fn unified_exec_runs_on_all_platforms() -> Result<()>
作用:确认最基本的 unified exec 命令能跨平台运行。这里用较宽松的匹配,因为 Windows 输出可能带控制字符。
数据流:进去的是 echo 'hello crossplat' 命令 → 系统运行并完成回合 → 出来是输出里能匹配到 hello crossplat。
调用关系:它使用 submit_unified_exec_turn 和 collect_tool_outputs,是跨平台冒烟测试,表示最小功能可用。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, collect_tool_outputs, submit_unified_exec_turn);外部调用 8 个(assert!, assert_regex_match, wait_for_event, json!, skip_if_no_network!, skip_if_sandbox!, skip_if_wine_exec!, vec!)。
unified_exec_prunes_exited_sessions_first3408–3552 ↗
async fn unified_exec_prunes_exited_sessions_first() -> Result<()>
作用:这是一个被忽略的测试,用来验证会话太多需要清理时,系统优先删掉已经退出的会话,而不是还活着的会话。
数据流:进去的是一个应保留的 cat 会话、一个会退出的 sleep 会话、许多填充会话和两次后续写入 → 系统触发会话裁剪 → 出来是保留会话仍能写入,被裁剪会话再写入会报未知进程。
调用关系:它构造很多 ev_function_call 填满会话缓存,使用 parse_unified_exec_output 逐项解析请求日志;当前标记 ignore,通常不会自动跑。
调用图:调用 8 个内部函数(ev_completed, ev_function_call, mount_sse_sequence, sse, start_mock_server, test_codex, parse_unified_exec_output, submit_unified_exec_turn);外部调用 9 个(assert!, wait_for_event, format!, json!, to_string, skip_if_no_network!, skip_if_sandbox!, skip_if_windows!, vec!)。
assert_command3554–3566 ↗
fn assert_command(command: &[String], expected_args: &str, expected_cmd: &str)
作用:检查事件里记录的 shell 命令形状是否符合预期:三段参数、第一段是 bash、第二段是 -lc、第三段是真正命令。
数据流:进去的是命令数组、期望的 shell 参数和期望命令文本 → 它逐项断言长度、bash 路径和两个参数 → 没有返回值,失败就让测试失败。
调用关系:begin 事件相关测试调用它,避免每个测试重复写 bash 命令格式检查。
调用图:被 2 处调用(unified_exec_emits_exec_command_begin_event, unified_exec_emits_one_begin_and_one_end_event);外部调用 2 个(assert!, assert_eq!)。
core/tests/suite/apply_patch_cli.rs源码 ↗
apply_patch 的作用是让模型用一段补丁文字去改项目文件,听起来简单,但风险很高:路径可能逃出项目目录,符号链接可能指到外面,补丁可能只改到一半就失败,还要把改动摘要发给前端看。这个测试文件就是给这些风险“把关”。它先搭一个假的 Codex 会话和假的模型服务器,让模型按测试要求发出 apply_patch 调用;再检查磁盘上的文件、工具输出、事件流和统一 diff(一种展示文件前后差异的文本格式)是否符合预期。文件里有一些辅助函数负责创建测试环境、提交用户输入、安装模拟响应、设置沙箱权限。后面的大量测试函数则像一条条安全用例:正常补丁要成功,坏补丁要报清楚错误,失败不能留下半成品,越权路径和软链接逃逸要被挡住,多个环境和多次工具调用的 diff 也要正确合并。
apply_patch_harness67–69 ↗
async fn apply_patch_harness() -> Result<TestCodexHarness>
作用:创建一个默认的 apply_patch 测试环境。测试用它拿到临时工作区、假服务器和 Codex 会话,省得每个测试重复搭台子。
数据流:进去没有额外配置 → 它把默认测试构建器交给 apply_patch_harness_with → 出来一个可用的 TestCodexHarness,后续测试可以写文件、提交请求、读取结果。
调用关系:这是很多测试的入口小工具。它不自己搭全部细节,而是把活交给 apply_patch_harness_with,所以需要特殊配置的测试会直接调用后者。
调用图:调用 1 个内部函数(apply_patch_harness_with);被 28 处调用(apply_patch_aggregates_diff_across_multiple_tool_calls, apply_patch_aggregates_diff_preserves_success_after_failure, apply_patch_change_context_disambiguates_target, apply_patch_cli_add_overwrites_existing_file, apply_patch_cli_delete_directory_reports_verification_error, apply_patch_cli_delete_missing_file_reports_error, apply_patch_cli_end_of_file_anchor, apply_patch_cli_insert_only_hunk_modifies_file, apply_patch_cli_missing_second_chunk_context_rejected, apply_patch_cli_move_overwrites_existing_destination (+15 more))。
apply_patch_harness_with71–78 ↗
async fn apply_patch_harness_with(
configure: impl FnOnce(TestCodexBuilder) -> TestCodexBuilder,
) -> Result<TestCodexHarness>
作用:创建一个可定制的 apply_patch 测试环境。测试可以在这里改模型名、工作目录、功能开关或预置文件。
数据流:进去一个“修改构建器”的闭包 → 它先拿默认 test_codex 构建器,再套上调用者的配置,最后启动带远程环境能力的测试 harness → 出来完整测试环境。
调用关系:它是 apply_patch_harness 背后的真正搭建者,也被需要特殊环境的测试直接使用,比如测试嵌套目录、远程环境、流式事件或 Windows shell。
调用图:调用 2 个内部函数(with_remote_env_builder, test_codex);被 11 处调用(apply_patch_clears_aggregated_diff_after_inexact_delta, apply_patch_cli_can_use_shell_command_output_as_patch_input, apply_patch_cli_does_not_write_through_symlink_escape_outside_workspace, apply_patch_cli_multiple_operations_integration, apply_patch_cli_preserves_existing_hard_link_outside_workspace, apply_patch_custom_tool_streaming_emits_updated_changes, apply_patch_harness, apply_patch_shell_command_failure_propagates_error_and_skips_diff, apply_patch_shell_command_heredoc_with_cd_emits_turn_diff, apply_patch_shell_command_heredoc_with_cd_updates_relative_workdir (+1 more));外部调用 1 个(pin)。
submit_without_wait80–88 ↗
async fn submit_without_wait(harness: &TestCodexHarness, prompt: &str) -> Result<()>
作用:向测试中的 Codex 提交一句用户话,但不等整个回合结束。这样测试可以一边让任务运行,一边监听中间事件。
数据流:进去 harness 和提示词 → 它使用默认的危险全访问沙箱策略,并不传额外权限配置 → 交给 submit_without_wait_with_turn_permissions 发送,结果是请求已提交。
调用关系:需要检查事件流的测试会用它,例如等待 TurnDiff 或 PatchApplyBegin。它只是简化包装,真正提交逻辑在 submit_without_wait_with_turn_permissions。
调用图:调用 1 个内部函数(submit_without_wait_with_turn_permissions);被 9 处调用(apply_patch_aggregates_diff_across_multiple_tool_calls, apply_patch_aggregates_diff_preserves_success_after_failure, apply_patch_clears_aggregated_diff_after_inexact_delta, apply_patch_cli_move_without_content_change_has_no_turn_diff, apply_patch_custom_tool_streaming_emits_updated_changes, apply_patch_emits_turn_diff_event_with_unified_diff, apply_patch_shell_command_failure_propagates_error_and_skips_diff, apply_patch_shell_command_heredoc_with_cd_emits_turn_diff, apply_patch_turn_diff_paths_stay_repo_relative_when_session_cwd_is_nested)。
submit_without_wait_with_turn_permissions90–124 ↗
async fn submit_without_wait_with_turn_permissions(
harness: &TestCodexHarness,
prompt: &str,
sandbox_policy: SandboxPolicy,
permission_profile: Option<PermissionProfile>,
) -> Result<
作用:按指定权限提交一次用户输入。它让测试能精确模拟“本回合允许读写哪里、要不要审批”的情况。
数据流:进去 harness、提示词、沙箱策略和可选权限档案 → 它读取当前会话模型,把提示词包装成 Op::UserInput,并设置永不请求审批、沙箱和协作模式 → 出来是提交成功或错误。
调用关系:submit_without_wait 调用它。它直接跟测试里的 codex.submit 打交道,是那些需要边跑边监听事件测试的提交通道。
调用图:调用 1 个内部函数(test);被 1 处调用(submit_without_wait);外部调用 2 个(default, vec!)。
restrictive_workspace_write_profile126–133 ↗
fn restrictive_workspace_write_profile() -> PermissionProfile
作用:生成一个很严格的“只能写项目内、网络受限”的权限档案。用来测试 apply_patch 不能把文件写到工作区外面。
数据流:进去没有参数 → 它调用 workspace_write_with,关闭 tmp 目录等宽松例外,并限制网络 → 出来一个 PermissionProfile。
调用关系:路径穿越测试会用它,例如写 ../escape.txt 或移动到 ../escape-move.txt,借此确认越界写入会被拒绝。
调用图:调用 1 个内部函数(workspace_write_with);被 2 处调用(apply_patch_cli_rejects_move_path_traversal_outside_workspace, apply_patch_cli_rejects_path_traversal_outside_workspace)。
workspace_write_with_read_only_root135–154 ↗
fn workspace_write_with_read_only_root(read_only_root: AbsolutePathBuf) -> PermissionProfile
作用:生成一个权限档案:项目根可以写,但指定的外部目录只能读。它用来测试软链接、硬链接指向外面时会怎样。
数据流:进去一个只读目录路径 → 它构造文件系统沙箱规则:该路径只读,项目根可写,网络受限 → 出来一个 PermissionProfile。
调用关系:软链接逃逸和硬链接语义测试会调用它。它提供“外面不能写”的边界,让测试能看 apply_patch 是否尊重这个边界。
调用图:调用 2 个内部函数(from_runtime_permissions, restricted);被 2 处调用(apply_patch_cli_does_not_write_through_symlink_escape_outside_workspace, apply_patch_cli_preserves_existing_hard_link_outside_workspace);外部调用 1 个(vec!)。
workspace_write_with_unreadable_path157–176 ↗
fn workspace_write_with_unreadable_path(unreadable_path: AbsolutePathBuf) -> PermissionProfile
作用:生成一个权限档案:项目根可写,但某个指定路径完全不可读。它用于验证补丁校验阶段也在沙箱里运行。
数据流:进去一个不可读路径 → 它构造规则:该路径拒绝访问,项目根可写,网络受限 → 出来一个 PermissionProfile。
调用关系:intercepted_apply_patch_verification_uses_local_sandbox 用它制造“软链接指向禁止读取文件”的场景,检查校验不会偷偷绕过沙箱。
调用图:调用 2 个内部函数(from_runtime_permissions, restricted);被 1 处调用(intercepted_apply_patch_verification_uses_local_sandbox);外部调用 1 个(vec!)。
create_file_symlink189–194 ↗
fn create_file_symlink(_source: &std::path::Path, _link: &std::path::Path) -> std::io::Result<()>
作用:创建文件软链接。软链接可以理解成快捷方式,测试用它模拟“项目内路径其实指向项目外文件”的危险情况。
数据流:进去源文件路径和链接路径 → 在 Unix 上调用 symlink,在 Windows 上调用 symlink_file,不支持的平台返回错误 → 出来是创建成功或系统错误。
调用关系:软链接相关测试调用它。它把平台差异藏起来,让测试重点放在 apply_patch 是否会越界写入或读取。
调用图:被 2 处调用(apply_patch_cli_does_not_write_through_symlink_escape_outside_workspace, intercepted_apply_patch_verification_uses_local_sandbox);外部调用 3 个(new, symlink, symlink_file)。
mount_apply_patch196–212 ↗
async fn mount_apply_patch(
harness: &TestCodexHarness,
call_id: &str,
patch: &str,
assistant_msg: &str,
)
作用:给假模型服务器安装一套响应:模型会调用 apply_patch,然后再发一句助手消息。测试用它控制模型“说出”哪段补丁。
数据流:进去 harness、调用编号、补丁文本和助手回复 → 它用 apply_patch_responses 生成 SSE 事件串,再挂到 mock server 上 → 后续 Codex 请求会收到这套假响应。
调用关系:绝大多数 apply_patch 测试都用它布置模型输出。它把响应细节交给 apply_patch_responses,把安装工作交给 mount_sse_sequence。
调用图:调用 3 个内部函数(mount_sse_sequence, server, apply_patch_responses);被 28 处调用(apply_patch_change_context_disambiguates_target, apply_patch_cli_add_overwrites_existing_file, apply_patch_cli_delete_directory_reports_verification_error, apply_patch_cli_delete_missing_file_reports_error, apply_patch_cli_does_not_write_through_symlink_escape_outside_workspace, apply_patch_cli_end_of_file_anchor, apply_patch_cli_insert_only_hunk_modifies_file, apply_patch_cli_missing_second_chunk_context_rejected, apply_patch_cli_move_overwrites_existing_destination, apply_patch_cli_move_without_content_change_has_no_turn_diff (+15 more))。
mount_apply_patch_model_output214–232 ↗
async fn mount_apply_patch_model_output(
harness: &TestCodexHarness,
call_id: &str,
patch: &str,
assistant_msg: &str,
model_output: ApplyPatchModelOutput,
)
作用:安装一种特定形式的模型输出,比如让模型通过 shell heredoc 的方式调用 apply_patch。heredoc 可以理解成把一大段文本喂给命令。
数据流:进去 harness、调用编号、补丁、助手消息和输出形式 → 它选择对应的 apply_patch 调用事件生成函数,再生成并挂载 SSE 响应 → 后续测试会收到这种形式的调用。
调用关系:测试 shell heredoc 或被拦截的 apply_patch 时会用它。它和 mount_apply_patch 类似,但能选择模型输出形态。
调用图:调用 3 个内部函数(mount_sse_sequence, server, apply_patch_responses);被 2 处调用(apply_patch_shell_accepts_lenient_heredoc_wrapped_patch, intercepted_apply_patch_verification_uses_local_sandbox)。
apply_patch_responses234–251 ↗
fn apply_patch_responses(
call_id: &str,
patch: &str,
assistant_msg: &str,
apply_patch_call: fn(&str, &str) -> serde_json::Value,
) -> Vec<String>
作用:把一段补丁包装成假模型服务器要返回的事件列表。它统一了“先工具调用、后助手回复”的测试剧本。
数据流:进去调用编号、补丁、助手消息和生成工具调用事件的函数 → 它组装两个 SSE 响应:第一个包含 apply_patch 调用,第二个包含助手消息 → 出来字符串列表。
调用关系:mount_apply_patch 和 mount_apply_patch_model_output 都靠它生成响应内容。它不接触服务器,只负责拼测试剧本。
调用图:被 2 处调用(mount_apply_patch, mount_apply_patch_model_output);外部调用 1 个(vec!)。
apply_patch_cli_uses_codex_self_exe_with_linux_sandbox_helper_alias255–286 ↗
async fn apply_patch_cli_uses_codex_self_exe_with_linux_sandbox_helper_alias() -> Result<()>
作用:检查 Linux 下 apply_patch 通过正确的沙箱辅助程序别名运行。这样能保证补丁命令走的是受控入口,而不是绕开沙箱的普通程序。
数据流:进去测试运行环境 → 它确认配置里的沙箱 helper 名字正确,挂载一个新增文件补丁并提交 → 出来应看到成功输出,且文件 helper-alias.txt 被创建。
调用关系:它用 apply_patch_harness 搭环境,用 mount_apply_patch 模拟模型调用,并用正则和文件读取确认工具链和沙箱入口都正常。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 3 个(assert_eq!, assert_regex_match, skip_if_no_network!)。
apply_patch_cli_multiple_operations_integration289–325 ↗
async fn apply_patch_cli_multiple_operations_integration() -> Result<()>
作用:测试一个补丁里同时新增、修改、删除文件的完整流程。它验证 apply_patch 能处理真实常见的混合改动。
数据流:进去临时工作区 → 它先写入待修改和待删除文件,再提交包含三种操作的补丁 → 出来应有成功输出,新文件存在,旧文件删除,修改文件内容更新。
调用关系:它通过 apply_patch_harness_with 指定模型,再用 mount_apply_patch 安排工具调用,是基本集成能力的总体验收测试。
调用图:调用 2 个内部函数(apply_patch_harness_with, mount_apply_patch);外部调用 4 个(assert!, assert_eq!, assert_regex_match, skip_if_no_network!)。
apply_patch_cli_multiple_chunks328–348 ↗
async fn apply_patch_cli_multiple_chunks() -> Result<()>
作用:检查同一个文件里多个位置的修改能一次完成。像在一页纸上同时改第二行和第四行,不能只改一处。
数据流:进去一个有四行内容的文件 → 它提交含两个修改块的补丁 → 出来文件中两处目标行都被替换。
调用关系:它用默认 harness 和 mount_apply_patch,专门覆盖补丁解析器处理多个 hunk(修改块)的能力。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert_eq!, skip_if_no_network!)。
apply_patch_cli_moves_file_to_new_directory351–370 ↗
async fn apply_patch_cli_moves_file_to_new_directory() -> Result<()>
作用:测试补丁能把文件移动到新目录,并同时改内容。重点是目标目录不存在时也要能创建。
数据流:进去 old/name.txt → 它提交带 Move to 的补丁 → 出来旧路径消失,新路径 renamed/dir/name.txt 出现并包含新内容。
调用关系:它使用 mount_apply_patch 模拟模型发出移动补丁,验证 apply_patch 的移动和建目录行为。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 3 个(assert!, assert_eq!, skip_if_no_network!)。
apply_patch_cli_updates_file_appends_trailing_newline373–392 ↗
async fn apply_patch_cli_updates_file_appends_trailing_newline() -> Result<()>
作用:确认修改后文件末尾会补上换行符。很多代码工具默认文本文件应以换行结尾,这能避免后续 diff 混乱。
数据流:进去一个末尾没有换行的文件 → 它提交替换内容的补丁 → 出来文件内容变为两行,并且最后一个字符是换行。
调用关系:它走普通 apply_patch 流程,关注文本格式细节,防止工具生成不规范文件。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 3 个(assert!, assert_eq!, skip_if_no_network!)。
apply_patch_cli_insert_only_hunk_modifies_file395–415 ↗
async fn apply_patch_cli_insert_only_hunk_modifies_file() -> Result<()>
作用:测试只插入新行、不删除旧行的补丁能成功。也就是在两行中间加一行。
数据流:进去 alpha/omega 两行文件 → 它提交只带新增行 beta 的补丁 → 出来文件变成 alpha、beta、omega 三行。
调用关系:它覆盖一种容易被解析器忽略的情况:修改块里没有减号删除行,只有上下文和新增行。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert_eq!, skip_if_no_network!)。
apply_patch_cli_move_overwrites_existing_destination418–440 ↗
async fn apply_patch_cli_move_overwrites_existing_destination() -> Result<()>
作用:测试移动文件时如果目标文件已经存在,apply_patch 会用新内容覆盖它。这样行为是明确的,不会留下两个冲突版本。
数据流:进去一个源文件和一个已有目标文件 → 它提交移动并修改的补丁 → 出来源文件消失,目标文件内容变成补丁里的新内容。
调用关系:它通过普通模型调用触发 apply_patch,验证移动操作遇到已有目标时的结果。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 3 个(assert!, assert_eq!, skip_if_no_network!)。
apply_patch_cli_move_without_content_change_has_no_turn_diff443–473 ↗
async fn apply_patch_cli_move_without_content_change_has_no_turn_diff() -> Result<()>
作用:确认纯重命名、内容没变时不会发 TurnDiff。TurnDiff 是给用户看的内容差异,纯移动不应假装内容变化。
数据流:进去一个内容为 same 的文件 → 它提交只移动不改内容的补丁,并监听事件 → 出来文件路径变了,但没有观察到 TurnDiff 事件。
调用关系:它用 submit_without_wait 让测试能监听事件流,用 wait_for_event 等到回合结束,检查事件层的行为。
调用图:调用 3 个内部函数(apply_patch_harness, mount_apply_patch, submit_without_wait);外部调用 4 个(assert!, assert_eq!, wait_for_event, skip_if_no_network!)。
apply_patch_cli_add_overwrites_existing_file476–494 ↗
async fn apply_patch_cli_add_overwrites_existing_file() -> Result<()>
作用:测试 Add File 指向已有文件时会覆盖内容。这个用例明确了“新增”碰到同名文件时的实际行为。
数据流:进去 duplicate.txt 旧内容 → 它提交 Add File 同名补丁 → 出来 duplicate.txt 内容变成新内容。
调用关系:它使用常规 harness 和 mount_apply_patch,是覆盖文件写入语义的边界测试。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert_eq!, skip_if_no_network!)。
apply_patch_cli_rejects_invalid_hunk_header497–519 ↗
async fn apply_patch_cli_rejects_invalid_hunk_header() -> Result<()>
作用:检查遇到不认识的补丁头时会拒绝,并给出可读错误。坏格式不能被误当成正常改动。
数据流:进去一个带 Frobnicate File 的非法补丁 → 它提交并读取工具输出 → 出来输出包含验证失败和“不是有效 hunk header”的诊断。
调用关系:它验证 apply_patch 的解析错误会正确传回工具输出,而不是静默失败或乱改文件。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert!, skip_if_no_network!)。
apply_patch_cli_reports_missing_context522–548 ↗
async fn apply_patch_cli_reports_missing_context() -> Result<()>
作用:测试补丁要求替换的旧内容不存在时会报错,并且不改原文件。上下文对不上说明补丁可能过期了。
数据流:进去 modify.txt,内容不含 missing → 它提交要求替换 missing 的补丁 → 出来工具输出说明找不到期望行,文件仍保持原样。
调用关系:它使用 mount_apply_patch 安排失败补丁,是“校验失败不能有副作用”的基础场景之一。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 3 个(assert!, assert_eq!, skip_if_no_network!)。
apply_patch_cli_reports_missing_target_file551–577 ↗
async fn apply_patch_cli_reports_missing_target_file() -> Result<()>
作用:检查更新一个不存在的文件时会明确失败。用户应看到缺哪个文件,而不是得到模糊错误。
数据流:进去没有 missing.txt 的工作区 → 它提交 Update File missing.txt → 出来输出包含读取更新目标失败和文件名,磁盘上仍没有该文件。
调用关系:它验证 apply_patch 在读取目标文件阶段的错误报告。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert!, skip_if_no_network!)。
apply_patch_cli_delete_missing_file_reports_error580–607 ↗
async fn apply_patch_cli_delete_missing_file_reports_error() -> Result<()>
作用:检查删除不存在的文件会报错。这样可以避免测试或模型误以为删除已经成功。
数据流:进去没有 missing.txt 的工作区 → 它提交 Delete File missing.txt → 出来输出包含验证失败、读取失败和文件路径。
调用关系:它覆盖删除操作的失败分支,和缺失更新目标的测试互补。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert!, skip_if_no_network!)。
apply_patch_cli_rejects_empty_patch610–627 ↗
async fn apply_patch_cli_rejects_empty_patch() -> Result<()>
作用:测试空补丁会被拒绝。没有任何改动的补丁不应被当作成功操作。
数据流:进去只含 Begin/End 的补丁 → 它提交并读取输出 → 出来输出包含“empty patch”拒绝信息。
调用关系:它验证 apply_patch 的输入基本检查,防止空操作混进成功路径。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert!, skip_if_no_network!)。
apply_patch_cli_delete_directory_reports_verification_error630–647 ↗
async fn apply_patch_cli_delete_directory_reports_verification_error() -> Result<()>
作用:检查把目录当文件删除时会报验证错误。apply_patch 的 Delete File 只应处理文件,不应误删目录。
数据流:进去一个名为 dir 的目录 → 它提交 Delete File: dir → 出来输出包含验证失败和读取失败,目录不会被当文件删除。
调用关系:它覆盖文件类型不匹配的错误场景,确保工具不会做危险的目录删除。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert!, skip_if_no_network!)。
apply_patch_cli_rejects_path_traversal_outside_workspace650–687 ↗
async fn apply_patch_cli_rejects_path_traversal_outside_workspace() -> Result<()>
作用:测试用 ../ 试图写到项目目录外会被拒绝。路径穿越就像从围栏底下钻出去,必须拦住。
数据流:进去严格工作区权限和 Add File: ../escape.txt 补丁 → 它提交后读取输出并检查外部路径 → 出来有越界写入被拒绝的消息,外部文件不存在。
调用关系:它调用 restrictive_workspace_write_profile 设置边界,再用 mount_apply_patch 触发越界补丁,是沙箱安全的重要测试。
调用图:调用 3 个内部函数(apply_patch_harness, mount_apply_patch, restrictive_workspace_write_profile);外部调用 2 个(assert!, skip_if_no_network!)。
intercepted_apply_patch_verification_uses_local_sandbox691–746 ↗
async fn intercepted_apply_patch_verification_uses_local_sandbox() -> Result<()>
作用:检查通过 shell heredoc 形式拦截到的 apply_patch,在校验读取文件时也使用本地沙箱。不能因为调用形式不同就绕过权限。
数据流:进去一个软链接,链接指向被禁止读取的真实文件 → 它提交通过 heredoc 形式的补丁并带不可读路径权限 → 出来工具输出是普通文本验证失败,外部目标内容不变。
调用关系:它用 create_file_symlink 制造危险链接,用 workspace_write_with_unreadable_path 设置禁读规则,用 mount_apply_patch_model_output 模拟 heredoc 调用。
调用图:调用 5 个内部函数(apply_patch_harness, create_file_symlink, mount_apply_patch_model_output, workspace_write_with_unreadable_path, try_from);外部调用 6 个(assert!, assert_eq!, format!, skip_if_no_network!, skip_if_remote!, write)。
apply_patch_cli_does_not_write_through_symlink_escape_outside_workspace749–811 ↗
async fn apply_patch_cli_does_not_write_through_symlink_escape_outside_workspace() -> Result<()>
作用:测试项目内软链接指向项目外文件时,apply_patch 不会顺着链接改外部文件。这是防止越权写入的关键安全测试。
数据流:进去一个工作区软链接和外部 victim.txt → 它提交修改软链接路径的补丁,并把外部目录设为只读 → 出来外部文件仍是原内容,软链接本身仍然是软链接。
调用关系:它通过 apply_patch_harness_with 自定义工作目录,create_file_symlink 创建软链接,workspace_write_with_read_only_root 建立沙箱边界。
调用图:调用 5 个内部函数(apply_patch_harness_with, create_file_symlink, mount_apply_patch, workspace_write_with_read_only_root, try_from);外部调用 12 个(assert!, assert_eq!, cfg!, eprintln!, format!, skip_if_no_network!, skip_if_remote!, current_dir, create_dir_all, symlink_metadata (+2 more))。
apply_patch_cli_preserves_existing_hard_link_outside_workspace814–909 ↗
async fn apply_patch_cli_preserves_existing_hard_link_outside_workspace() -> Result<()>
作用:测试硬链接场景下 apply_patch 的平台行为。硬链接像两个门牌指向同一个真实文件,所以这里要明确工具是否保持这种关系。
数据流:进去一个项目内硬链接和项目外原文件 → 它提交修改硬链接路径的补丁 → Windows 上应拒绝并保持内容不变;其他平台上允许共享文件内容更新,并确认硬链接关系没有被替换断开。
调用关系:它用 workspace_write_with_read_only_root 设置外部只读边界,用 mount_apply_patch 触发修改,专门记录不同操作系统下预期差异。
调用图:调用 4 个内部函数(apply_patch_harness_with, mount_apply_patch, workspace_write_with_read_only_root, try_from);外部调用 11 个(assert!, assert_eq!, cfg!, format!, skip_if_no_network!, skip_if_remote!, current_dir, create_dir_all, hard_link, write (+1 more))。
apply_patch_cli_rejects_move_path_traversal_outside_workspace912–954 ↗
async fn apply_patch_cli_rejects_move_path_traversal_outside_workspace() -> Result<()>
作用:测试移动文件时目标路径不能用 ../ 跑到项目外。新增越界要拦,移动越界也一样要拦。
数据流:进去 stay.txt 和严格写权限 → 它提交 Move to: ../escape-move.txt 的补丁 → 出来越界路径不存在,原文件内容仍为 from。
调用关系:它复用 restrictive_workspace_write_profile,配合 mount_apply_patch 验证移动操作的路径安全。
调用图:调用 3 个内部函数(apply_patch_harness, mount_apply_patch, restrictive_workspace_write_profile);外部调用 4 个(assert!, assert_eq!, skip_if_no_network!, skip_if_wine_exec!)。
apply_patch_cli_verification_failure_has_no_side_effects957–975 ↗
async fn apply_patch_cli_verification_failure_has_no_side_effects() -> Result<()>
作用:检查一个补丁前半段能成功、后半段失败时,前半段也不能留下改动。补丁应该像交易一样,要么全成,要么全不成。
数据流:进去一个包含新增文件和更新缺失文件的补丁 → 校验更新缺失文件失败 → 出来新增文件 created.txt 也不存在。
调用关系:它用普通 apply_patch 调用验证原子性,也就是失败时不产生部分写入。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert!, skip_if_no_network!)。
apply_patch_shell_command_heredoc_with_cd_updates_relative_workdir978–1010 ↗
async fn apply_patch_shell_command_heredoc_with_cd_updates_relative_workdir() -> Result<()>
作用:测试在 shell 命令里先 cd 到子目录,再用 heredoc 调 apply_patch 时,相对路径按新的目录解释。
数据流:进去 sub/in_sub.txt → 模型返回 shell_command:cd sub && apply_patch → 出来命令输出成功,sub/in_sub.txt 从 before 变成 after。
调用关系:它不用 mount_apply_patch,而是手动 mount_sse_sequence 模拟 shell_command 调用,覆盖命令行形式的 apply_patch。
调用图:调用 2 个内部函数(mount_sse_sequence, apply_patch_harness_with);外部调用 4 个(assert!, assert_eq!, skip_if_no_network!, vec!)。
apply_patch_cli_can_use_shell_command_output_as_patch_input1013–1153 ↗
async fn apply_patch_cli_can_use_shell_command_output_as_patch_input() -> Result<()>
作用:测试模型先运行 shell 命令读取文件内容,再把这个输出拼成 apply_patch 输入。重点是工具调用之间的数据能正确传递,包括非 ASCII 字符。
数据流:进去 source.txt,内容含 naïve café → 假服务器第一次让 Codex 读文件,第二次从请求体里取读到的输出并生成新增 target.txt 的补丁 → 出来 target.txt 内容与 source.txt 完全相同。
调用关系:它自定义 wiremock Respond 动态响应三次请求,不走固定 mount_apply_patch,专门测试“上一次工具输出进入下一次模型决策”的链路。
调用图:调用 1 个内部函数(apply_patch_harness_with);外部调用 7 个(new, given, assert_eq!, skip_if_no_network!, skip_if_remote!, method, path_regex)。
apply_patch_custom_tool_streaming_emits_updated_changes1156–1257 ↗
async fn apply_patch_custom_tool_streaming_emits_updated_changes() -> Result<()>
作用:测试 apply_patch 输入一点点流式到达时,会发出逐步更新的改动事件。这样界面可以边接收边展示即将发生的文件变化。
数据流:进去开启 ApplyPatchStreamingEvents 功能的 harness 和分段补丁事件 → 它提交请求并监听 PatchApplyUpdated → 出来先看到空内容的新增文件变化,最后看到完整 hello/world 内容,文件也被创建。
调用关系:它用 apply_patch_harness_with 打开功能开关,用 mount_sse_sequence 手写流式事件,用 submit_without_wait 和 wait_for_event 观察中间事件。
调用图:调用 3 个内部函数(mount_sse_sequence, apply_patch_harness_with, submit_without_wait);外部调用 5 个(new, assert_eq!, wait_for_event, skip_if_no_network!, vec!)。
apply_patch_shell_command_heredoc_with_cd_emits_turn_diff1260–1319 ↗
async fn apply_patch_shell_command_heredoc_with_cd_emits_turn_diff() -> Result<()>
作用:测试通过 shell heredoc 执行 apply_patch 成功后,也会发出 TurnDiff。也就是说不只专用工具调用会产生用户可见差异。
数据流:进去 sub/in_sub.txt 和 shell_command 形式补丁 → 它提交后监听 PatchApplyBegin、PatchApplyEnd 和 TurnDiff → 出来开始/结束事件都出现,结束标记成功,并收到包含 diff --git 的差异文本。
调用关系:它手动挂载 shell_command 响应,并用 submit_without_wait 监听事件,覆盖 shell 路径与事件系统的衔接。
调用图:调用 3 个内部函数(mount_sse_sequence, apply_patch_harness_with, submit_without_wait);外部调用 5 个(assert!, wait_for_event, json!, skip_if_no_network!, vec!)。
apply_patch_turn_diff_paths_stay_repo_relative_when_session_cwd_is_nested1322–1390 ↗
async fn apply_patch_turn_diff_paths_stay_repo_relative_when_session_cwd_is_nested() -> Result<()>
作用:测试会话工作目录在仓库子目录时,TurnDiff 里的路径仍相对仓库根目录。这样不会泄露绝对路径,也方便用户理解。
数据流:进去一个 cwd 在 subdir 的假仓库和 repo.txt → 它从子目录提交更新 ../repo.txt 的补丁 → 出来 diff 使用 a/repo.txt b/repo.txt,不包含本机绝对路径。
调用关系:它用 apply_patch_harness_with 设置嵌套 cwd 和工作区文件,用 mount_apply_patch 触发改动,再监听 TurnDiff。
调用图:调用 3 个内部函数(apply_patch_harness_with, mount_apply_patch, submit_without_wait);外部调用 3 个(assert!, wait_for_event, skip_if_no_network!)。
apply_patch_shell_command_failure_propagates_error_and_skips_diff1393–1447 ↗
async fn apply_patch_shell_command_failure_propagates_error_and_skips_diff() -> Result<()>
作用:测试 shell 形式的 apply_patch 如果校验失败,会把错误传出来,并且不发 TurnDiff。失败的改动不能显示成成功差异。
数据流:进去 invalid.txt 内容 ok → 它提交一个找不到 nope 的 shell heredoc 补丁 → 出来没有 TurnDiff,stdout 里有找不到期望行和文件名,文件保持 ok。
调用关系:它用 mount_sse_sequence 模拟 shell_command,用 submit_without_wait 监听事件,再读取 function_call_stdout 验证错误输出。
调用图:调用 3 个内部函数(mount_sse_sequence, apply_patch_harness_with, submit_without_wait);外部调用 6 个(assert!, assert_eq!, wait_for_event, json!, skip_if_no_network!, vec!)。
apply_patch_shell_accepts_lenient_heredoc_wrapped_patch1450–1485 ↗
async fn apply_patch_shell_accepts_lenient_heredoc_wrapped_patch() -> Result<()>
作用:测试 heredoc 包起来的补丁能被宽容接受,并输出普通文本而不是 JSON。这样模型用 shell 写法时也能正常工作。
数据流:进去一个 Add File lenient.txt 的补丁 → 它通过 ShellCommandViaHeredoc 形式提交 → 出来 stdout 是普通文本成功信息,文件 lenient.txt 被创建。
调用关系:它调用 mount_apply_patch_model_output 指定 heredoc 输出形式,验证 shell 包装路径的兼容性。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch_model_output);外部调用 4 个(assert!, assert_eq!, format!, skip_if_no_network!)。
apply_patch_cli_end_of_file_anchor1488–1502 ↗
async fn apply_patch_cli_end_of_file_anchor() -> Result<()>
作用:测试补丁里的 End of File 锚点能定位到文件末尾。锚点就像告诉工具“就在最后这里改”。
数据流:进去 tail.txt,末尾行为 last → 它提交带 End of File 的替换补丁 → 出来 last 被改成 end。
调用关系:它用常规 apply_patch 流程覆盖一种特殊补丁标记的解析。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert_eq!, skip_if_no_network!)。
apply_patch_cli_missing_second_chunk_context_rejected1505–1532 ↗
async fn apply_patch_cli_missing_second_chunk_context_rejected() -> Result<()>
作用:测试第二个修改块缺少必要上下文时会被拒绝。不能因为第一个块合法,就放过后面含糊的修改。
数据流:进去 two_chunks.txt → 它提交第一个块有 @@、第二处缺少上下文的补丁 → 出来输出验证失败,原文件保持 a/b/c/d。
调用关系:它覆盖多块补丁里的错误处理,确认解析或匹配失败不会造成部分修改。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 3 个(assert!, assert_eq!, skip_if_no_network!)。
apply_patch_emits_turn_diff_event_with_unified_diff1535–1566 ↗
async fn apply_patch_emits_turn_diff_event_with_unified_diff() -> Result<()>
作用:测试普通 apply_patch 成功后会发出统一 diff 事件。统一 diff 是常见的“哪些行新增或删除”的展示格式。
数据流:进去一个新增 udiff.txt 的补丁 → 它提交并监听 TurnDiff → 出来 diff 包含 diff --git、/dev/null 或 a/、以及 b/ 这些新增文件标记。
调用关系:它用 submit_without_wait 等待事件,是验证 apply_patch 和前端差异展示连接的基础测试。
调用图:调用 3 个内部函数(apply_patch_harness, mount_apply_patch, submit_without_wait);外部调用 4 个(assert!, wait_for_event, format!, skip_if_no_network!)。
apply_patch_turn_diff_tracks_local_and_remote_environment_paths1569–1731 ↗
async fn apply_patch_turn_diff_tracks_local_and_remote_environment_paths() -> Result<()>
作用:测试同一回合里本地环境和远程环境都应用补丁时,TurnDiff 能分别标出 local 和 remote 路径。这样用户能看清哪个环境改了什么。
数据流:进去一个本地和远程共享路径、两个带 Environment ID 的补丁 → 它分别应用本地和远程新增同名文件 → 出来本地文件内容是 local,远程文件内容是 remote,diff 中路径带 local/ 和 remote/ 前缀。
调用关系:它直接搭 mock server 和 test_codex,使用 turn_permission_fields 和环境选择提交请求,是多环境 diff 聚合的端到端测试。
调用图:调用 6 个内部函数(mount_sse_sequence, start_mock_server, test_codex, turn_permission_fields, new, from_path);外部调用 11 个(default, from, assert_eq!, get_remote_test_env, wait_for_event, format!, create_dir_all, remove_dir_all, skip_if_no_network!, skip_if_wine_exec! (+1 more))。
apply_patch_aggregates_diff_across_multiple_tool_calls1734–1781 ↗
async fn apply_patch_aggregates_diff_across_multiple_tool_calls() -> Result<()>
作用:测试一个回合里多次 apply_patch 调用的改动会合成一份最终 diff。用户看到的是最终结果,而不是割裂的片段。
数据流:进去两个补丁:先新增 agg/a.txt,再修改它并新增 agg/b.txt → 它依次挂载两次工具调用并提交 → 出来 TurnDiff 同时包含 a.txt 和 b.txt,a.txt 显示最终 v2。
调用关系:它手动组织多段 SSE 响应,用 submit_without_wait 监听最终 TurnDiff,验证跨工具调用的差异聚合。
调用图:调用 4 个内部函数(mount_sse_sequence, sse, apply_patch_harness, submit_without_wait);外部调用 4 个(assert!, wait_for_event, skip_if_no_network!, vec!)。
apply_patch_aggregates_diff_preserves_success_after_failure1784–1854 ↗
async fn apply_patch_aggregates_diff_preserves_success_after_failure() -> Result<()>
作用:测试多次补丁里先成功、后失败时,最终 diff 仍保留成功的改动。一次失败不应抹掉之前已经完成的有效结果。
数据流:进去一个成功新增文件的补丁和一个缺少上下文的失败补丁 → 它提交后等待 TurnDiff → 出来 diff 包含 success.txt 和 +ok,失败调用输出有验证错误,文件内容仍是 ok。
调用关系:它使用 wait_for_event_with_timeout 防止异步等待卡住,验证聚合 diff 对失败调用的处理。
调用图:调用 3 个内部函数(mount_sse_sequence, apply_patch_harness, submit_without_wait);外部调用 6 个(from_secs, assert!, assert_eq!, wait_for_event_with_timeout, skip_if_no_network!, vec!)。
apply_patch_clears_aggregated_diff_after_inexact_delta1857–1924 ↗
async fn apply_patch_clears_aggregated_diff_after_inexact_delta() -> Result<()>
作用:测试遇到无法精确计算差异的改动时,会清空聚合 diff。比如二进制文件被当文本覆盖,系统宁可不展示不可靠的差异。
数据流:进去一个二进制 binary.dat 和一个先成功新增文本文件的补丁 → 第二个补丁把 binary.dat 覆盖成文本 → 出来最后 TurnDiff 是空字符串,但两个文件实际内容都符合最终状态。
调用关系:它通过 apply_patch_harness_with 预置二进制文件,用 wait_for_event_with_timeout 等事件,覆盖“差异不可信时如何处理”的场景。
调用图:调用 3 个内部函数(mount_sse_sequence, apply_patch_harness_with, submit_without_wait);外部调用 5 个(from_secs, assert_eq!, wait_for_event_with_timeout, skip_if_no_network!, vec!)。
apply_patch_change_context_disambiguates_target1927–1946 ↗
async fn apply_patch_change_context_disambiguates_target() -> Result<()>
作用:测试补丁里的 change_context 能在重复内容中选中正确位置。就像同一句话出现两次时,用附近标题说明要改哪一处。
数据流:进去 multi_ctx.txt,里面 fn a 和 fn b 下都有 x=10 → 它提交带 @@ fn b 的补丁 → 出来只把 fn b 下面的 x=10 改成 x=11。
调用关系:它使用普通 mount_apply_patch 流程,专门验证补丁匹配算法能利用上下文消除歧义。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 2 个(assert_eq!, skip_if_no_network!)。
输出整形与中断
这些测试关注工具结果在执行中止时如何被序列化、截断、并行化、快照化和表示。
core/tests/suite/shell_serialization.rs源码 ↗
这个测试文件只在非 Windows 系统上跑。它像一个“假服务器 + 临时工作目录”的演练场:测试先假装模型发来工具调用,比如执行 shell 命令或应用补丁;Codex 真正在测试目录里执行;然后测试检查 Codex 回传给模型的工具结果。重点不是命令本身,而是输出怎么包装:必须是普通文本,前面有退出码和 Wall time(墙钟时间,也就是实际过去了多久),后面有 Output 区域。文件还特别检查几种容易出错的情况:shell 输出本身是 JSON 时不能被当成结构化 JSON;命令失败时也要用同样的自由文本格式;apply_patch 成功时要显示改了哪些文件,失败时要原样给出失败原因;长输出在 10000 字节以内不截断,超过后按约定截断。没有这些测试,系统升级时很容易悄悄改坏工具输出格式,让模型收到“看起来像对、其实不对”的信息。
shell_responses37–58 ↗
fn shell_responses(call_id: &str, command: Vec<&str>) -> Result<Vec<String>>
作用:这个小帮手用来造一组假的模型回复,内容是“请执行一个 shell_command 工具调用,然后说 done”。多个测试都会用它,避免每个测试重复手写同样的假响应。
数据流:进去的是一个调用编号 call_id 和一串命令参数 → 它先把命令参数安全拼成 shell 命令字符串,再包成 JSON 参数,里面带 command 和 timeout_ms → 出来的是两段 SSE(服务器发送事件,一种服务端按顺序推消息的格式)文本:第一段要求 Codex 调工具,第二段模拟模型收尾回复。它不改文件,只生成测试用输入。
调用关系:它是几个 shell 输出测试的“道具制造器”。shell_output_preserves_fixture_json_as_freeform、shell_output_records_duration、shell_output_is_freeform_for_nonzero_exit 会先调用它,再把生成的假响应挂到 mock server 上,让 Codex 以为真的是模型发来的工具请求。
调用图:被 3 处调用(shell_output_is_freeform_for_nonzero_exit, shell_output_preserves_fixture_json_as_freeform, shell_output_records_duration);外部调用 3 个(json!, try_join, vec!)。
shell_output_preserves_fixture_json_as_freeform61–109 ↗
async fn shell_output_preserves_fixture_json_as_freeform() -> Result<()>
作用:这个测试确认:如果 shell 命令打印出来的内容刚好是一段 JSON 文件,Codex 也必须把它当普通文本返回,而不是偷偷解析成 JSON 对象。这样模型看到的是命令的真实输出,不会因为格式碰巧像 JSON 就被改样。
数据流:开始时测试创建 mock server 和一个临时 Codex 实例,再在工作目录写入 fixture.json → 它安排模型请求执行 sed,把这个 JSON 文件原样打印出来 → Codex 执行后把结果回传给 mock server → 测试取出这次工具输出,检查它不能被 serde_json 解析成 JSON,并且文本里有退出码、耗时和完整的文件内容。
调用关系:这个测试调用 start_mock_server 和 test_codex 搭测试环境,调用 shell_responses 生成假工具调用,再用 mount_sse_sequence 把假响应挂上去。最后它通过断言工具检查 Codex 发回服务器的内容,验证 shell 输出序列化这一整条链路没有把文本误处理。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, shell_responses);外部调用 6 个(assert!, assert_eq!, assert_regex_match, write, skip_if_no_network!, vec!)。
shell_output_records_duration112–152 ↗
async fn shell_output_records_duration() -> Result<()>
作用:这个测试确认 shell 命令输出里会记录实际耗时,而且耗时不是空的或假的 0。它防止工具结果丢掉“这条命令跑了多久”这个排查问题时很有用的信息。
数据流:进去没有业务输入,测试自己搭 mock server 和 Codex → 它安排模型请求执行 sleep 0.2,也就是让 shell 等大约 0.2 秒 → Codex 跑完后返回工具输出 → 测试用正则表达式找出 Wall time 的秒数,并确认它大于 0.1 秒,同时输出格式仍然包含退出码、耗时和 Output 区域。
调用关系:它和其他 shell 测试一样,先用 shell_responses 造工具调用,再用 mount_sse_sequence 送给测试服务器。它不关心命令输出内容,主要盯住 Codex 执行命令后生成的元信息,也就是耗时字段。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, shell_responses);外部调用 5 个(new, assert!, assert_regex_match, skip_if_no_network!, vec!)。
apply_patch_custom_tool_call_creates_file155–194 ↗
async fn apply_patch_custom_tool_call_creates_file() -> Result<()>
作用:这个测试确认模型通过自定义 apply_patch 工具要求新增文件时,Codex 会真的创建文件,并把成功信息用正确格式回传。apply_patch 可以理解成“按补丁说明改文件”的工具。
数据流:测试先建立 apply_patch 专用测试环境 → 准备一个补丁,内容是新增 custom_tool_apply_patch.txt 并写入一行文字 → mock server 假装模型发来这个 apply_patch 调用 → Codex 执行补丁后,测试读取工具输出,检查里面有退出码、耗时、Success 信息和新增文件标记 A → 最后再从磁盘读新文件,确认内容确实写进去了。
调用关系:它使用 apply_patch_harness 搭好带临时目录的测试夹具,再用 mount_apply_patch 安排模型的工具调用。Codex 跑完后,测试通过 harness.apply_patch_output 拿回工具输出,并通过 harness.read_file_text 检查磁盘结果,覆盖了“返回信息”和“实际文件变化”两边。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 4 个(assert_eq!, assert_regex_match, format!, skip_if_no_network!)。
apply_patch_custom_tool_call_updates_existing_file197–234 ↗
async fn apply_patch_custom_tool_call_updates_existing_file() -> Result<()>
作用:这个测试确认 apply_patch 不只能新建文件,也能正确修改已有文件,并报告修改成功。它防止补丁工具表面说成功但文件内容没变,或输出格式变乱。
数据流:测试先创建 apply_patch 环境,并写一个已有文件,内容是 before → 然后准备补丁,把 before 改成 after → mock server 让 Codex 执行这个补丁 → 测试检查回传输出中有成功信息和 M 标记,M 表示修改了已有文件 → 最后读文件确认内容已经变成 after。
调用关系:它跟创建文件的测试走同一条链路:apply_patch_harness 准备环境,mount_apply_patch 注入模型调用,Codex 执行后由 harness 收集输出和读取文件。它补上的是“更新已有文件”这条分支。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 4 个(assert_eq!, assert_regex_match, format!, skip_if_no_network!)。
apply_patch_custom_tool_call_reports_failure_output237–268 ↗
async fn apply_patch_custom_tool_call_reports_failure_output() -> Result<()>
作用:这个测试确认 apply_patch 失败时,Codex 会把真实失败原因返回给模型,而不是包装成看不懂的格式。这里的失败场景是要修改一个不存在的文件。
数据流:测试先跳过一些不适合的运行环境,比如 Wine 下路径文字可能不同 → 然后创建 apply_patch 环境,准备一个修改缺失文件的补丁 → Codex 尝试执行后失败 → 测试拿到工具输出,确认它正好是“读不到要更新的文件”的错误信息,包含目标路径和系统错误。
调用关系:它同样通过 apply_patch_harness 和 mount_apply_patch 驱动完整流程,但这次不是看成功摘要,而是看失败信息是否原样、准确。skip_if_wine_exec 和 skip_if_no_network 用来避免在不稳定或不适合的环境里误报失败。
调用图:调用 2 个内部函数(apply_patch_harness, mount_apply_patch);外部调用 4 个(assert_eq!, format!, skip_if_no_network!, skip_if_wine_exec!)。
shell_output_is_freeform_for_nonzero_exit271–302 ↗
async fn shell_output_is_freeform_for_nonzero_exit() -> Result<()>
作用:这个测试确认 shell 命令退出码不是 0,也就是命令失败时,工具输出仍然是普通文本格式。这样模型能清楚看到“Exit code: 42”,而不是收到特殊的异常结构。
数据流:测试搭好 mock server 和 Codex → 生成一个 shell 调用,命令是直接 exit 42 → Codex 执行失败退出后,把结果回传 → 测试取出 output 字符串,检查它以 Exit code: 42 开头,包含 Wall time 和 Output 区域,即使命令没有输出正文也要格式稳定。
调用关系:它调用 shell_responses 生成失败命令的假模型响应,再用 mount_sse_sequence 驱动 Codex 执行。它和成功命令测试互相补充,保证 shell 工具无论成功失败,都按同一种“自由文本”方式报告。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, shell_responses);外部调用 3 个(assert_regex_match, skip_if_no_network!, vec!)。
shell_command_output_is_freeform305–354 ↗
async fn shell_command_output_is_freeform() -> Result<()>
作用:这个测试确认 shell_command 工具的普通输出会被作为自由文本返回,并包含命令实际打印的内容。自由文本的意思是一个完整字符串,而不是拆成 JSON 字段给模型。
数据流:测试搭 mock server 和 Codex → 手写一段假模型响应,要求执行 echo shell command → Codex 执行后把工具结果发回服务器 → 测试取出 output 字符串,检查格式包含退出码 0、耗时、Output 区域,以及 echo 打印出的 shell command。
调用关系:这个测试没有用 shell_responses,而是直接用 json 和 sse 拼出工具调用参数,因为它要覆盖 shell_command 参数里的 login 和 timeout_ms。它仍然通过 mount_sse_sequence 接入 mock server,最后用正则断言 Codex 的返回格式。
调用图:调用 3 个内部函数(mount_sse_sequence, start_mock_server, test_codex);外部调用 4 个(assert_regex_match, json!, skip_if_no_network!, vec!)。
shell_command_output_is_not_truncated_under_10k_bytes357–405 ↗
async fn shell_command_output_is_not_truncated_under_10k_bytes() -> Result<()>
作用:这个测试确认 shell 输出刚好在 10000 字节时不会被截短。它保护的是一个边界规则:不大的输出应该完整交给模型。
数据流:测试准备 mock server 和 Codex → 模拟模型要求执行 perl 命令,打印 10000 个字符“1” → Codex 执行并返回工具输出 → 测试检查 Output 区域后面确实是完整的 10000 个“1”,中间没有省略号或截断提示。
调用关系:它直接构造 shell_command 调用,再通过 mount_sse_sequence 送入 Codex。它和超过 10000 字节的测试是一对边界测试,一个证明阈值内完整保留,另一个证明阈值外会按规则截断。
调用图:调用 3 个内部函数(mount_sse_sequence, start_mock_server, test_codex);外部调用 4 个(assert_regex_match, json!, skip_if_no_network!, vec!)。
shell_command_output_is_not_truncated_over_10k_bytes408–456 ↗
async fn shell_command_output_is_not_truncated_over_10k_bytes() -> Result<()>
作用:这个测试确认 shell 输出超过 10000 字节时,会用约定的方式截断,并明确告诉模型有多少字符被省略。这样既避免传太长的内容,又不让模型误以为看到了完整输出。
数据流:测试搭好 mock server 和 Codex → 模拟模型执行 perl 命令,打印 10001 个“1” → Codex 执行后生成工具输出 → 测试用正则检查输出前后仍有一部分原文,中间出现省略号和“chars truncated”提示,说明长内容被压缩过。
调用关系:它和 shell_command_output_is_not_truncated_under_10k_bytes 共同锁定长输出策略。流程上仍是 mock server 发工具调用,Codex 执行 shell_command,测试从最后一次请求中取回 function_call_output 并验证格式。
调用图:调用 3 个内部函数(mount_sse_sequence, start_mock_server, test_codex);外部调用 4 个(assert_regex_match, json!, skip_if_no_network!, vec!)。
core/tests/suite/abort_tasks.rs源码 ↗
这个文件是在做一组集成测试,也就是把系统的几个真实部件连起来跑,看它们配合得对不对。测试里会假装模型通过 Responses API 返回一个工具调用,让系统去执行一个很久才结束的命令,比如 sleep 60。然后测试马上发送 Interrupt,也就是“用户要求中断”。它检查三件事:第一,系统会发出 TurnAborted 事件,表示这一轮对话确实被取消了;第二,下次再请求模型时,会把原来的工具调用和一段“用户中止了”的工具输出一起放进历史里;第三,下次请求里还会带上 <turn_aborted> 这个模型能看见的标记。这里用到了 mock server,也就是假的服务器,像舞台上的替身一样,帮测试稳定地模拟模型服务返回什么,而不用真的连外网。
interrupt_long_running_tool_emits_turn_aborted23–68 ↗
async fn interrupt_long_running_tool_emits_turn_aborted()
作用:这个测试确认:如果模型让系统执行一个很久的 shell 命令,用户中途打断后,系统会发出“本轮已中止”的事件。它主要防止一种严重问题:命令还在跑,界面或上层逻辑却不知道已经被用户取消了。
数据流:测试先准备一个假的模型响应,里面包含一次 shell_command 工具调用,命令是 sleep 60。然后它启动假的 Responses 服务器,把这段响应挂上去,再创建一个测试用的 Codex 实例。接着它提交一条用户输入,等到系统真的开始执行命令后,再提交 Interrupt。最后它等待并检查系统输出了 TurnAborted 事件。结果是:输入从普通用户请求变成了一次长命令执行,再被中断,最终对外表现为这一轮被取消。
调用关系:这个测试自己搭好假服务器和测试 Codex 环境,会用 mount_sse_once、sse、start_mock_server 和 test_codex 来布置舞台。它通过 wait_for_event 先等到 ExecCommandBegin,避免命令还没开始就打断造成测试不稳定;然后再等 TurnAborted,验证核心中断流程真的走完。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 4 个(default, wait_for_event, json!, vec!)。
interrupt_tool_records_history_entries75–174 ↗
async fn interrupt_tool_records_history_entries()
作用:这个测试确认:中断工具命令后,系统不只是发个事件了事,还会把这次工具调用和“已被用户中止”的结果写进后续发给模型的上下文。这样模型下一轮才知道刚才发生了什么,不会误会工具成功返回了正常结果。
数据流:测试先准备两次假的模型响应:第一次让系统调用 shell_command 跑 sleep 60,第二次代表用户后续又发了一句话。它启动假服务器并记录发到服务器的请求。随后测试提交第一条用户输入,等命令开始后稍微睡 0.1 秒,再发 Interrupt,并等到 TurnAborted。之后它提交第二条用户输入,等这一轮完成。最后它检查假服务器收到的两次请求:确认第二次请求的历史里能看到原来的工具调用,并且对应的工具输出文本写着运行了多少秒以及 aborted by user。输出中的秒数还要至少达到 0.1 秒,说明系统记录的是实际经过的时间,而不是随便写的固定值。
调用关系:这个测试比第一个更进一步:它不只看事件,还检查下一次发给模型的请求内容。它用 mount_sse_sequence 安排两段连续的假 SSE 响应,SSE 是服务器连续推送事件的一种格式;用 response_mock.requests() 读取系统实际发出的请求;还用正则表达式检查中止信息的文字格式。它覆盖的是“中断之后如何进入下一轮对话历史”的流程。
调用图:调用 4 个内部函数(mount_sse_sequence, sse, start_mock_server, test_codex);外部调用 10 个(clone, default, from_secs_f32, new, assert!, assert_matches!, wait_for_event, json!, sleep, vec!)。
interrupt_persists_turn_aborted_marker_in_next_request179–256 ↗
async fn interrupt_persists_turn_aborted_marker_in_next_request()
作用:这个测试确认:用户中断一轮之后,系统会在下一次请求模型时加入 <turn_aborted> 标记。这个标记像一张便签,明确告诉模型“上一轮不是正常结束,而是被用户打断了”。
数据流:测试同样先让假模型返回一个长时间运行的 shell_command 调用,然后启动测试 Codex。它提交第一条用户输入,等命令开始执行后,暂停大约 0.1 秒并发送 Interrupt。系统发出 TurnAborted 后,测试再提交一条后续用户输入并等待完成。最后它取出假服务器收到的第二次请求,查看里面用户角色的文本内容,确认至少有一段包含 <turn_aborted>。也就是说,之前的中断状态被带进了下一次模型请求,而不是只存在于本地事件里。
调用关系:这个测试和 interrupt_tool_records_history_entries 使用相似的舞台布置:假服务器、连续 SSE 响应、测试 Codex、等待事件。不同点是,它重点检查发送给模型的用户文本里有没有 <turn_aborted> 标记。它依赖 mount_sse_sequence 来模拟前后两轮模型交互,也依赖响应记录对象来查看第二次请求的具体内容。
调用图:调用 4 个内部函数(mount_sse_sequence, sse, start_mock_server, test_codex);外部调用 9 个(clone, default, from_secs_f32, assert!, assert_eq!, wait_for_event, json!, sleep, vec!)。
core/tests/suite/shell_snapshot.rs源码 ↗
shell 快照可以理解成“先给命令拍一张环境说明书”:里面记着 shell 里的 PATH、导出变量、别名等信息,之后执行命令前先读这份说明书,让命令更像在用户真实终端里跑。这个测试文件会搭一个假的 Codex 会话和假的模型返回,用服务器推送事件(SSE,一种按顺序把消息推给客户端的机制)让 Codex 去调用 shell_command 或 exec_command。测试会等待命令开始事件、找到生成的快照文件、读取内容,再等命令结束事件,然后检查命令参数、输出、退出码和快照内容。它还特意测试环境策略是否覆盖快照里的 PATH、apply_patch 是否仍会被识别成补丁操作、关机后快照文件是否被删除。没有这些测试,shell 快照很容易在不同系统或不同工具入口下悄悄失效。
wait_for_snapshot52–74 ↗
async fn wait_for_snapshot(codex_home: &Path) -> Result<PathBuf>
作用:等待 Codex 在自己的 home 目录下生成 shell 快照文件。它用于解决异步测试里的时间差:快照不是立刻出现,所以测试要耐心等一小会儿。
数据流:输入是 Codex home 目录路径 → 它反复查看 home/shell_snapshots 目录,寻找扩展名是 .sh 或 .ps1 的文件 → 找到就返回这个快照路径;如果 5 秒内都没找到,就让测试失败。
调用关系:它是多条测试和运行辅助函数的“等文件出现”小工具。run_snapshot_command_with_options、run_shell_command_snapshot_with_options 先触发命令,再靠它确认快照已生成;策略测试、apply_patch 测试和关机清理测试也用它拿到那份快照文件。
调用图:被 6 处调用(linux_unified_exec_snapshot_preserves_shell_environment_policy_set, run_shell_command_snapshot_with_options, run_snapshot_command_with_options, shell_command_snapshot_preserves_shell_environment_policy_set, shell_command_snapshot_still_intercepts_apply_patch, shell_snapshot_deleted_after_shutdown_with_skills);外部调用 7 个(from_millis, from_secs, now, join, bail!, read_dir, sleep)。
wait_for_file_contents76–91 ↗
async fn wait_for_file_contents(path: &Path) -> Result<String>
作用:等待某个文件真正写到磁盘上,并读出里面的文字。它主要用来避免测试刚检查文件时,文件还没来得及生成。
数据流:输入是一个文件路径 → 它最多等 15 秒,期间不断尝试读取文件;如果文件还不存在就继续等,如果出现别的读取错误就直接报错 → 成功时返回文件内容,超时则让测试失败。
调用关系:它是文件落盘检查的辅助件,通常放在某个命令已经触发文件写入之后,用来确认最终文件内容真的符合预期。
调用图:外部调用 6 个(from_millis, from_secs, now, bail!, read_to_string, sleep)。
policy_set_path_for_test93–95 ↗
fn policy_set_path_for_test() -> HashMap<String, String>
作用:构造一份测试用的环境策略,只设置 PATH。这里的 PATH 是故意写死的,方便后面验证“策略设置”是否能压过快照里的 PATH。
数据流:没有输入 → 它创建一个键值表,把 PATH 设成测试常量 /codex/policy/path → 返回这份环境变量设置表。
调用关系:它服务于环境策略相关测试。测试先把这份 PATH 放进配置,再故意改写快照里的 PATH,最后检查实际执行命令时到底谁生效。
调用图:外部调用 1 个(from)。
snapshot_override_content_for_policy_test97–101 ↗
fn snapshot_override_content_for_policy_test() -> String
作用:生成一段假的 shell 快照内容,用来模拟快照里也设置了 PATH 和一个标记变量。这样测试就能检查:快照会被加载,但 PATH 仍应被安全策略覆盖。
数据流:没有输入 → 它把固定的快照头、PATH 设置和标记变量拼成一段 shell 脚本文本 → 返回这段文本,供测试写进快照文件。
调用关系:策略测试会先正常生成快照,再用它产出的内容覆盖快照文件。随后测试命令会读取这个快照,以确认快照里的普通变量生效,但 PATH 被策略重新控制。
调用图:被 2 处调用(linux_unified_exec_snapshot_preserves_shell_environment_policy_set, shell_command_snapshot_preserves_shell_environment_policy_set);外部调用 1 个(format!)。
command_asserting_policy_after_snapshot103–107 ↗
fn command_asserting_policy_after_snapshot() -> String
作用:拼出一条 shell 命令,用它来检查环境策略是否在加载快照之后仍然生效。它不是随便跑命令,而是在命令内部自己判断 PATH 和标记变量是否正确。
数据流:没有输入 → 它生成一段 shell 判断脚本:先看快照标记变量是否存在,再看 PATH 是否不是快照里的值,同时包含策略里的路径 → 如果符合就输出成功标记,否则输出当前 PATH 和标记变量方便排错。
调用关系:环境策略测试用它作为第二次命令执行的内容。它和 snapshot_override_content_for_policy_test 配套:一个伪造快照,一个验证最终环境。
调用图:被 2 处调用(linux_unified_exec_snapshot_preserves_shell_environment_policy_set, shell_command_snapshot_preserves_shell_environment_policy_set);外部调用 1 个(format!)。
run_snapshot_command109–111 ↗
async fn run_snapshot_command(command: &str) -> Result<SnapshotRun>
作用:用默认选项跑一次 unified exec 工具,并要求它启用 shell 快照。unified exec 可以理解成项目里较新的统一命令执行入口。
数据流:输入是一条要执行的命令字符串 → 它套上默认的 SnapshotRunOptions → 把实际搭环境、发请求、等事件的工作交给 run_snapshot_command_with_options,最后返回一次运行的完整记录。
调用关系:Linux、macOS、Windows 的 unified exec 快照测试都通过它开始。它本身很薄,主要是给常规测试一个简单入口。
调用图:调用 1 个内部函数(run_snapshot_command_with_options);被 3 处调用(linux_unified_exec_uses_shell_snapshot, macos_unified_exec_uses_shell_snapshot, windows_unified_exec_uses_shell_snapshot);外部调用 1 个(default)。
run_snapshot_command_with_options113–210 ↗
async fn run_snapshot_command_with_options(
command: &str,
options: SnapshotRunOptions,
) -> Result<SnapshotRun>
作用:完整搭建一次 unified exec 的测试场景,并收集快照和命令执行结果。它把“配置功能开关、模拟模型调用、提交用户输入、等待事件”这些步骤串起来。
数据流:输入是命令字符串和可选的 shell 环境策略 → 它创建测试 Codex,打开 UnifiedExec 和 ShellSnapshot 功能,准备假的模型响应让模型调用 exec_command,再提交一条用户消息 → 之后它等待命令开始事件、等待快照文件、读取快照内容、等待命令结束和回合完成,最后返回 SnapshotRun,里面有开始事件、结束事件、快照路径、快照内容和 home 目录。
调用关系:它是 unified exec 相关测试的主发动机。run_snapshot_command 调用它;它内部依赖测试框架挂载 SSE 响应、设置权限字段、等待 Codex 事件,并调用 wait_for_snapshot 找到快照文件。
调用图:调用 6 个内部函数(mount_sse_sequence, with_builder, local_selections, test_codex, turn_permission_fields, wait_for_snapshot);被 1 处调用(run_snapshot_command);外部调用 6 个(default, wait_for_event, wait_for_event_match, read_to_string, json!, vec!)。
run_shell_command_snapshot212–214 ↗
async fn run_shell_command_snapshot(command: &str) -> Result<SnapshotRun>
作用:用默认选项跑一次旧的 shell_command 工具,并要求它启用 shell 快照。它给测试提供一个最简单的入口。
数据流:输入是一条 shell 命令 → 它使用默认选项 → 把真正的测试流程交给 run_shell_command_snapshot_with_options,并返回执行记录。
调用关系:linux_shell_command_uses_shell_snapshot 通过它验证 shell_command 入口也会使用快照。它和 run_snapshot_command 是一对,只是测试的工具名不同。
调用图:调用 1 个内部函数(run_shell_command_snapshot_with_options);被 1 处调用(linux_shell_command_uses_shell_snapshot);外部调用 1 个(default)。
run_shell_command_snapshot_with_options216–308 ↗
async fn run_shell_command_snapshot_with_options(
command: &str,
options: SnapshotRunOptions,
) -> Result<SnapshotRun>
作用:完整搭建一次 shell_command 的测试场景,并收集快照文件和命令结果。它验证旧命令执行入口是否也能接上 shell 快照机制。
数据流:输入是命令字符串和可选环境策略 → 它创建测试 Codex,打开 ShellSnapshot 功能,安排假的模型响应去调用 shell_command,然后提交用户输入 → 它等待命令开始、等待快照生成、读取快照、等待命令结束和回合完成,最后返回 SnapshotRun。
调用关系:它是 shell_command 快照测试的主流程。run_shell_command_snapshot 调用它;它和 unified exec 版本结构几乎一样,只是传给模型的工具名和参数字段不同。
调用图:调用 6 个内部函数(mount_sse_sequence, with_builder, local_selections, test_codex, turn_permission_fields, wait_for_snapshot);被 1 处调用(run_shell_command_snapshot);外部调用 6 个(default, wait_for_event, wait_for_event_match, read_to_string, json!, vec!)。
run_tool_turn_on_harness310–376 ↗
async fn run_tool_turn_on_harness(
harness: &TestCodexHarness,
prompt: &str,
call_id: &str,
tool_name: &str,
args: serde_json::Value,
) -> Result<ExecCommandEndEvent>
作用:在一个已经建好的测试 harness 上跑一轮工具调用,并返回命令结束事件。harness 可以理解成“测试用的 Codex 小实验室”。
数据流:输入是测试 harness、用户提示词、调用编号、工具名和工具参数 → 它给假服务器挂上两段模型响应:先调用工具,再回复 done;接着向 Codex 提交用户输入并设置无审批、无沙箱限制的本地环境 → 它等待命令开始、命令结束和回合完成,最后返回 ExecCommandEndEvent。
调用关系:环境策略测试需要在同一个 harness 里先预热生成快照,再改写快照,再跑第二次命令验证结果,所以不能每次都新建环境;这个函数正好负责在同一个环境里反复跑工具回合。
调用图:调用 5 个内部函数(mount_sse_sequence, server, test, local_selections, turn_permission_fields);被 2 处调用(linux_unified_exec_snapshot_preserves_shell_environment_policy_set, shell_command_snapshot_preserves_shell_environment_policy_set);外部调用 4 个(default, wait_for_event, wait_for_event_match, vec!)。
normalize_newlines378–380 ↗
fn normalize_newlines(text: &str) -> String
作用:把 Windows 风格换行符转换成普通的 \n。这样不同系统上的输出比较不会因为换行格式不同而失败。
数据流:输入是一段文本 → 它把所有 \r\n 替换成 \n → 返回标准化后的文本,不修改原始字符串。
调用关系:它在输出断言前使用,尤其是 linux_unified_exec_uses_shell_snapshot 这类测试会先统一换行,再检查 stdout 里是否有预期文字。
调用图:被 1 处调用(linux_unified_exec_uses_shell_snapshot)。
assert_posix_snapshot_sections382–391 ↗
fn assert_posix_snapshot_sections(snapshot: &str)
作用:检查类 Unix 系统的快照内容是否包含几个关键段落。POSIX 这里可以简单理解成 Linux/macOS 这类 shell 的通用风格。
数据流:输入是快照文本 → 它检查里面是否有快照文件头、aliases、exports、setopts,以及 PATH → 如果缺少任何关键内容,就让测试失败,并在 PATH 缺失时附带快照内容帮助排查。
调用关系:Linux、macOS 和 apply_patch 相关测试用它确认快照不是空壳,而是真的记录了 shell 环境的重要部分。
调用图:被 4 处调用(linux_shell_command_uses_shell_snapshot, linux_unified_exec_uses_shell_snapshot, macos_unified_exec_uses_shell_snapshot, shell_command_snapshot_still_intercepts_apply_patch);外部调用 1 个(assert!)。
linux_unified_exec_uses_shell_snapshot395–412 ↗
async fn linux_unified_exec_uses_shell_snapshot() -> Result<()>
作用:验证 Linux 上 unified exec 执行命令时会使用 shell 快照。它确认命令参数形式、快照位置、快照内容、输出和退出码都对。
数据流:没有外部输入 → 它让 unified exec 跑 echo snapshot-linux → 拿到运行记录后检查命令是通过 shell -lc 执行、快照在 Codex home 下、快照有关键段落、命令成功退出,并且 stdout 里包含 snapshot-linux。
调用关系:这是 unified exec 在 Linux 上的主验收测试。它调用 run_snapshot_command 跑完整流程,再用 normalize_newlines 和 assert_posix_snapshot_sections 做结果检查。
调用图:调用 3 个内部函数(assert_posix_snapshot_sections, normalize_newlines, run_snapshot_command);外部调用 2 个(assert!, assert_eq!)。
linux_shell_command_uses_shell_snapshot416–432 ↗
async fn linux_shell_command_uses_shell_snapshot() -> Result<()>
作用:验证非 Windows 系统上的 shell_command 工具也会使用 shell 快照。它确保旧入口和新入口一样不会绕过快照机制。
数据流:没有外部输入 → 它让 shell_command 执行 echo shell-command-snapshot-linux → 检查启动命令是 shell -lc,快照在 Codex home 下,快照内容完整,stdout 精确等于预期文本,退出码为 0。
调用关系:它通过 run_shell_command_snapshot 跑 shell_command 版本的完整流程,并复用 assert_posix_snapshot_sections 检查快照结构。
调用图:调用 2 个内部函数(assert_posix_snapshot_sections, run_shell_command_snapshot);外部调用 2 个(assert!, assert_eq!)。
shell_command_snapshot_preserves_shell_environment_policy_set436–481 ↗
async fn shell_command_snapshot_preserves_shell_environment_policy_set() -> Result<()>
作用:验证 shell_command 加载快照后,配置里的环境策略仍然能强制设置 PATH。也就是说,快照能带来环境信息,但不能偷偷覆盖安全策略。
数据流:没有外部输入 → 它创建启用 ShellSnapshot 的测试 Codex,并把 PATH 策略设成固定测试路径;先跑一次命令生成快照,再把快照内容改成带有另一个 PATH 和标记变量;随后执行一条自检命令 → 如果输出是 policy-after-snapshot 且退出码为 0,就说明快照被加载了,但 PATH 仍按策略生效。
调用关系:它用 run_tool_turn_on_harness 在同一个 harness 里跑两轮:第一轮预热生成快照,第二轮验证策略。它还使用 wait_for_snapshot 找快照,用 snapshot_override_content_for_policy_test 和 command_asserting_policy_after_snapshot 组成测试陷阱。
调用图:调用 6 个内部函数(with_builder, test_codex, command_asserting_policy_after_snapshot, run_tool_turn_on_harness, snapshot_override_content_for_policy_test, wait_for_snapshot);外部调用 4 个(assert!, assert_eq!, write, json!)。
linux_unified_exec_snapshot_preserves_shell_environment_policy_set485–535 ↗
async fn linux_unified_exec_snapshot_preserves_shell_environment_policy_set() -> Result<()>
作用:验证 Linux 上 unified exec 加载快照后,环境策略设置的 PATH 不会被快照覆盖。这是对新执行入口的安全边界检查。
数据流:没有外部输入 → 它创建启用 UnifiedExec 和 ShellSnapshot 的测试 Codex,并设置固定 PATH 策略;先跑一次 exec_command 生成快照,再用伪造内容覆盖快照;然后跑一条自检命令 → 输出成功标记且退出码为 0,说明策略 PATH 赢过了快照 PATH,同时快照里的标记变量确实被加载。
调用关系:它和 shell_command_snapshot_preserves_shell_environment_policy_set 测同一个原则,只是工具换成 exec_command。它调用 run_tool_turn_on_harness 执行两轮工具调用,并依赖 wait_for_snapshot、snapshot_override_content_for_policy_test、command_asserting_policy_after_snapshot 完成验证。
调用图:调用 6 个内部函数(with_builder, test_codex, command_asserting_policy_after_snapshot, run_tool_turn_on_harness, snapshot_override_content_for_policy_test, wait_for_snapshot);外部调用 4 个(assert!, assert_eq!, write, json!)。
shell_command_snapshot_still_intercepts_apply_patch539–644 ↗
async fn shell_command_snapshot_still_intercepts_apply_patch() -> Result<()>
作用:验证启用 shell 快照后,shell_command 里写的 apply_patch 仍会被 Codex 识别成补丁操作。apply_patch 是一种通过文本补丁改文件的特殊命令,不能因为外面套了快照 shell 就变成普通命令漏掉。
数据流:没有外部输入 → 它搭建启用 ShellSnapshot 的测试 Codex,让模型调用 shell_command,并在命令里放入 apply_patch 脚本来新增 snapshot-apply.txt → 它等待快照生成并检查快照内容,再监听补丁开始和补丁结束事件,最后确认补丁成功且目标文件内容是 hello from snapshot。
调用关系:这是快照机制和补丁拦截机制之间的兼容性测试。它直接挂载模型 SSE 响应、提交用户输入、等待 Codex 事件,并调用 wait_for_snapshot 与 assert_posix_snapshot_sections 辅助检查。
调用图:调用 7 个内部函数(mount_sse_sequence, with_builder, local_selections, test_codex, turn_permission_fields, assert_posix_snapshot_sections, wait_for_snapshot);外部调用 7 个(default, assert!, assert_eq!, wait_for_event, read_to_string, json!, vec!)。
shell_snapshot_deleted_after_shutdown_with_skills648–677 ↗
async fn shell_snapshot_deleted_after_shutdown_with_skills() -> Result<()>
作用:验证 Codex 关闭后会删除 shell 快照文件。这样临时环境文件不会一直留在磁盘上,避免污染后续测试或泄露环境信息。
数据流:没有外部输入 → 它创建启用 ShellSnapshot 的测试 Codex,等待快照出现并确认存在;然后提交 Shutdown 关闭操作,等到 ShutdownComplete;再释放对象并稍等一会儿 → 最后检查原快照路径已经不存在。
调用关系:它专门测试生命周期结尾的清理动作。它调用 wait_for_snapshot 拿到清理目标,然后通过 Codex 的 Shutdown 流程触发删除。
调用图:调用 3 个内部函数(with_builder, test_codex, wait_for_snapshot);外部调用 5 个(from_millis, assert!, assert_eq!, wait_for_event, sleep)。
macos_unified_exec_uses_shell_snapshot685–710 ↗
async fn macos_unified_exec_uses_shell_snapshot() -> Result<()>
作用:验证 macOS 上 unified exec 使用 shell 快照时,命令包装方式正确。macOS 的启动命令和 Linux 不完全一样,所以需要单独测。
数据流:没有外部输入 → 它让 unified exec 执行 echo snapshot-macos → 检查命令参数中先 source 快照,再 exec 真正命令;同时确认快照在 Codex home 下、快照有 POSIX 关键段落、stdout 是 snapshot-macos、退出码为 0。
调用关系:它通过 run_snapshot_command 复用 unified exec 的通用测试流程,再针对 macOS 特有的命令参数形状做断言,并复用 assert_posix_snapshot_sections 检查快照内容。
调用图:调用 2 个内部函数(assert_posix_snapshot_sections, run_snapshot_command);外部调用 2 个(assert!, assert_eq!)。
windows_unified_exec_uses_shell_snapshot715–746 ↗
async fn windows_unified_exec_uses_shell_snapshot() -> Result<()>
作用:验证 Windows 上 unified exec 使用 PowerShell 快照的预期行为。这个测试目前被忽略,不会默认运行,但保留了 Windows 支持应满足的检查标准。
数据流:没有外部输入 → 它准备执行 Write-Output snapshot-windows → 理论上会检查命令参数包含快照文件、使用 -NoProfile、通过 PowerShell 脚本加载快照并执行原命令;还会检查快照内容、stdout 和退出码。
调用关系:它通过 run_snapshot_command 走 unified exec 的通用流程,但因为标记为 ignored,目前更像一份待恢复的 Windows 行为说明和回归测试模板。
调用图:调用 1 个内部函数(run_snapshot_command);外部调用 2 个(assert!, assert_eq!)。
core/tests/suite/tool_parallelism.rs源码 ↗
这个测试文件专门盯住“工具并行执行”这件事。模型可能一次要求跑多个工具,比如执行几条 shell 命令,或调用测试用的同步工具。如果系统把它们串行执行,本来 0.3 秒能一起完成的事,可能会变成 0.6 秒、0.9 秒,体验会明显变慢。这里用假的服务器模拟模型返回的 SSE 流(服务器持续推送事件的一种格式),让 Codex 收到多个工具调用。测试会安排工具睡眠、写时间戳,或用“闸门”故意卡住流的一部分,观察工具是不是已经开始跑。它还检查:回传给模型的内容里,所有“工具调用记录”要先出现,所有“工具输出”再一起出现,并且输出顺序要和调用顺序一一对应。整个文件只在非 Windows 系统上跑,因为里面大量依赖 Unix 风格的 shell 命令和 sleep 行为。
run_turn35–70 ↗
async fn run_turn(test: &TestCodex, prompt: &str) -> anyhow::Result<()>
作用:启动一次完整的用户回合:把一段用户提示词交给测试中的 Codex,然后等到这一回合结束。它把权限、沙箱和本地环境都设置好,让测试不用每次重复写这些样板代码。
数据流:输入是一个 TestCodex 测试实例和一段提示词。它读取当前会话的模型名、测试目录和配置,生成本轮需要的权限字段,再提交一个 UserInput 操作给 Codex。之后它一直等到收到 TurnComplete,也就是“这一轮处理完了”的事件,最后返回成功或错误。
调用关系:它是多个测试的共用小助手。read_file_tools_run_in_parallel 用它先做预热,tool_results_grouped 用它触发三次工具调用,run_turn_and_measure 也包着它来统计耗时。它内部把环境选择交给 local_selections,把权限字段交给 turn_permission_fields,最后靠 wait_for_event 等收尾事件。
调用图:调用 2 个内部函数(local_selections, turn_permission_fields);被 3 处调用(read_file_tools_run_in_parallel, run_turn_and_measure, tool_results_grouped);外部调用 3 个(default, wait_for_event, vec!)。
run_turn_and_measure72–76 ↗
async fn run_turn_and_measure(test: &TestCodex, prompt: &str) -> anyhow::Result<Duration>
作用:运行一次用户回合,并量出这次花了多久。测试用它判断多个工具是不是并行跑完,而不是串行拖长时间。
数据流:输入是测试实例和提示词。它先记录当前时间,然后调用 run_turn 真正执行这一轮,等完成后用当前时间减去开始时间,输出一个 Duration,也就是耗时长度。
调用关系:它站在计时器的位置。read_file_tools_run_in_parallel、shell_tools_run_in_parallel 和 mixed_parallel_tools_run_in_parallel 都通过它拿到耗时,然后把结果交给 assert_parallel_duration 判断是否足够快。
调用图:调用 1 个内部函数(run_turn);被 3 处调用(mixed_parallel_tools_run_in_parallel, read_file_tools_run_in_parallel, shell_tools_run_in_parallel);外部调用 1 个(now)。
build_codex_with_test_tool78–81 ↗
async fn build_codex_with_test_tool(server: &wiremock::MockServer) -> anyhow::Result<TestCodex>
作用:创建一个带测试工具的 Codex 测试实例。这样测试可以让模型“调用工具”,但实际调用的是可控的假工具,方便精确验证并行行为。
数据流:输入是假模型服务器。它从 test_codex 测试构造器开始,指定模型名为 test-gpt-5.1-codex,然后用这个服务器构建出 TestCodex。输出是准备好的测试环境。
调用关系:它是搭建测试舞台的小工具。read_file_tools_run_in_parallel、mixed_parallel_tools_run_in_parallel 和 tool_results_grouped 都先调用它,拿到一个能识别 test_sync_tool 的 Codex 实例,再继续安排模型事件。
调用图:调用 1 个内部函数(test_codex);被 3 处调用(mixed_parallel_tools_run_in_parallel, read_file_tools_run_in_parallel, tool_results_grouped)。
assert_parallel_duration83–89 ↗
fn assert_parallel_duration(actual: Duration)
作用:判断一次测试耗时是否短到足以说明工具是在并行执行。它给慢速 CI 机器留了一点余量,但如果超过 1.6 秒就认为不对劲。
数据流:输入是实际耗时 Duration。它把这个耗时和 1600 毫秒比较;如果耗时更短,什么也不改并继续;如果太久,就让测试失败,并打印实际耗时。
调用关系:它是几个并行测试最后的裁判。read_file_tools_run_in_parallel、shell_tools_run_in_parallel 和 mixed_parallel_tools_run_in_parallel 先制造多个耗时工具调用,再把测到的总时长交给它判断。
调用图:被 3 处调用(mixed_parallel_tools_run_in_parallel, read_file_tools_run_in_parallel, shell_tools_run_in_parallel);外部调用 1 个(assert!)。
read_file_tools_run_in_parallel92–151 ↗
async fn read_file_tools_run_in_parallel() -> anyhow::Result<()>
作用:验证测试用的同步工具在同一轮里被调用两次时,会同时执行。函数名里说 read_file,但实际这里用的是 test_sync_tool 这个可控测试工具。
数据流:它先启动假服务器并构建带测试工具的 Codex。然后准备两轮模拟模型回复:第一轮是短睡眠的预热,第二轮是两个各睡约 300 毫秒、还带同步屏障的工具调用。它先跑预热,再计时跑正式回合,最后检查总耗时是否足够短。
调用关系:这是一个完整的测试用例。它把假 SSE 响应挂到服务器上,借 build_codex_with_test_tool 搭环境,借 run_turn 做预热,借 run_turn_and_measure 计时,最后交给 assert_parallel_duration 判断是否真的并行。
调用图:调用 7 个内部函数(mount_sse_sequence, sse, start_mock_server, assert_parallel_duration, build_codex_with_test_tool, run_turn, run_turn_and_measure);外部调用 3 个(json!, skip_if_no_network!, vec!)。
shell_tools_run_in_parallel154–186 ↗
async fn shell_tools_run_in_parallel() -> anyhow::Result<()>
作用:验证两条 shell_command 命令会并行执行。shell_command 可以理解成“让系统打开终端跑一条命令”。
数据流:它启动假服务器,构建 Codex,然后准备两个相同的 shell 命令:sleep 0.25,也就是各自停 0.25 秒。模型模拟回复里一次发出两个 shell_command 调用。测试运行这一轮并测量总耗时,如果总时间没有明显变成两倍,就说明它们是并行跑的。
调用关系:这是专门测真实 shell 工具的测试用例。它自己用 test_codex 搭建环境,用 mount_sse_sequence 安排模型事件,用 run_turn_and_measure 触发并计时,再用 assert_parallel_duration 做最终判断。
调用图:调用 6 个内部函数(mount_sse_sequence, sse, start_mock_server, test_codex, assert_parallel_duration, run_turn_and_measure);外部调用 4 个(json!, to_string, skip_if_no_network!, vec!)。
mixed_parallel_tools_run_in_parallel189–222 ↗
async fn mixed_parallel_tools_run_in_parallel() -> anyhow::Result<()>
作用:验证不同种类的工具也能一起跑,而不是因为类型不同就互相等待。这里混合了测试同步工具和 shell 命令。
数据流:它启动假服务器,构建带测试工具的 Codex。随后准备一个 test_sync_tool 调用,让它睡约 300 毫秒;再准备一个 shell_command,让 shell 睡约 0.25 秒。模型模拟回复一次性发出这两个调用。测试记录整轮耗时,并判断是否在并行范围内完成。
调用关系:它补上了“混合工具”的场景。它使用 build_codex_with_test_tool 准备能跑测试工具的环境,用 mount_sse_sequence 安排模型先发工具调用再给最终消息,最后通过 run_turn_and_measure 和 assert_parallel_duration 验证结果。
调用图:调用 6 个内部函数(mount_sse_sequence, sse, start_mock_server, assert_parallel_duration, build_codex_with_test_tool, run_turn_and_measure);外部调用 4 个(json!, to_string, skip_if_no_network!, vec!)。
tool_results_grouped225–300 ↗
async fn tool_results_grouped() -> anyhow::Result<()>
作用:检查多个工具结果回传给模型时,格式和顺序是否规整。重点是:先列出所有工具调用,再列出所有工具输出,并且输出要按调用顺序对应。
数据流:它启动假服务器和 Codex,准备三次相同的 shell_command,命令会输出一行文本。第一段模拟模型回复发出三次工具调用;第二段模拟回复用于接收工具输出后的继续回答。运行结束后,它取出发给服务器的下一次输入,筛出 function_call 和 function_call_output,检查数量都是 3,检查所有调用都排在所有输出前面,再检查每个输出的 call_id 和对应调用一致。
调用关系:它关注的不是速度,而是回传给模型的数据形状。它用 mount_sse_once 分两次挂响应,用 run_turn 触发流程,然后直接检查假服务器收到的请求内容。assert_eq 和 assert 在这里充当验收规则。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, build_codex_with_test_tool, run_turn);外部调用 6 个(assert!, assert_eq!, json!, to_string, skip_if_no_network!, vec!)。
shell_tools_start_before_response_completed_when_stream_delayed303–437 ↗
async fn shell_tools_start_before_response_completed_when_stream_delayed() -> anyhow::Result<()>
作用:验证当模型的流式回复还没宣布“完成”时,shell 工具也会立刻开始执行。这样系统不用傻等整段回复结束,能更早干活。
数据流:它创建一个临时文件,让四条 shell 命令把当前毫秒时间戳写进去。接着搭建一个可控的流式 SSE 服务器,用几个“闸门”控制事件什么时候放出来:先放出 response.created 和四个 shell 调用,但暂时不放 response.completed。测试提交用户输入后打开第一个闸门,让工具调用到达;再等待临时文件里出现四个时间戳。确认四个工具都已启动后,才放出 completed 事件。最后它拿服务器记录的完成时间和四个工具时间戳比较,确认工具启动时间不晚于响应完成时间,并关闭服务器。
调用关系:这是最细的时序测试。它没有用 run_turn,而是手写提交逻辑,因为需要精确控制流式事件的释放时间。它借 start_streaming_sse_server 搭建带闸门的假服务器,借 local_selections 和 turn_permission_fields 填好本轮配置,最后用 wait_for_event 等 Codex 收到 TurnComplete。
调用图:调用 5 个内部函数(sse, start_streaming_sse_server, local_selections, test_codex, turn_permission_fields);外部调用 16 个(default, from_millis, from_secs, assert!, assert_eq!, wait_for_event, format!, read_to_string, try_from, json! (+6 more))。
core/tests/suite/truncation.rs源码 ↗
这个文件像一套“超长输出压力测试”。测试会假装模型要求 Codex 去运行 shell 命令,或调用 MCP 工具(MCP 可以理解成让模型使用外部工具的一套接口)。然后它故意制造很长的输出,比如打印 1 到 100000,看看 Codex 发回给模型的结果是否按规则缩短。它还检查不同模型使用不同截断策略:有的按字符估算,有的按 token(模型理解文本的小块)估算;截断提示里也要写对是删了多少字符还是多少 token。文件还覆盖了例外情况:如果配置给了更大的预算,就不该截断;如果 MCP 返回图片,就要保留图片,不要硬塞文字截断说明。整体上,这些测试保护的是一个很现实的问题:工具可以很啰嗦,但模型只能吃有限长度的内容。
assert_wall_time_header34–40 ↗
fn assert_wall_time_header(output: &str)
作用:检查一段输出开头是不是标准的“耗时信息 + Output 标记”。这能保证 MCP 图片输出里的文字头格式稳定,不会因为格式改动让后续模型读不懂。
数据流:输入是一段文本 → 它把第一行和后面的 Output 标记拆开 → 用正则表达式确认第一行长得像“Wall time: 数字 seconds”,再确认第二行正好是“Output:”;它不返回新数据,只在格式不对时让测试失败。
调用关系:它是本文件里的小检查工具,只被 mcp_image_output_preserves_image_and_no_text_summary 使用。图片测试拿到 MCP 输出数组后,把第一个文字项交给它确认格式。
调用图:被 1 处调用(mcp_image_output_preserves_image_and_no_text_summary);外部调用 2 个(assert_eq!, assert_regex_match)。
tool_call_output_configured_limit_chars_type45–119 ↗
async fn tool_call_output_configured_limit_chars_type() -> Result<()>
作用:验证当配置把工具输出预算调得很大时,普通 shell 工具的大输出不会被默认截得太短。这里重点还确认输出仍然是普通文本,不是被包装成 JSON。
数据流:测试先启动假服务器,再创建一个使用 gpt-5.2 的 Codex 测试实例,并把 tool_output_token_limit 设成 100000 → 假模型要求执行会产生大量数字的 shell 命令 → Codex 执行后把结果发回假服务器 → 测试读取这次发回的 function_call_output,确认它是纯文本、长度接近大预算允许的范围,并且没有“tokens truncated”这类 token 截断提示。
调用关系:它由异步测试运行器启动。它用 start_mock_server 和 mount_sse_once 搭出两轮假模型响应,用 test_codex 创建被测 Codex,再通过 submit_turn_with_permission_profile 触发真实流程,最后检查第二轮请求里 Codex 发给模型的工具输出。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 5 个(assert!, cfg!, json!, skip_if_no_network!, vec!)。
tool_call_output_exceeds_limit_truncated_chars_limit124–196 ↗
async fn tool_call_output_exceeds_limit_truncated_chars_limit() -> Result<()>
作用:验证 gpt-5.2 这类按字符上限处理的场景里,超长 shell 输出会被截短到大约 1 万字符,并带上“删了多少字符”的提示。
数据流:输入是一个会打印 100000 行数字的 shell 命令,以及默认输出限制 → Codex 按假模型指令运行命令 → 结果太长,于是在发回模型前保留开头和结尾,中间插入“chars truncated”提示 → 测试拿到发回内容,检查格式、总行数、截断提示和最终长度都符合预期。
调用关系:它由测试框架直接执行。假服务器先让模型发起 shell_command,再让模型结束回合;测试中的 mock2 捕获 Codex 第二次请求,从那里验证截断后的工具输出。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 6 个(assert!, cfg!, assert_regex_match, json!, skip_if_no_network!, vec!)。
tool_call_output_exceeds_limit_truncated_for_model201–277 ↗
async fn tool_call_output_exceeds_limit_truncated_for_model() -> Result<()>
作用:验证 gpt-5.4 这类按 token 预算处理的模型,收到超长 shell 输出前会被按模型可承受的长度截短。token 可以理解成模型读文本时使用的小片段。
数据流:测试准备一个会输出 100000 行数字的命令 → 假模型要求 Codex 执行它 → Codex 得到巨大文本后,按 gpt-5.4 的 token 规则截掉中间大段内容 → 测试读取发回模型的文本,确认开头、结尾还在,中间出现“tokens truncated”提示,并且报告的 token 数符合预期。
调用关系:它和前一个字符策略测试很像,但把模型换成 gpt-5.4。它依靠 mock SSE 响应驱动一次工具调用,再检查 Codex 发给第二轮模型请求的 function_call_output。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 6 个(assert!, cfg!, assert_regex_match, json!, skip_if_no_network!, vec!)。
tool_call_output_truncated_only_once281–337 ↗
async fn tool_call_output_truncated_only_once() -> Result<()>
作用:确认同一份 shell 输出只会被截断一次,不会出现多个“已截断”提示。这样模型看到的内容不会又乱又重复。
数据流:测试让 shell 输出 10000 行数字 → Codex 按模型要求执行并截短输出 → 测试读取最终发回模型的文本,数一数“tokens truncated”出现了几次 → 期望正好一次;如果多次出现,说明截断流程可能重复跑了。
调用关系:它由测试运行器执行,使用假服务器安排一次 shell_command 调用和一次完成响应。它不关心具体截掉多少,只检查整个工具输出格式化链路里没有重复追加截断标记。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 5 个(assert_eq!, cfg!, json!, skip_if_no_network!, vec!)。
mcp_tool_call_output_exceeds_limit_truncated_for_model342–441 ↗
async fn mcp_tool_call_output_exceeds_limit_truncated_for_model() -> Result<()>
作用:验证 MCP 工具返回很长文本时,Codex 也会在发回模型前截短。MCP 是外部工具接入协议,这里测试的不是 shell,而是通过 MCP 服务器调用 echo 工具。
数据流:测试先构造一个很长的 message 参数 → 启动假模型服务器,并编译/配置一个本地 stdio MCP 测试服务器 → 假模型要求调用 mcp__rmcp.echo → MCP 返回很长 JSON 文本 → Codex 给它加上耗时头,再按 500 token 的小预算截短 → 测试确认没有 shell 专属的“Total output lines”头,并且输出里有 token 截断提示且总长度受控。
调用关系:它把假模型服务器、Codex 测试实例、本地 MCP 测试服务器串起来。wait_for_mcp_server 确认 MCP 服务已连上后,submit_turn_with_permission_profile 触发调用,最后从 mock2 捕获的请求里检查 Codex 发回模型的 MCP 工具结果。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, read_only);外部调用 8 个(assert!, assert_regex_match, stdio_server_bin, wait_for_mcp_server, format!, json!, skip_if_no_network!, vec!)。
mcp_image_output_preserves_image_and_no_text_summary446–568 ↗
async fn mcp_image_output_preserves_image_and_no_text_summary() -> Result<()>
作用:验证 MCP 工具返回图片时,图片会被原样保留下来,而且不会因为没有文字可截断就多加一段没意义的截断说明。
数据流:测试准备一个 1x1 PNG 的 data URL,并把它通过环境变量交给 MCP 测试服务器 → 假模型要求调用 image 工具 → Codex 收到 MCP 返回的图片内容后,把输出整理成数组:第一个是耗时文字,第二个是图片项 → 测试确认数组只有这两项,图片 URL 和 detail 完全正确,没有额外截断摘要。
调用关系:这是本文件里最接近真实用户输入流程的测试:它直接向 fixture.codex.submit 发送 UserInput,并设置权限、沙箱和本地环境选择。完成后,它用 wait_for_event 等到回合结束,再调用 assert_wall_time_header 检查第一个输出项。
调用图:调用 7 个内部函数(mount_sse_once, sse, start_mock_server, local_selections, test_codex, assert_wall_time_header, read_only);外部调用 9 个(default, assert!, assert_eq!, stdio_server_bin, wait_for_event, wait_for_mcp_server, format!, skip_if_no_network!, vec!)。
token_policy_marker_reports_tokens572–619 ↗
async fn token_policy_marker_reports_tokens() -> Result<()>
作用:验证按 token 截断时,提示文字里说的是“tokens truncated”,而不是字符或字节。这样模型和开发者都能知道限制依据是什么。
数据流:测试把模型设为 gpt-5.4,并把工具输出预算设得很小 → shell 命令输出 1 到 150 → Codex 截掉中间部分,但保留前后数字 → 测试用正则确认中间标记包含 tokens truncated,且开头和结尾的数字都还在。
调用关系:它由测试运行器执行,通过假 SSE 响应让模型发起 shell_command。它重点验证截断标记的“单位”是否跟模型策略一致。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 4 个(assert_regex_match, json!, skip_if_no_network!, vec!)。
byte_policy_marker_reports_bytes623–670 ↗
async fn byte_policy_marker_reports_bytes() -> Result<()>
作用:验证按字符/字节估算截断时,提示文字里说的是“chars truncated”。这避免把字符策略误说成 token 策略。
数据流:测试使用 gpt-5.2,并设置很小的输出预算 → shell 输出 1 到 150 → Codex 按约 200 字节的限制截短中间内容 → 测试检查最终文本里出现 chars truncated,并且输出仍然保留前面的 1 到 19 和后面的 129 到 150。
调用关系:它和 token_policy_marker_reports_tokens 是一对对照测试。两者流程几乎一样,但模型不同,因此期望的截断提示单位不同。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 4 个(assert_regex_match, json!, skip_if_no_network!, vec!)。
shell_command_output_not_truncated_with_custom_limit674–730 ↗
async fn shell_command_output_not_truncated_with_custom_limit() -> Result<()>
作用:确认用户把 shell 工具输出预算调大后,较大的 shell 输出应该完整保留。这个测试防止配置项看起来存在、实际却不起作用。
数据流:测试把 gpt-5.4 的 tool_output_token_limit 设为 50000 → shell 命令输出 1 到 1000,并提前生成完整期望文本 → Codex 执行命令后把结果发回模型 → 测试确认最终输出以完整的 1 到 1000 结尾,并且完全没有 truncated 字样。
调用关系:它由测试框架启动,用假模型服务器安排一次工具调用。它验证配置层和工具输出格式化层之间的配合:配置给了足够预算,后面的截断逻辑就不该动这份输出。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 4 个(assert!, json!, skip_if_no_network!, vec!)。
mcp_tool_call_output_not_truncated_with_custom_limit734–830 ↗
async fn mcp_tool_call_output_not_truncated_with_custom_limit() -> Result<()>
作用:确认 MCP 工具的大输出在用户提高预算后也能完整发回模型,而不是只有 shell 工具享受这个配置。
数据流:测试构造 80000 个字符的消息 → 配置本地 MCP stdio 服务器,并把 tool_output_token_limit 提高到 50000 → 假模型请求调用 echo 工具 → MCP 返回完整大文本,Codex 加上耗时头后发回模型 → 测试确认总长度正好是预期长度,并且没有任何 truncated 标记。
调用关系:它覆盖 MCP 路径上的“不截断”分支。测试先用 stdio_server_bin 准备 MCP 测试服务器,再用 wait_for_mcp_server 等它可用,最后通过 read_only 权限提交用户回合并检查 mock2 捕获到的 function_call_output。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, read_only);外部调用 8 个(assert!, assert_eq!, stdio_server_bin, wait_for_mcp_server, format!, json!, skip_if_no_network!, vec!)。
MCP、应用与插件
这些套件覆盖 MCP、应用、插件以及扩展提供的工具中的延迟工具发现和调用,包括文件上传和安装流程。
core/tests/suite/extension_sandbox.rs源码 ↗
这份测试像是在给扩展做“门禁检查”。沙盒权限可以理解成一圈围栏,规定程序能看哪些文件、不能看哪些文件。第一个测试故意放一个被禁止读取的图片路径,让模型调用图片扩展,确认扩展拿不到文件时会把错误消息传回去,而不是绕过限制。第二个测试模拟模型先申请权限,用户只在本轮对话里批准访问某个图片目录,然后扩展再读取图片并调用图片编辑接口,最后确认输出里真的带回了图片数据。这里还用了假的网络服务器和假的模型流式事件,避免测试依赖真实服务,同时能精确检查每一步发生了什么。
image_generation_extensions49–54 ↗
fn image_generation_extensions(auth: &CodexAuth) -> Arc<ExtensionRegistry<Config>>
作用:这个小工具函数搭好一个只用于测试的扩展注册表,并把图片生成扩展装进去。测试需要它来模拟真实运行时“系统已经安装了图片扩展”的状态。
数据流:进去的是一个测试用的登录身份 CodexAuth → 函数先把这个身份变成扩展可用的认证管理器,再新建扩展注册表构建器,把图片生成扩展安装进去 → 出来的是一个可共享的扩展注册表,后面的测试会把它交给测试版 Codex 实例使用。
调用关系:它被两个测试函数共同调用。两个测试在启动测试版 Codex 之前,都会先用它准备好图片生成扩展;它内部把认证准备工作交给 auth_manager_from_auth,再调用图片扩展的安装函数完成注册。
调用图:调用 1 个内部函数(auth_manager_from_auth);被 2 处调用(extension_tool_receives_turn_environment_sandbox, extension_tool_uses_granted_turn_permissions);外部调用 4 个(new, new, install, clone)。
extension_tool_receives_turn_environment_sandbox57–139 ↗
async fn extension_tool_receives_turn_environment_sandbox() -> Result<()>
作用:这个测试确认:当某张图片被当前回合的沙盒规则明确禁止读取时,图片扩展不能读它,并且会把“无法读取”的错误传回模型流程里。
数据流:测试先启动一个假的服务器,创建测试登录身份和图片扩展,再配置一个支持文字和图片输入的测试模型环境 → 它在工作目录里写入一个 denied.png,随后设置文件沙盒规则,把这个文件标成“拒绝访问” → 假服务器模拟模型发来一次图片编辑工具调用,引用这张被禁止的图片 → Codex 执行这一轮后,测试读取发给假服务器的工具输出,确认里面是以“无法读取该图片路径”开头的错误信息。
调用关系:这是一个完整场景测试。它先调用 image_generation_extensions 准备扩展,再用测试辅助工具启动假服务器、搭建 Codex、挂载模拟的流式响应。图片扩展被模型工具调用间接触发,测试最后检查扩展返回给模型的结果是否符合沙盒限制。
调用图:调用 7 个内部函数(mount_sse_sequence, start_mock_server, test_codex, image_generation_extensions, create_dummy_chatgpt_auth_for_testing, from_runtime_permissions, default);外部调用 4 个(assert!, skip_if_no_network!, write, vec!)。
extension_tool_uses_granted_turn_permissions142–305 ↗
async fn extension_tool_uses_granted_turn_permissions() -> Result<()>
作用:这个测试确认:如果模型先申请访问权限,而用户只在当前回合批准了这项权限,图片扩展就可以在这一回合读取被授权的图片并完成编辑。
数据流:测试先启动假的服务器,并准备一个假的图片编辑接口返回固定的 base64 图片数据 → 它创建测试版 Codex,开启“请求权限工具”,并设置基础权限为只能写工作区、网络受限 → 接着在临时目录写入一张小 PNG 图片,模拟模型先调用 request_permissions 申请读取这个目录,再调用图片扩展编辑图片 → 测试收到权限申请事件后,提交“本回合有效”的批准 → 回合结束后,测试检查图片扩展的输出,确认它返回了 input_image,并且图片地址是预期的 data:image/png;base64,cG5n。
调用关系:它是第二个完整场景测试,比第一个多了“申请权限—用户批准—再使用权限”的流程。它调用 image_generation_extensions 准备图片扩展,用 turn_permission_fields 和 local_selections 拼出当前回合的运行环境,再通过 Codex 的提交接口发送用户输入和权限批准;图片扩展最终会把活交给假的图片编辑接口,测试则检查整个链条是否走通。
调用图:调用 9 个内部函数(mount_sse_sequence, start_mock_server, local_selections, test_codex, turn_permission_fields, image_generation_extensions, create_dummy_chatgpt_auth_for_testing, from_read_write_roots, workspace_write_with);外部调用 16 个(default, given, new, new, default, assert_eq!, wait_for_event, json!, panic!, skip_if_no_network! (+6 more))。
core/tests/suite/openai_file_mcp.rs源码 ↗
这份测试像一次彩排:用户让 Codex 调用某个应用工具去读取 report.txt,测试会假装有一个 OpenAI 服务、一个应用工具服务、一个文件上传服务在旁边配合。重点不是测试真实网络,而是确认流程对不对:模型先看到工具参数说明,知道 file 参数要填本地绝对路径;系统发现工具真正需要文件后,会先走“申请上传地址→上传文件→确认上传完成”这三步;然后再调用应用工具,并把原来的文件路径换成一个文件对象,里面有 file_id、download_url、文件名、大小等信息。第二个测试还加了一个 PostToolUse 钩子,也就是“工具调用结束后自动运行的小脚本”,确认这个脚本收到的输入里,file 也已经是上传后的文件对象。没有这些测试,文件参数可能只传了路径,远端工具或钩子就会打不开文件,尤其是大文件和远程环境里更容易出错。
write_post_tool_use_hook42–80 ↗
fn write_post_tool_use_hook(home: &Path) -> Result<()>
作用:这个函数给测试临时造一个“工具调用后运行”的钩子脚本。它的作用是让测试能看到:工具用完以后,系统到底把什么内容传给了钩子。
数据流:进去的是一个临时的 home 目录路径。函数在这个目录里写入一个 Python 脚本和一个 hooks.json 配置文件;脚本会从标准输入读取系统传来的 JSON,把它追加写到日志文件里,然后再返回一段额外上下文。出来的是成功或失败结果;成功后,临时目录里多了钩子脚本、钩子配置和之后会被写入的日志位置。
调用关系:它主要服务于测试 codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook。测试构建 Codex 环境之前会调用它准备钩子,后面的工具调用结束时,Codex 会按 hooks.json 去执行这个脚本,测试再读取脚本留下的日志来验收。
read_post_tool_use_hook_inputs82–89 ↗
fn read_post_tool_use_hook_inputs(home: &Path) -> Result<Vec<Value>>
作用:这个函数读取钩子脚本留下的日志,把每一行 JSON 还原成测试能比较的数据。它让测试不用猜钩子收到了什么,而是直接检查实际记录。
数据流:进去的是临时 home 目录路径。函数打开 post_tool_use_hook_log.jsonl,逐行读取,跳过空行,把每行 JSON 文本解析成 serde_json::Value。出来的是一个 JSON 列表;如果文件读不到或某一行不是合法 JSON,就返回错误。
调用关系:它在 codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook 的最后阶段使用。前面 write_post_tool_use_hook 负责准备会写日志的脚本,run_extract_turn 触发工具和钩子执行,这个函数则把钩子留下的证据取回来给断言使用。
调用图:被 1 处调用(codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook);外部调用 2 个(join, read_to_string)。
uploaded_file91–100 ↗
fn uploaded_file(server: &MockServer, file_size_bytes: u64) -> Value
作用:这个函数生成一份“上传成功后的文件信息”模板。测试用它来判断应用工具和钩子收到的文件对象是不是长得完全正确。
数据流:进去的是模拟服务器地址和文件大小。函数把它们拼进一个 JSON 对象,里面包括下载地址、file_id、文件类型、文件名、内部 uri 和字节大小。出来的是这份 JSON 值,不会改动外部状态。
调用关系:它是多个断言共用的小助手。文件上传接口的模拟响应和应用工具调用结果都围绕同一个 file_123 展开,测试用 uploaded_file 生成期望值,避免每个地方手写一遍容易写错的 JSON。
调用图:外部调用 1 个(json!)。
mount_file_upload_mocks102–137 ↗
async fn mount_file_upload_mocks(server: &MockServer, file_size_bytes: u64)
作用:这个函数在模拟服务器上装好一整套“文件上传接口”的假响应。它让测试可以验证系统真的按正确顺序、带正确内容去上传文件,而不用连接真实服务。
数据流:进去的是模拟服务器和文件大小。函数设置三个预期请求:先 POST /files 申请上传地址,再 PUT /upload/file_123 上传文件内容,最后 POST /files/file_123/uploaded 确认上传完成。每个预期都检查方法、路径、部分请求头或请求体,并返回预设的 JSON 或成功状态。出来没有普通返回值;它改变的是模拟服务器的行为和待验证请求清单。
调用关系:两个测试都会先调用它布置上传流程。之后 run_extract_turn 触发 Codex 的实际工具调用,Codex 内部应该命中这些模拟接口;测试结尾的 server.verify 会确认这些上传请求确实发生过且只发生了预期次数。
调用图:被 2 处调用(codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook, codex_apps_file_params_upload_environment_files_before_mcp_tool_call);外部调用 7 个(given, new, json!, body_json, header, method, path)。
run_extract_turn139–170 ↗
async fn run_extract_turn(test: &TestCodex, server: &MockServer) -> Result<ResponseMock>
作用:这个函数模拟一次用户对话:用户要求提取 report.txt,模型先发起应用工具调用,随后回复 done。它把两段测试都需要的“对话流程”收成一个可复用步骤。
数据流:进去的是 TestCodex 测试实例和模拟服务器。函数先在服务器上挂载一串 SSE 响应;SSE 是服务器不断推送事件的流式格式,这里用来假装模型返回“创建响应、调用工具、完成、再回复消息”。然后它向 Codex 提交一句用户请求,并设置不需要审批、权限关闭。出来的是 ResponseMock,里面能查看 Codex 发给模型接口的请求记录。
调用关系:两个测试都通过它启动真正的被测流程。它把工作交给 mount_sse_sequence 准备模型假响应,再调用 TestCodex 的 submit_turn_with_approval_and_permission_profile 发起一轮对话;随后测试用返回的 mock 检查模型请求,或用服务器记录检查应用工具调用。
调用图:调用 2 个内部函数(mount_sse_sequence, submit_turn_with_approval_and_permission_profile);被 2 处调用(codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook, codex_apps_file_params_upload_environment_files_before_mcp_tool_call);外部调用 1 个(vec!)。
codex_apps_file_params_upload_environment_files_before_mcp_tool_call173–227 ↗
async fn codex_apps_file_params_upload_environment_files_before_mcp_tool_call() -> Result<()>
作用:这个测试确认:当应用工具的参数是本地文件时,Codex 会在调用 MCP 工具前先上传文件,并把工具参数从“路径”替换成“上传后的文件对象”。MCP 可以理解成一种让模型调用外部工具的协议。
数据流:开始时它启动模拟服务器和应用工具服务器,设置文件上传假接口,并在测试工作区里写入一个 13MB 的 report.txt。接着构建带远程环境的 Codex,运行一次提取文本的对话。之后它检查两件事:发给模型的工具说明里,file 参数明确要求绝对本地路径;真正发给应用工具的参数里,file 已变成包含下载地址、文件编号、大小等信息的 JSON 对象,同时还带有调用编号、资源地址、连接器等元数据。最后验证所有模拟上传接口都被正确调用。
调用关系:这是本文件的主场景测试之一。它依赖 start_mock_server 和 AppsTestServer 准备外部世界,依赖 mount_file_upload_mocks 准备上传接口,依赖 run_extract_turn 触发对话,再用 recorded_apps_tool_call_by_name 取回应用工具收到的真实参数做比较。
调用图:调用 6 个内部函数(mount, apps_enabled_builder, recorded_apps_tool_call_by_name, start_mock_server, mount_file_upload_mocks, run_extract_turn);外部调用 2 个(assert_eq!, format!)。
codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook230–257 ↗
async fn codex_apps_file_params_pass_uploaded_file_to_post_tool_use_hook() -> Result<()>
作用:这个测试确认:应用工具调用结束后,如果有 PostToolUse 钩子脚本,它收到的 tool_input.file 也应该是上传后的文件对象,而不是原始本地路径。
数据流:开始时它启动模拟服务器和应用工具服务器,设置一个 11 字节文件的上传假接口。构建 Codex 前,它写入钩子脚本和 hooks.json,并把配置改成信任发现到的钩子。构建完成后,它在当前目录写入 report.txt,运行一次提取文本的对话。工具和钩子执行完后,它读取钩子日志,确认只有一次输入,并且其中 tool_input.file 等于预期的上传文件 JSON。最后验证模拟上传请求都按预期发生。
调用关系:这是本文件的第二个主场景测试,关注点从“应用工具收到什么”延伸到“工具后的钩子收到什么”。它先让 write_post_tool_use_hook 准备记录输入的脚本,再用 run_extract_turn 触发流程,最后用 read_post_tool_use_hook_inputs 读取证据并断言。
调用图:调用 6 个内部函数(mount, apps_enabled_builder, start_mock_server, mount_file_upload_mocks, read_post_tool_use_hook_inputs, run_extract_turn);外部调用 2 个(assert_eq!, write)。
core/tests/suite/plugins.rs源码 ↗
插件会影响模型看到的“开发者消息”和可调用工具,所以一旦顺序、权限或来源标记错了,模型就可能用错工具,或者把不该暴露的能力暴露出去。这个测试文件像搭了一个小型排练场:先在临时 home 目录里写出假的插件配置、技能文件、MCP 配置和 App 配置;再启动假 ChatGPT 服务和假 Apps 服务;最后让 Codex 收到用户输入或插件提及,并检查发给模型的请求内容。这里还特别区分两种登录方式:ChatGPT 登录和 API Key 登录。前者更偏向使用 Apps 工具,后者更偏向使用 MCP 工具。文件还检查插件被显式使用时,会不会正确发出 analytics(使用统计)事件。
sample_plugin_root33–35 ↗
fn sample_plugin_root(home: &TempDir) -> std::path::PathBuf
作用:算出测试插件应该放在哪个目录里。这样其他测试函数不用重复拼路径,也能保证所有假插件都放在同一个约定位置。
数据流:输入是一个临时 home 目录 → 它在这个目录后面拼上 plugins/cache/test/sample/local → 输出这个插件根目录路径,不写文件也不改状态。
调用关系:它是最底层的小帮手,只被 write_sample_plugin_manifest_and_config 使用。后者需要先知道插件根目录在哪里,才能往里面写 manifest 和配置。
调用图:被 1 处调用(write_sample_plugin_manifest_and_config);外部调用 1 个(path)。
write_sample_plugin_manifest_and_config37–55 ↗
fn write_sample_plugin_manifest_and_config(home: &TempDir) -> std::path::PathBuf
作用:在临时目录里造出一个最基本的可用插件。它会写插件说明文件和 Codex 配置,让测试里的 Codex 真的能发现并启用这个插件。
数据流:输入是临时 home 目录 → 它先调用 sample_plugin_root 算出插件位置,再创建 .codex-plugin 目录,写 plugin.json,并在 home 下写 config.toml 打开 plugins 功能和 sample@test 插件 → 输出插件根目录,同时磁盘上多了一套基础插件文件。
调用关系:这是多个测试插件搭建函数的共同地基。write_plugin_skill_plugin、write_plugin_mcp_plugin 和 write_plugin_app_plugin_with_name 都先调用它,再追加各自要测试的技能、MCP 或 App 配置。
调用图:调用 1 个内部函数(sample_plugin_root);被 3 处调用(write_plugin_app_plugin_with_name, write_plugin_mcp_plugin, write_plugin_skill_plugin);外部调用 4 个(path, format!, create_dir_all, write)。
write_plugin_skill_plugin57–67 ↗
fn write_plugin_skill_plugin(home: &TempDir) -> std::path::PathBuf
作用:给测试插件加一个“技能”文件。技能可以理解成插件告诉模型的一段能力说明,让模型知道这个插件会做什么。
数据流:输入是临时 home 目录 → 它先写好基础插件配置,再创建 skills/sample-search 目录,并写入 SKILL.md,里面带有描述 inspect sample data → 输出这个 SKILL.md 的路径,同时磁盘上出现一个插件技能。
调用关系:多个测试在模拟“带技能的插件”时会调用它。之后 Codex 启动并读取这些文件,测试函数再检查开发者消息里是否正确出现技能说明和命名空间。
调用图:调用 1 个内部函数(write_sample_plugin_manifest_and_config);被 5 处调用(capability_sections_render_in_developer_message_in_order, explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth, explicit_plugin_mentions_track_plugin_used_analytics, explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins, explicit_plugin_mentions_use_mcp_for_api_key_dual_surface_plugins);外部调用 2 个(create_dir_all, write)。
write_plugin_mcp_plugin69–86 ↗
fn write_plugin_mcp_plugin(home: &TempDir, command: &str)
作用:给测试插件加一个 MCP 服务器配置。MCP 可以理解成一套外部工具接口,模型可以通过它调用插件提供的工具。
数据流:输入是临时 home 目录和要启动的命令路径 → 它先写基础插件配置,再写 .mcp.json,里面声明名为 sample 的 MCP 服务器、启动命令、工作目录和超时时间 → 没有返回值,但磁盘上多了 MCP 配置。
调用关系:需要验证插件 MCP 工具显隐规则的测试会调用它。测试随后等待 MCP 服务器启动,并检查请求里是否包含或隐藏对应工具。
调用图:调用 1 个内部函数(write_sample_plugin_manifest_and_config);被 3 处调用(explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth, explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins, explicit_plugin_mentions_use_mcp_for_api_key_dual_surface_plugins);外部调用 2 个(format!, write)。
write_plugin_app_plugin88–90 ↗
fn write_plugin_app_plugin(home: &TempDir)
作用:给测试插件加一个默认名为 sample 的 App 配置。App 在这里指 ChatGPT 侧的连接器能力,比如日历工具。
数据流:输入是临时 home 目录 → 它把工作交给 write_plugin_app_plugin_with_name,并使用默认 App 名 sample → 没有返回值,但会让插件声明一个 App。
调用关系:这是一个省事的包装函数。大多数测试只需要默认名字,就调用它;如果要测试 App 名不冲突的情况,则直接调用 write_plugin_app_plugin_with_name。
调用图:调用 1 个内部函数(write_plugin_app_plugin_with_name);被 3 处调用(capability_sections_render_in_developer_message_in_order, explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins, explicit_plugin_mentions_use_mcp_for_api_key_dual_surface_plugins)。
write_plugin_app_plugin_with_name92–107 ↗
fn write_plugin_app_plugin_with_name(home: &TempDir, app_name: &str)
作用:给测试插件写入指定名字的 App 配置。它让测试可以控制 App 名称,用来验证名称冲突和不冲突时行为是否不同。
数据流:输入是临时 home 目录和 App 名字 → 它先写基础插件配置,再写 .app.json,声明这个 App 名字对应 id 为 calendar 的应用 → 没有返回值,但磁盘上多了 App 配置。
调用关系:write_plugin_app_plugin 会用默认名字调用它;专门测试 MCP 和 App 不冲突的用例会直接调用它并传入 sample_app。
调用图:调用 1 个内部函数(write_sample_plugin_manifest_and_config);被 2 处调用(explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth, write_plugin_app_plugin);外部调用 2 个(format!, write)。
build_analytics_plugin_test_codex109–125 ↗
async fn build_analytics_plugin_test_codex(
server: &MockServer,
codex_home: Arc<TempDir>,
) -> Result<TestCodex>
作用:搭建一个用于测试插件使用统计的 Codex 实例。它使用假的 ChatGPT 登录,并指定模型名,方便后面检查统计事件里有没有正确记录这些信息。
数据流:输入是假服务器和临时 home 目录 → 它取出服务器地址,创建测试 Codex 构建器,设置 home、假 ChatGPT 认证、模型 gpt-5.2 和 chatgpt_base_url → 输出建好的 TestCodex,如果搭建失败就返回错误。
调用关系:它只服务于 explicit_plugin_mentions_track_plugin_used_analytics。那个测试需要一个能把请求打到假服务器上的 Codex,然后从假服务器收到的请求里找 analytics 事件。
调用图:调用 2 个内部函数(test_codex, create_dummy_chatgpt_auth_for_testing);被 1 处调用(explicit_plugin_mentions_track_plugin_used_analytics);外部调用 1 个(uri)。
build_apps_enabled_plugin_test_codex127–146 ↗
async fn build_apps_enabled_plugin_test_codex(
server: &MockServer,
codex_home: Arc<TempDir>,
chatgpt_base_url: String,
) -> Result<TestCodex>
作用:搭建一个启用了 Apps 功能的测试 Codex。它用于检查插件里的 App 工具在 ChatGPT 登录场景下是否会正确暴露给模型。
数据流:输入是假服务器、临时 home 目录和 ChatGPT 基础地址 → 它创建测试 Codex 构建器,设置 home、假 ChatGPT 认证,并在配置里打开 Apps 功能和服务地址 → 输出可运行的 TestCodex。
调用关系:多个 Apps 相关测试都会用它。测试先准备插件文件和假服务器,再调用它启动 Codex,随后提交用户输入并检查发出的模型请求。
调用图:调用 2 个内部函数(test_codex, create_dummy_chatgpt_auth_for_testing);被 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)。
tool_names148–163 ↗
fn tool_names(body: &serde_json::Value) -> Vec<String>
作用:从请求 JSON 里提取所有工具名字。测试用它快速判断某个工具有没有被发给模型。
数据流:输入是一段 JSON 请求体 → 它读取 tools 数组,对每个工具优先取 name,没有 name 就取 type,并收集成字符串列表 → 输出工具名列表;如果没有 tools,就输出空列表。
调用关系:它被多个显式插件提及测试使用。那些测试拿到假服务器记录的请求后,用它检查 Apps 工具或 MCP 工具是可见还是被隐藏。
调用图:被 3 处调用(explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth, explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins, explicit_plugin_mentions_use_mcp_for_api_key_dual_surface_plugins);外部调用 1 个(get)。
capability_sections_render_in_developer_message_in_order166–233 ↗
async fn capability_sections_render_in_developer_message_in_order() -> Result<()>
作用:测试开发者消息里的能力说明顺序是否正确:Apps 在前,Skills 居中,Plugins 在后。顺序很重要,因为模型会按这些说明理解自己能用什么。
数据流:它启动假服务器和假 Apps 服务,写入带技能和 App 的插件,构建启用 Apps 的 Codex → 向 Codex 提交一条普通文本 hello → 等待一轮完成后取出发给模型的 developer 消息,检查章节顺序、插件描述是否没有误出现、插件技能是否带上命名空间说明。
调用关系:这是一个完整端到端测试。它调用写插件文件的帮手和 build_apps_enabled_plugin_test_codex 搭场景,再通过假服务器捕获请求,最后用断言确认 Codex 组装提示词的结果。
调用图:调用 7 个内部函数(mount_with_connector_name, mount_sse_once, sse, start_mock_server, build_apps_enabled_plugin_test_codex, write_plugin_app_plugin, write_plugin_skill_plugin);外部调用 8 个(clone, new, default, new, assert!, wait_for_event, skip_if_no_network!, vec!)。
explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins236–323 ↗
async fn explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins() -> Result<()>
作用:测试同一个插件同时声明 MCP 和 App 时,在 ChatGPT 登录下显式提及插件会优先使用 App。这样可以避免同一能力通过两条路重复暴露。
数据流:它启动假服务器和 Apps 服务,写入技能、MCP 和 App 三种插件内容,构建启用 Apps 的 Codex,并等待 Apps MCP 服务器可用 → 提交一个 plugin://sample@test 的插件提及 → 等待完成后检查 developer 消息:技能和 App 指引出现,MCP 指引被压掉;再检查工具列表:日历 App 工具出现,sample MCP echo 工具不出现,并确认 App 工具描述里写明来自 sample 插件。
调用关系:它把 write_plugin_skill_plugin、write_plugin_mcp_plugin、write_plugin_app_plugin 和 build_apps_enabled_plugin_test_codex 串起来,专门覆盖 ChatGPT 认证下“双表面插件”的选择规则。tool_names 用来辅助判断请求里有哪些工具。
调用图:调用 9 个内部函数(mount_with_connector_name, mount_sse_once, sse, start_mock_server, build_apps_enabled_plugin_test_codex, tool_names, write_plugin_app_plugin, write_plugin_mcp_plugin, write_plugin_skill_plugin);外部调用 11 个(clone, new, default, new, assert!, stdio_server_bin, wait_for_event, wait_for_mcp_server, eprintln!, skip_if_no_network! (+1 more))。
explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth326–403 ↗
async fn explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth() -> Result<()>
作用:测试 ChatGPT 登录下,如果插件的 MCP 名和 App 名不冲突,MCP 工具不应该被错误隐藏。也就是说,系统只压掉冲突的那部分,不是一刀切。
数据流:它启动假服务器和 Apps 服务,写入技能、MCP 配置,以及名字为 sample_app 的 App 配置 → 构建启用 Apps 的 Codex,等待 sample MCP 服务器启动 → 提交插件提及 → 检查 developer 消息里 MCP 和 App 指引都存在;检查工具列表里 App 工具存在,sample MCP 的 echo 工具也存在,并确认 MCP 工具描述标明来自 sample 插件。
调用关系:它和前一个测试形成对照:前一个验证冲突时偏向 App,这一个验证不冲突时保留 MCP。它直接调用 write_plugin_app_plugin_with_name 来制造“不冲突”的 App 名。
调用图:调用 9 个内部函数(mount_with_connector_name, mount_sse_once, sse, start_mock_server, build_apps_enabled_plugin_test_codex, tool_names, write_plugin_app_plugin_with_name, write_plugin_mcp_plugin, write_plugin_skill_plugin);外部调用 11 个(clone, new, default, new, assert!, stdio_server_bin, wait_for_event, wait_for_mcp_server, eprintln!, skip_if_no_network! (+1 more))。
explicit_plugin_mentions_use_mcp_for_api_key_dual_surface_plugins406–498 ↗
async fn explicit_plugin_mentions_use_mcp_for_api_key_dual_surface_plugins() -> Result<()>
作用:测试同一个插件同时声明 MCP 和 App 时,在 API Key 登录下显式提及插件会使用 MCP,而不是 Apps。不同登录方式能用的后端能力不同,所以这里要防止走错通道。
数据流:它启动假服务器,写入技能、MCP 和 App 配置,用 API Key 认证创建 Codex,并打开 Apps 功能 → 等待 sample MCP 服务器可用 → 提交插件提及 → 检查 developer 消息里技能和 MCP 指引出现,App 指引被隐藏;检查工具列表里没有日历 App 工具,但有 sample MCP 的 echo 工具,并确认描述里带有插件来源。
调用关系:这个测试不用 build_apps_enabled_plugin_test_codex,而是直接用 test_codex 构建 API Key 认证场景。它和 ChatGPT 登录场景的测试一起保证“双表面插件”会根据认证方式选择正确工具。
调用图:调用 9 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, tool_names, write_plugin_app_plugin, write_plugin_mcp_plugin, write_plugin_skill_plugin, from_api_key);外部调用 11 个(clone, new, default, new, assert!, stdio_server_bin, wait_for_event, wait_for_mcp_server, eprintln!, skip_if_no_network! (+1 more))。
explicit_plugin_mentions_track_plugin_used_analytics501–576 ↗
async fn explicit_plugin_mentions_track_plugin_used_analytics() -> Result<()>
作用:测试用户显式提及插件后,系统会发出一条“插件被使用”的统计事件。这个事件用于后续分析哪些插件被用了、怎么被用的。
数据流:它启动假服务器,写入带技能的插件,构建用于 analytics 的 Codex → 提交 plugin://sample@test 的插件提及并等待回合结束 → 在最多 10 秒内反复查看假服务器收到的请求,找到 codex_plugin_used 事件 → 检查事件参数,包括插件 id、插件名、市场名、是否有技能、MCP 数量、连接器列表、客户端 id、模型名、线程 id 和回合 id。
调用关系:它调用 build_analytics_plugin_test_codex 准备带统计地址的 Codex,并用 write_plugin_skill_plugin 制造被使用的插件。它不关心模型回复内容,重点是确认后台统计请求确实发出且字段正确。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, build_analytics_plugin_test_codex, write_plugin_skill_plugin);外部调用 14 个(clone, new, default, from_millis, from_secs, now, new, assert!, assert_eq!, wait_for_event (+4 more))。
core/tests/suite/request_plugin_install.rs源码 ↗
这个测试文件模拟了一次真实对话:用户说“list tools”,系统准备把可用工具发给模型。这里特意把模型配置成不支持 tool_search,也就是不能先靠“搜索工具”找插件。测试要确认两件事:第一,发给模型的工具列表里确实没有 tool_search;第二,仍然有 list_available_plugins_to_install 和 request_plugin_install,让模型可以先列出可安装插件,再请求安装。它还检查这两个工具的说明文字,确保说明够谨慎:只有用户明确要求某个插件或连接器时才用,安装请求不能和其他工具并行调用,也不能把内部的 Gmail 连接器编号泄露出去。可以把它理解成一次“验收演练”:后台搭好假服务器,发出一轮假对话,然后检查系统递给模型的“工具菜单”是否安全、完整、说法正确。
tool_names29–44 ↗
fn tool_names(body: &Value) -> Vec<String>
作用:这个小工具函数从一次发给模型的请求正文里,把工具名字全部摘出来。测试用它来判断某个工具有没有出现在“工具菜单”里。
数据流:输入是一段 JSON 请求内容;它读取里面的 tools 数组,逐个查看每个工具的 name,如果没有就看 type;最后输出一个字符串列表。它不改动原始 JSON,只是把工具名整理出来,方便后面断言。
调用关系:主测试 request_plugin_install_is_available_without_search_tool_after_discovery_attempts 在收到假服务器记录的请求后调用它。它只做提取工作,把复杂的 JSON 变成简单的名字列表,然后主测试再检查列表里有没有该有或不该有的工具。
调用图:被 1 处调用(request_plugin_install_is_available_without_search_tool_after_discovery_attempts);外部调用 1 个(get)。
function_tool_description46–60 ↗
fn function_tool_description(body: &Value, name: &str) -> Option<String>
作用:这个小工具函数从请求正文里找到指定工具的说明文字。测试用它确认工具说明没有写错,也没有泄露不该给模型看的内部信息。
数据流:输入是一段 JSON 请求内容和一个工具名;它进入 tools 数组,找到 name 等于这个工具名的项目,再取出它的 description 字段;如果找到了就返回说明文字,找不到就返回空。它只读取数据,不修改任何东西。
调用关系:主测试在确认工具存在之后调用它,分别读取“列出可安装插件”和“请求安装插件”的说明。随后主测试检查这些说明里是否包含必要的使用规则,以及是否没有包含敏感的连接器编号。
调用图:被 1 处调用(request_plugin_install_is_available_without_search_tool_after_discovery_attempts);外部调用 1 个(get)。
configure_apps_without_search_tool62–89 ↗
fn configure_apps_without_search_tool(config: &mut Config, apps_base_url: &str)
作用:这个函数专门把测试环境调成一种特殊状态:应用、插件、工具建议功能都打开,但当前模型不支持搜索工具。这样测试才能验证“没有搜索工具时,插件安装工具还在不在”。
数据流:输入是可修改的配置对象和一个假的应用服务地址;它打开 Apps、Plugins、ToolSuggest 三个功能,加载内置模型目录,找到 gpt-5.4 这个模型,把聊天接口地址指向假服务,把模型设成 gpt-5.4,加入一个可发现的 Gmail 连接器,并把这个模型的搜索工具支持标记改成 false;最后这些改动留在配置里供测试使用。
调用关系:主测试在构建测试版 Codex 实例时把它交给配置回调使用。它不直接发请求,而是先布置好舞台,让后面的对话请求自然走到“模型没有搜索工具,但插件安装工具应该可用”的场景。
调用图:外部调用 2 个(bundled_models_response, vec!)。
request_plugin_install_is_available_without_search_tool_after_discovery_attempts92–164 ↗
async fn request_plugin_install_is_available_without_search_tool_after_discovery_attempts() -> Result<()>
作用:这是核心测试:它验证当搜索工具不可用时,系统仍会把插件安装相关工具发给模型,而且这些工具的说明文字安全、准确。这个测试保护的是用户安装插件的完整体验。
数据流:它先在需要时跳过无网络环境,然后启动一个假服务器和假应用服务;接着挂上一段模拟的流式响应,让模型看起来正常回复完成;然后用测试配置启动一个带假登录的 Codex 实例,提交一轮用户输入。之后它取出假服务器收到的请求正文,提取工具名和工具说明,检查 tool_search 不存在、两个插件安装工具存在,并检查说明文字包含必要规则、不包含内部连接器编号或过时说法。全部检查通过后返回成功。
调用关系:它是这个文件里的顶层测试函数,由测试框架自动运行。它负责串起所有辅助函数和测试设施:用 configure_apps_without_search_tool 布置配置,用假服务器接住请求,用 tool_names 和 function_tool_description 读出请求内容,最后用断言判断系统行为是否符合预期。
调用图:调用 8 个内部函数(mount, mount_sse_once, sse, start_mock_server, test_codex, function_tool_description, tool_names, create_dummy_chatgpt_auth_for_testing);外部调用 3 个(assert!, skip_if_no_network!, vec!)。
core/tests/suite/search_tool.rs源码 ↗
可以把这里测的功能想成“工具抽屉的目录”。系统里可能有日历、MCP 服务器、动态工具、多代理工具等很多工具;如果全塞给模型,既乱,也可能暴露不该直接用的工具。这个测试文件会启动假的模型服务器和假的应用工具服务器,模拟模型发请求、搜索工具、调用工具、收到结果的完整过程。它重点检查几件事:tool_search 默认会出现;应用工具在搜索前会被藏起来;“仅应用内部可用”的工具不会被模型硬调成功;API Key 登录时不会暴露应用来源;搜索结果能按名称、描述、参数结构匹配;搜索出来的 MCP 或动态工具后续可以正常路由调用;调用失败的错误也会原样反馈给模型。它不是线上功能代码,而是防止这些安全边界和交互流程被改坏的护栏。
tool_names66–81 ↗
fn tool_names(body: &Value) -> Vec<String>
作用:从一次发给模型的请求 JSON 里,抽出所有工具的名字。测试用它来快速判断某个工具有没有被暴露给模型。
数据流:输入是一段 JSON 请求体 → 它读取里面的 tools 数组,并优先取每个工具的 name,没有 name 时取 type → 输出一个字符串列表;如果没有 tools,就返回空列表,不改动任何外部状态。
调用关系:它是很多测试的“放大镜”。always_defer_feature_hides_small_app_tool_sets、search_tool_hides_apps_tools_without_search、tool_search_returns_deferred_tools_without_follow_up_tool_injection 等测试都会先拿到模型请求,再交给它提取工具名,然后做断言。
调用图:被 9 处调用(always_defer_feature_hides_small_app_tool_sets, app_search_sources_are_hidden_for_api_key_auth, explicit_app_mentions_respect_always_defer, search_tool_hides_apps_tools_without_search, tool_search_indexes_only_enabled_non_app_mcp_tools, tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call, tool_search_returns_deferred_tools_without_follow_up_tool_injection, tool_search_returns_deferred_v1_multi_agent_tools, tool_search_surfaced_mcp_tool_errors_are_returned_to_model);外部调用 1 个(get)。
tool_search_description83–97 ↗
fn tool_search_description(body: &Value) -> Option<String>
作用:从请求 JSON 里找到 tool_search 这个工具,并取出它的说明文字。测试用它确认说明里有没有该给模型看的搜索指引。
数据流:输入是一段 JSON 请求体 → 它遍历 tools 数组,找 type 等于 tool_search 的那一项 → 如果找到了,就输出 description 字符串;找不到就输出空值。
调用关系:app_search_sources_are_hidden_for_api_key_auth 用它确认 API Key 登录时说明里不泄露应用来源;search_tool_adds_discovery_instructions_to_tool_description 用它确认 tool_search 的描述包含新的发现工具流程。
调用图:被 2 处调用(app_search_sources_are_hidden_for_api_key_auth, search_tool_adds_discovery_instructions_to_tool_description);外部调用 1 个(get)。
tool_search_output_item99–101 ↗
fn tool_search_output_item(request: &ResponsesRequest, call_id: &str) -> Value
作用:从一次模型请求记录里,取出某个 tool_search 调用的返回项。测试用它检查搜索结果的状态、执行方式和返回内容。
数据流:输入是一条 ResponsesRequest 和一个 call_id → 它调用 request.tool_search_output(call_id) 找到对应输出 → 输出那段 JSON,不额外加工。
调用关系:它是读取搜索结果的基础小工具。tool_search_output_tools 会继续用它拿 tools 字段;一些测试也直接用它检查 tool_search 输出项的 status、execution 等字段。
调用图:调用 1 个内部函数(tool_search_output);被 3 处调用(tool_search_output_tools, tool_search_returns_deferred_tools_without_follow_up_tool_injection, tool_search_returns_deferred_v1_multi_agent_tools)。
tool_search_output_tools103–109 ↗
fn tool_search_output_tools(request: &ResponsesRequest, call_id: &str) -> Vec<Value>
作用:从 tool_search 的输出里取出真正返回的工具列表。测试用它确认搜索出来的是哪些工具,以及格式是不是对。
数据流:输入是一条请求记录和 call_id → 它先通过 tool_search_output_item 找到该次搜索输出,再读取里面的 tools 数组 → 输出 JSON 工具列表;没有就返回空列表。
调用关系:它被多个搜索结果测试反复使用,比如检查日历工具、动态工具、多代理工具、普通 MCP 工具是否被搜索出来。它把底层 JSON 读取细节藏起来,让测试重点放在行为判断上。
调用图:调用 1 个内部函数(tool_search_output_item);被 5 处调用(tool_search_indexes_only_enabled_non_app_mcp_tools, tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call, tool_search_returns_deferred_tools_without_follow_up_tool_injection, tool_search_returns_deferred_v1_multi_agent_tools, tool_search_uses_non_app_mcp_server_instructions_as_namespace_description)。
tool_search_output_has_namespace_child111–121 ↗
fn tool_search_output_has_namespace_child(
request: &ResponsesRequest,
call_id: &str,
namespace: &str,
tool_name: &str,
) -> bool
作用:判断某次 tool_search 输出里,某个命名空间下面有没有某个子工具。命名空间可以理解成“工具分组”,比如日历组里有创建事件工具。
数据流:输入是请求记录、搜索 call_id、命名空间名和工具名 → 它取出搜索返回的 tools,临时包装成一段 JSON,再交给 namespace_child_tool 查找 → 输出 true 或 false,不修改外部数据。
调用关系:它调用 namespace_child_tool 做真正的查找,自己负责把 tool_search 输出整理成那个查找函数能看懂的形状。测试用它写出更直观的断言:某组里应该有或不该有某个工具。
调用图:调用 1 个内部函数(namespace_child_tool);外部调用 1 个(json!)。
search_tool_enabled_by_default_adds_tool_search124–179 ↗
async fn search_tool_enabled_by_default_adds_tool_search() -> Result<()>
作用:确认在默认开启工具搜索的配置下,请求模型时会带上 tool_search。没有这个测试,默认行为被改坏后,模型可能根本找不到被延迟加载的工具。
数据流:它启动假的模型服务器和可搜索的应用工具服务器 → 提交一轮“list tools”用户输入 → 读取模型收到的请求体 → 断言 tools 里有一个结构完整的 tool_search,包括参数 query 和 limit。
调用关系:这个测试通过 start_mock_server、mount_searchable、mount_sse_once 搭好假环境,再用 search_capable_apps_builder 创建测试中的 Codex 会话。最后只检查第一轮发给模型的工具声明。
调用图:调用 5 个内部函数(mount_searchable, search_capable_apps_builder, mount_sse_once, sse, start_mock_server);外部调用 3 个(assert_eq!, skip_if_no_network!, vec!)。
always_defer_feature_hides_small_app_tool_sets182–225 ↗
async fn always_defer_feature_hides_small_app_tool_sets() -> Result<()>
作用:确认打开“总是延迟 MCP 工具”这个特性后,就算应用工具数量很少,也不会直接暴露给模型。模型只能先看到 tool_search。
数据流:它启动假服务器和应用服务器 → 在配置里启用 ToolSearchAlwaysDeferMcpTools → 提交一轮请求 → 用 tool_names 取出工具名 → 断言有 tool_search,并且没有任何以 mcp__ 开头的 MCP 工具名。
调用关系:它依赖 search_capable_apps_builder 生成带应用搜索能力的测试配置,并调用 tool_names 做最终检查。这个测试覆盖的是功能开关对工具暴露策略的影响。
调用图:调用 6 个内部函数(mount, search_capable_apps_builder, mount_sse_once, sse, start_mock_server, tool_names);外部调用 3 个(assert!, skip_if_no_network!, vec!)。
app_only_tools_are_not_visible_or_runnable_by_direct_model_calls228–299 ↗
async fn app_only_tools_are_not_visible_or_runnable_by_direct_model_calls() -> Result<()>
作用:确认“只给应用内部用”的工具不会出现在模型可见列表里,即使模型硬编一个调用也不会真的打到应用服务器。
数据流:它搭建一个带 app-only 工具的假应用服务器 → 模拟模型直接调用这个隐藏工具 → 系统返回 unsupported call 之类的失败输出 → 最后确认应用服务器没有记录到真实工具调用。
调用关系:它用 mount_with_app_only_tool 准备特殊工具,用 mount_sse_sequence 模拟模型先强行调用、再结束对话。断言既检查请求声明,也检查调用没有被转发到 MCP 应用服务器。
调用图:调用 4 个内部函数(mount_with_app_only_tool, apps_enabled_builder, mount_sse_sequence, start_mock_server);外部调用 3 个(assert!, skip_if_no_network!, vec!)。
search_tool_adds_discovery_instructions_to_tool_description347–386 ↗
async fn search_tool_adds_discovery_instructions_to_tool_description() -> Result<()>
作用:确认 tool_search 的描述文字会告诉模型有哪些工具来源,以及应该如何发现工具。说明写错时,模型可能不知道要先搜索。
数据流:它启动可搜索应用环境 → 让模型处理一轮简单请求 → 取出 tool_search 的 description → 检查里面包含新的发现工具说明,并且不再包含旧版“本会话持久注入”的说法。
调用关系:它调用 tool_search_description 专门读取说明文字。前面的 mock server 和 builder 只是为了产生一条真实的模型请求。
调用图:调用 6 个内部函数(mount_searchable, search_capable_apps_builder, mount_sse_once, sse, start_mock_server, tool_search_description);外部调用 3 个(assert!, skip_if_no_network!, vec!)。
search_tool_hides_apps_tools_without_search389–422 ↗
async fn search_tool_hides_apps_tools_without_search() -> Result<()>
作用:确认在模型还没调用 tool_search 之前,应用工具不会直接出现在工具列表里。这样模型不会绕过搜索流程直接调用。
数据流:它启动可搜索应用服务器 → 提交一轮普通输入 → 用 tool_names 读取初始请求里的工具名 → 断言 tool_search 存在,但日历创建、日历列表和日历命名空间都不存在。
调用关系:它和 search_tool_enabled_by_default_adds_tool_search 类似,但关注点不是 tool_search 的结构,而是应用工具是否被正确隐藏。
调用图:调用 6 个内部函数(mount_searchable, search_capable_apps_builder, mount_sse_once, sse, start_mock_server, tool_names);外部调用 3 个(assert!, skip_if_no_network!, vec!)。
explicit_app_mentions_respect_always_defer425–477 ↗
async fn explicit_app_mentions_respect_always_defer() -> Result<()>
作用:确认用户消息里明确提到某个应用时,如果“总是延迟”开关打开,系统仍然不会直接暴露该应用工具。这个测试防止显式 app 链接绕过安全策略。
数据流:它开启 ToolSearchAlwaysDeferMcpTools → 用户输入里包含 calendar 应用链接 → 系统生成模型请求 → 用 tool_names 和 namespace_child_tool 检查 → 结果应当只有 tool_search 可见,日历子工具不可见。
调用关系:它通过 search_capable_apps_builder 加功能开关,通过 tool_names 看工具清单,并用 namespace_child_tool 检查命名空间子工具是否被直接塞进请求。
调用图:调用 6 个内部函数(mount, search_capable_apps_builder, mount_sse_once, sse, start_mock_server, tool_names);外部调用 3 个(assert!, skip_if_no_network!, vec!)。
tool_search_returns_deferred_tools_without_follow_up_tool_injection480–759 ↗
async fn tool_search_returns_deferred_tools_without_follow_up_tool_injection() -> Result<()>
作用:确认模型调用 tool_search 后,搜索结果会作为历史输出返回,而不是把找到的工具偷偷注入到下一次请求的 tools 列表里。这样工具来源更清楚,也避免工具列表越变越乱。
数据流:它模拟三轮模型响应:先搜索日历创建工具,再调用搜索出来的日历工具,最后结束 → 测试等待 MCP 工具开始和结束事件 → 检查调用参数、应用元数据、turn 元数据、搜索输出和后续请求工具列表 → 结果证明工具能调用成功,但没有被后续直接注入。
调用关系:这是本文件最完整的端到端场景之一。它调用 tool_names、tool_search_output_item、tool_search_output_tools 读取请求和搜索结果,也用 recorded_apps_tool_call_by_call_id 检查应用服务器实际收到的 MCP 调用。
调用图:调用 8 个内部函数(mount_searchable, recorded_apps_tool_call_by_call_id, search_capable_apps_builder, mount_sse_sequence, start_mock_server, tool_names, tool_search_output_item, tool_search_output_tools);外部调用 8 个(default, assert!, assert_eq!, wait_for_event, from_str, skip_if_no_network!, unreachable!, vec!)。
tool_search_returns_deferred_v1_multi_agent_tools762–862 ↗
async fn tool_search_returns_deferred_v1_multi_agent_tools() -> Result<()>
作用:确认旧版多代理工具也可以被 tool_search 搜到,而且以命名空间里的子工具形式返回。这样多代理工具不会一开始就干扰模型上下文。
数据流:它配置一个支持工具搜索的模型 → 模拟模型搜索 spawn agent → 检查第一轮请求里没有 spawn_agent 等多代理工具,也没有相关长说明 → 再检查 tool_search 输出里有 multi_agent_v1 命名空间和 spawn_agent 子工具。
调用关系:它使用 test_codex 创建不依赖应用服务器的测试会话,用 tool_names 检查初始工具隐藏,用 tool_search_output_tools 和 namespace_child_tool 检查搜索结果结构。
调用图:调用 7 个内部函数(mount_sse_sequence, namespace_child_tool, start_mock_server, test_codex, tool_names, tool_search_output_item, tool_search_output_tools);外部调用 4 个(assert!, assert_eq!, skip_if_no_network!, vec!)。
tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call865–1045 ↗
async fn tool_search_returns_deferred_dynamic_tool_and_routes_follow_up_call() -> Result<()>
作用:确认运行时动态加入的工具可以被搜索到,并且模型随后调用它时,系统会把调用转成 DynamicToolCallRequest 交给外部处理。动态工具可以理解成“会话开始后临时挂上的工具”。
数据流:它创建一个动态命名空间 codex_app,里面有 automation_update 工具 → 模型先 tool_search,再调用该工具 → 系统发出 DynamicToolCallRequest 事件 → 测试回传 DynamicToolResponse → 最后检查模型收到的函数调用输出是 dynamic-search-ok,并且工具没有被直接注入后续 tools 列表。
调用关系:它用 thread_manager.start_thread_with_tools 开新线程并挂载动态工具;用 wait_for_event 等待动态工具请求;用 tool_search_output_tools 和 tool_names 分别检查搜索结果和请求工具列表。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, tool_names, tool_search_output_tools);外部调用 11 个(default, assert!, assert_eq!, wait_for_event, json!, Namespace, from_value, to_string, skip_if_no_network!, unreachable! (+1 more))。
tool_search_indexes_only_enabled_non_app_mcp_tools1048–1173 ↗
async fn tool_search_indexes_only_enabled_non_app_mcp_tools() -> Result<()>
作用:确认普通 MCP 服务器里的工具,只有启用且未禁用的那些会进入搜索索引。MCP 是一种让模型调用外部工具的协议,这里测试它的过滤规则。
数据流:它启动一个 stdio 方式的测试 MCP 服务器 rmcp,配置 echo 和 image,其中 image 又被禁用 → 模型搜索 echo 和 image → 检查初始请求只暴露 tool_search → 搜索 echo 能找到 rmcp.echo,搜索 image 找不到有效工具。
调用关系:它调用 stdio_server_bin 拿到测试 MCP 程序,wait_for_mcp_server 等服务器就绪,再用 tool_search_output_tools 和 namespace_child_tool 检查搜索结果。
调用图:调用 7 个内部函数(mount_searchable, search_capable_apps_builder, mount_sse_sequence, namespace_child_tool, start_mock_server, tool_names, tool_search_output_tools);外部调用 7 个(assert!, assert_eq!, stdio_server_bin, wait_for_mcp_server, json!, skip_if_no_network!, vec!)。
tool_search_surfaced_mcp_tool_errors_are_returned_to_model1176–1331 ↗
async fn tool_search_surfaced_mcp_tool_errors_are_returned_to_model() -> Result<()>
作用:确认通过 tool_search 找到并调用的 MCP 工具,如果执行失败,错误会返回给模型,而不是被误判成“不支持的调用”。这对模型纠错很重要。
数据流:它配置 rmcp 的 echo 工具并强制模型用空参数调用 → MCP 工具因为缺少 message 字段失败 → 测试等待 McpToolCallEnd 事件并确认失败内容 → 再检查发回模型的 function_call_output 里包含缺字段错误。
调用关系:它先用 tool_names 确认 rmcp 命名空间没有提前暴露,再用 tool_search_output_has_namespace_child 确认 echo 是搜索出来的,最后检查第三次请求里模型可见的错误输出。
调用图:调用 5 个内部函数(mount_searchable, search_capable_apps_builder, mount_sse_sequence, start_mock_server, tool_names);外部调用 10 个(default, assert!, assert_eq!, stdio_server_bin, wait_for_event, wait_for_mcp_server, panic!, skip_if_no_network!, unreachable!, vec!)。
tool_search_uses_non_app_mcp_server_instructions_as_namespace_description1334–1422 ↗
async fn tool_search_uses_non_app_mcp_server_instructions_as_namespace_description() -> Result<()>
作用:确认普通 MCP 服务器提供的说明文字,会作为搜索结果里命名空间的描述。这样模型看到搜索结果时,能知道这一组工具大概是干什么的。
数据流:它启动 rmcp 测试服务器 → 模型搜索 echo 工具 → 从 tool_search 输出里找到 mcp__rmcp 命名空间 → 断言 description 等于服务器给出的说明文字。
调用关系:它和普通 MCP 搜索测试共用 mock server、search_capable_apps_builder 和 wait_for_mcp_server;最后通过 tool_search_output_tools 读取搜索结果。
调用图:调用 5 个内部函数(mount_searchable, search_capable_apps_builder, mount_sse_sequence, start_mock_server, tool_search_output_tools);外部调用 5 个(assert_eq!, stdio_server_bin, wait_for_mcp_server, skip_if_no_network!, vec!)。
tool_search_matches_mcp_tools_by_distinct_name_description_and_schema_terms1425–1505 ↗
async fn tool_search_matches_mcp_tools_by_distinct_name_description_and_schema_terms() -> Result<()>
作用:确认搜索 MCP 应用工具时,不只按工具名匹配,也会按描述和参数结构里的词匹配。这样用户用不同说法找工具时也能搜到。
数据流:它准备三次搜索:按原始工具名、按工具描述里的词、按参数 schema 里的 starts_at 字段 → 提交一轮请求 → 检查每个 call_id 对应的搜索输出里都出现了预期的日历子工具。
调用关系:它主要通过 mount_sse_sequence 一次模拟多个 tool_search 调用,并用 tool_search_output_has_namespace_child 检查每次搜索是否命中了正确工具。
调用图:调用 4 个内部函数(mount_searchable, search_capable_apps_builder, mount_sse_sequence, start_mock_server);外部调用 4 个(assert!, assert_eq!, skip_if_no_network!, vec!)。
tool_search_matches_dynamic_tools_by_name_description_namespace_and_schema_terms1508–1617 ↗
async fn tool_search_matches_dynamic_tools_by_name_description_namespace_and_schema_terms() -> Result<()>
作用:确认动态工具的搜索也很全面:可以按工具名、带空格的名称写法、描述、命名空间名和参数字段匹配。这样动态挂载的工具不会因为搜索词稍微不同就找不到。
数据流:它创建 orbit_ops 命名空间和 quasar_ping_beacon 动态工具,工具描述和参数里放入特定词 → 模型连续发起多次 tool_search → 等会话结束后检查每个搜索结果都包含这个动态工具。
调用关系:它用 test_codex 创建基础会话,再通过 start_thread_with_tools 注入动态工具;用 wait_for_event 等待 TurnComplete;最后用 tool_search_output_has_namespace_child 统一检查每个搜索调用的结果。
调用图:调用 3 个内部函数(mount_sse_sequence, start_mock_server, test_codex);外部调用 7 个(default, assert!, assert_eq!, wait_for_event, Namespace, skip_if_no_network!, vec!)。
代码模式与运行时项目
这些测试验证更高层的运行时行为,包括 Code Mode 编排、发出的项目/事件、图像工具以及用户调用的 shell 命令。
core/tests/suite/code_mode.rs源码 ↗
代码模式可以让模型写一段 JavaScript 脚本,再通过 tools 去跑命令、查网页、调外部工具、保存临时数据。这个能力很强,也很容易出问题:比如输出太长、脚本卡住、工具报错、图片格式不对、后台任务没结束等。这个测试文件就用假的服务器和假的模型回复,模拟真实对话流程,一项项验证这些边界情况。文件前半部分放了一些小帮手,用来启动测试会话、取出工具输出、解析正在运行的脚本编号。后面每个测试都像一个小剧本:先让假模型要求执行代码,再检查 Codex 发回给模型的结果是不是正确。它还覆盖 MCP(模型上下文协议,一种让程序接外部工具的协议)、动态工具、网页搜索、图片处理、暂停/等待/终止等场景。没有这些测试,代码模式很容易在真实使用中出现“脚本没停住”“工具结果丢了”“模型看不到该看的工具”等隐蔽故障。
custom_tool_output_items69–77 ↗
fn custom_tool_output_items(req: &ResponsesRequest, call_id: &str) -> Vec<Value>
作用:把某次自定义工具调用的输出,统一拿成一组内容项。测试里不想管输出原来是纯文字还是数组,就用它来“拆包”。
数据流:输入是一条模拟请求和工具调用编号 → 它读取这次自定义工具的 output 字段,如果是数组就原样返回,如果是字符串就包装成一个文字内容项 → 输出是一组 JSON 内容项;格式不对时直接让测试失败。
调用关系:很多测试在检查 exec 代码模式的返回内容前都会先调用它。它依赖测试支持里的 custom_tool_call_output 取原始输出,再把结果交给 text_item 或断言代码继续检查。
调用图:调用 1 个内部函数(custom_tool_call_output);被 21 处调用(code_mode_background_keeps_running_on_later_turn_without_wait, code_mode_can_apply_patch_via_nested_tool, code_mode_can_output_images_via_global_helper, code_mode_can_return_exec_command_output, code_mode_can_run_multiple_yielded_sessions, code_mode_can_use_mcp_image_result_with_image_helper, code_mode_can_use_view_image_result_with_image_helper, code_mode_can_yield_and_resume_with_wait, code_mode_concurrent_cells_merge_only_the_stored_values_they_write, code_mode_exit_stops_script_immediately (+11 more));外部调用 2 个(panic!, vec!)。
tool_names79–94 ↗
fn tool_names(body: &Value) -> Vec<String>
作用:从发给模型的请求正文里提取工具名字列表。它用来确认模型当前能看到哪些工具。
数据流:输入是一段 JSON 请求正文 → 它找到里面的 tools 数组,并从每个工具取 name 或 type → 输出工具名字符串列表;没有工具时输出空列表。
调用关系:它主要服务于检查“代码模式 only”时工具暴露范围的测试,也用于确认被排除的命名空间仍按预期直接暴露。
调用图:外部调用 1 个(get)。
function_tool_output_items96–104 ↗
fn function_tool_output_items(req: &ResponsesRequest, call_id: &str) -> Vec<Value>
作用:把普通函数工具调用的输出统一拿成内容项数组。它和 custom_tool_output_items 类似,只是面对的是普通工具,不是自定义工具。
数据流:输入是一条模拟请求和函数工具调用编号 → 它读取这次函数工具的 output 字段,数组就直接返回,字符串就包成文字项 → 输出内容项数组;遇到异常格式就让测试失败。
调用关系:等待脚本继续运行的 wait 工具测试都会用它。它把 wait 的结果整理好,再交给断言代码检查脚本是继续运行、完成、失败还是终止。
调用图:调用 1 个内部函数(function_call_output);被 7 处调用(code_mode_can_run_multiple_yielded_sessions, code_mode_can_yield_and_resume_with_wait, code_mode_wait_can_terminate_and_continue, code_mode_wait_returns_error_for_unknown_session, code_mode_wait_terminate_returns_completed_session_if_it_finished_after_yield_control, code_mode_wait_uses_its_own_max_tokens_budget, code_mode_yield_and_termination_are_not_starved_by_runtime_output);外部调用 2 个(panic!, vec!)。
text_item106–111 ↗
fn text_item(items: &[Value], index: usize) -> &str
作用:从内容项数组里取出指定位置的文字。它让测试代码不用重复写一堆 JSON 取字段的代码。
数据流:输入是一组 JSON 内容项和下标 → 它取出该项里的 text 字段并确认它真的是字符串 → 输出这段文字;如果不是文字项就让测试失败。
调用关系:几乎所有检查脚本输出的测试都会用它。它通常接在 custom_tool_output_items 或 function_tool_output_items 后面。
调用图:被 18 处调用(code_mode_background_keeps_running_on_later_turn_without_wait, code_mode_can_apply_patch_via_nested_tool, code_mode_can_output_images_via_global_helper, code_mode_can_return_exec_command_output, code_mode_can_run_multiple_yielded_sessions, code_mode_can_use_mcp_image_result_with_image_helper, code_mode_can_use_view_image_result_with_image_helper, code_mode_can_yield_and_resume_with_wait, code_mode_concurrent_cells_merge_only_the_stored_values_they_write, code_mode_exit_stops_script_immediately (+8 more))。
extract_running_cell_id113–118 ↗
fn extract_running_cell_id(text: &str) -> String
作用:从“脚本仍在运行”的提示文字里抠出脚本编号。这个编号后面要交给 wait,才能继续等同一个脚本。
数据流:输入是一段运行状态文字 → 它检查文字是否以 Script running with cell ID 开头,并取出后面的第一行 → 输出脚本编号字符串;格式不对就让测试失败。
调用关系:暂停、恢复、终止、多会话这些测试都会先用它拿到编号,再把编号放进后续的 wait 工具调用里。
调用图:被 7 处调用(code_mode_can_run_multiple_yielded_sessions, code_mode_can_yield_and_resume_with_wait, code_mode_concurrent_cells_merge_only_the_stored_values_they_write, code_mode_wait_can_terminate_and_continue, code_mode_wait_terminate_returns_completed_session_if_it_finished_after_yield_control, code_mode_wait_uses_its_own_max_tokens_budget, code_mode_yield_and_termination_are_not_starved_by_runtime_output)。
wait_for_file_source120–127 ↗
fn wait_for_file_source(path: &Path) -> Result<String>
作用:生成一段 JavaScript 等待代码,用来等某个文件出现。测试用这个文件当“闸门”,控制脚本什么时候继续跑。
数据流:输入是一个文件路径 → 它把路径安全地加引号,拼出一段循环调用 tools.exec_command 的 JavaScript → 输出这段代码字符串。
调用关系:需要精确控制脚本暂停和恢复的测试会调用它。测试先把这段代码塞进代码模式脚本,之后通过写入对应文件来放行。
调用图:被 6 处调用(code_mode_can_run_multiple_yielded_sessions, code_mode_can_yield_and_resume_with_wait, code_mode_concurrent_cells_merge_only_the_stored_values_they_write, code_mode_wait_can_terminate_and_continue, code_mode_wait_terminate_returns_completed_session_if_it_finished_after_yield_control, code_mode_wait_uses_its_own_max_tokens_budget);外部调用 3 个(to_string_lossy, format!, try_join)。
custom_tool_output_body_and_success129–147 ↗
fn custom_tool_output_body_and_success(
req: &ResponsesRequest,
call_id: &str,
) -> (String, Option<bool>)
作用:同时拿到自定义工具输出的正文和成功标记。它把分散在不同字段里的结果整理成测试最关心的两样东西。
数据流:输入是一条模拟请求和调用编号 → 它先读取工具输出的原始内容和成功状态,再调用 custom_tool_output_items 抽取文字项,最后合并成一段正文 → 输出 (正文, 是否成功)。
调用关系:大量测试用它判断代码模式脚本有没有成功,以及最终输出是不是预期内容。它是自定义工具输出检查的主入口。
调用图:调用 2 个内部函数(custom_tool_call_output_content_and_success, custom_tool_output_items);被 30 处调用(app_only_tools_are_not_visible_or_runnable_by_code_mode_model, code_mode_can_call_hidden_dynamic_tools, code_mode_can_compare_elapsed_time_around_set_timeout, code_mode_can_output_images_via_global_helper, code_mode_can_output_serialized_text_via_global_helper, code_mode_can_print_content_only_mcp_tool_result_fields, code_mode_can_print_error_mcp_tool_result_fields, code_mode_can_print_structured_mcp_tool_result_fields, code_mode_can_resume_after_set_timeout, code_mode_can_store_and_load_values_across_turns (+15 more))。
custom_tool_output_last_non_empty_text149–164 ↗
fn custom_tool_output_last_non_empty_text(req: &ResponsesRequest, call_id: &str) -> Option<String>
作用:从自定义工具输出里找最后一段非空文字。适合输出里有状态头、图片或多段内容时,只关心最后真正的文本结果。
数据流:输入是一条模拟请求和调用编号 → 它读取输出,如果是字符串就检查是否非空,如果是数组就从后往前找非空 text → 输出最后一段非空文字,找不到就输出空。
调用关系:检查 JSON 结果、工具元数据、隐藏动态工具返回值时会用它。它帮助测试避开前面的运行状态说明。
调用图:调用 1 个内部函数(custom_tool_call_output);被 6 处调用(code_mode_can_call_hidden_dynamic_tools, code_mode_can_compare_elapsed_time_around_set_timeout, code_mode_can_store_and_load_values_across_turns, code_mode_concurrent_cells_merge_only_the_stored_values_they_write, code_mode_exports_all_tools_metadata_for_builtin_tools, code_mode_exports_all_tools_metadata_for_namespaced_mcp_tools)。
run_code_mode_turn166–172 ↗
async fn run_code_mode_turn(
server: &MockServer,
prompt: &str,
code: &str,
) -> Result<(TestCodex, ResponseMock)>
作用:用默认配置跑一轮最常见的代码模式测试。测试只需要给提示词和要执行的代码,就能得到测试会话和后续请求记录。
数据流:输入是假服务器、用户提示和脚本代码 → 它转交给 run_code_mode_turn_with_config,默认不额外改配置 → 输出测试对象和第二次模型请求的记录。
调用关系:很多简单测试都从它开始。它是更完整配置函数的薄包装,减少重复搭测试环境的代码。
调用图:调用 1 个内部函数(run_code_mode_turn_with_config);被 19 处调用(code_mode_can_apply_patch_via_nested_tool, code_mode_can_compare_elapsed_time_around_set_timeout, code_mode_can_output_images_via_global_helper, code_mode_can_output_serialized_text_via_global_helper, code_mode_can_resume_after_set_timeout, code_mode_can_return_exec_command_output, code_mode_exec_command_explicit_max_output_tokens_truncates, code_mode_exec_explicit_max_above_default_preserves_output, code_mode_exec_explicit_max_above_default_truncates_larger_output, code_mode_exec_explicit_max_output_tokens_truncates (+9 more))。
run_code_mode_turn_with_config174–182 ↗
async fn run_code_mode_turn_with_config(
server: &MockServer,
prompt: &str,
code: &str,
configure: impl FnOnce(&mut Config) + Send + 'static,
) -> Result<(TestCodex, ResponseMock)>
作用:在默认代码模式测试基础上,允许测试临时改配置。比如打开图片缩放、设置 token 限制等。
数据流:输入是假服务器、提示、脚本和一个修改配置的函数 → 它指定默认模型,再把工作交给 run_code_mode_turn_with_model_and_config → 输出测试对象和后续请求记录。
调用关系:需要特殊配置的测试会用它。它位于最简单入口和最完整入口之间。
调用图:调用 1 个内部函数(run_code_mode_turn_with_model_and_config);被 5 处调用(code_mode_exec_explicit_max_above_truncation_policy_preserves_output, code_mode_exec_without_max_preserves_output_beyond_truncation_policy, code_mode_get_context_remaining_returns_structured_result, resize_all_images_replaces_malformed_code_mode_image, run_code_mode_turn)。
run_code_mode_turn_with_model_and_config184–218 ↗
async fn run_code_mode_turn_with_model_and_config(
server: &MockServer,
prompt: &str,
code: &str,
model: &'static str,
configure: impl FnOnce(&mut Config) + Send + 'static,
) -> Re
作用:搭好一整套代码模式测试场景:启用功能、挂上两次假的模型流式回复,并提交一轮用户输入。
数据流:输入是假服务器、提示、脚本、模型名和配置修改函数 → 它创建测试 Codex,启用代码模式,安排第一次回复要求执行 exec,第二次回复说完成 → 输出测试对象和第二次请求记录。
调用关系:这是普通代码模式测试的核心搭台函数。上层的 run_code_mode_turn 和 run_code_mode_turn_with_config 都调用它。
调用图:调用 3 个内部函数(mount_sse_once, sse, test_codex);被 2 处调用(resize_all_images_resizes_explicit_original_code_mode_image, run_code_mode_turn_with_config);外部调用 1 个(vec!)。
code_mode_can_call_standalone_web_search221–320 ↗
async fn code_mode_can_call_standalone_web_search() -> Result<()>
作用:验证代码模式里的脚本可以直接调用独立网页搜索工具。这样模型在脚本中查网页时,搜索请求和结果都必须正确传递。
数据流:它启动假服务器并模拟搜索接口 → 让代码模式执行 tools.web__run → 检查发出的搜索请求包含正确模型、命令和设置,并确认搜索结果回到了工具输出里。
调用关系:这个测试自己搭认证、扩展注册和网页搜索配置。它覆盖代码模式、扩展系统和网页搜索接口之间的配合。
调用图:调用 6 个内部函数(auth_manager_from_auth, mount_sse_once, sse, start_mock_server, test_codex, from_api_key);外部调用 11 个(new, new, given, new, assert_eq!, install, json!, skip_if_no_network!, vec!, method (+1 more))。
run_code_mode_turn_with_rmcp322–328 ↗
async fn run_code_mode_turn_with_rmcp(
server: &MockServer,
prompt: &str,
code: &str,
) -> Result<(TestCodex, ResponseMock)>
作用:用默认模型跑一轮带 MCP 测试服务器的代码模式测试。MCP 可以理解成“外部工具服务器”。
数据流:输入是假服务器、提示和脚本 → 它把默认模型补上,再调用 run_code_mode_turn_with_rmcp_model → 输出测试对象和后续请求记录。
调用关系:检查 MCP 工具可见性、返回结构、全局工具对象等测试都会用它作为入口。
调用图:调用 1 个内部函数(run_code_mode_turn_with_rmcp_model);被 8 处调用(code_mode_can_print_content_only_mcp_tool_result_fields, code_mode_can_print_error_mcp_tool_result_fields, code_mode_can_print_structured_mcp_tool_result_fields, code_mode_exports_all_tools_metadata_for_namespaced_mcp_tools, code_mode_exposes_mcp_tools_on_global_tools_object, code_mode_exposes_namespaced_mcp_tools_on_global_tools_object, code_mode_exposes_normalized_illegal_mcp_tool_names, code_mode_lists_global_scope_items)。
run_code_mode_turn_with_rmcp_model330–341 ↗
async fn run_code_mode_turn_with_rmcp_model(
server: &MockServer,
prompt: &str,
code: &str,
model: &'static str,
) -> Result<(TestCodex, ResponseMock)>
作用:和 MCP 测试服务器跑一轮代码模式测试,但允许指定模型名。
数据流:输入是假服务器、提示、脚本和模型名 → 它用默认的非 only 模式、默认工具命名方式,调用 run_code_mode_turn_with_rmcp_config → 输出测试对象和请求记录。
调用关系:需要特定模型能力的 MCP 图片测试会用它。它把复杂配置继续下沉给统一函数。
调用图:调用 1 个内部函数(run_code_mode_turn_with_rmcp_config);被 2 处调用(code_mode_can_use_mcp_image_result_with_image_helper, run_code_mode_turn_with_rmcp)。
run_code_mode_turn_with_rmcp_mode343–358 ↗
async fn run_code_mode_turn_with_rmcp_mode(
server: &MockServer,
prompt: &str,
code: &str,
code_mode_only: bool,
) -> Result<(TestCodex, ResponseMock)>
作用:和 MCP 测试服务器跑一轮,并允许切换“代码模式 only”。这个模式会限制提示阶段直接暴露的工具。
数据流:输入是假服务器、提示、脚本和是否 only 的布尔值 → 它补上默认模型和默认 MCP 名字规则 → 调用 run_code_mode_turn_with_rmcp_config 并返回结果。
调用关系:专门服务于验证代码模式 only 也能调用 MCP 工具的测试。
调用图:调用 1 个内部函数(run_code_mode_turn_with_rmcp_config);被 1 处调用(code_mode_only_can_call_mcp_tool)。
run_code_mode_turn_with_rmcp_config360–438 ↗
async fn run_code_mode_turn_with_rmcp_config(
server: &MockServer,
prompt: &str,
code: &str,
model: &'static str,
code_mode_only: bool,
non_prefixed_mcp_tool_names: bool,
) ->
作用:搭建最完整的 MCP 代码模式测试环境。它启动一个本地 stdio MCP 测试服务器,并把它接进 Codex 配置。
数据流:输入是假服务器、提示、脚本、模型名、是否 only、是否用非前缀工具名 → 它启用对应功能,配置 MCP 服务器和环境变量,等待服务器就绪,再安排两次假的模型回复并提交用户输入 → 输出测试对象和第二次请求记录。
调用关系:所有需要 MCP 的辅助入口最后都会调用它。它负责把外部工具服务器、模型假回复和代码模式串起来。
调用图:调用 3 个内部函数(mount_sse_once, sse, test_codex);被 3 处调用(code_mode_uses_non_prefixed_mcp_tool_names_when_feature_enabled, run_code_mode_turn_with_rmcp_mode, run_code_mode_turn_with_rmcp_model);外部调用 3 个(stdio_server_bin, wait_for_mcp_server, vec!)。
code_mode_can_return_exec_command_output442–480 ↗
async fn code_mode_can_return_exec_command_output() -> Result<()>
作用:验证代码模式里的脚本调用 exec_command 后,能拿到命令输出、退出码和耗时等结构化结果。
数据流:它让脚本执行一个打印标记的命令 → 读取返回给模型的内容项 → 检查有运行完成头、命令输出、退出码 0 和耗时字段。
调用关系:它使用 run_code_mode_turn 搭环境,再用 custom_tool_output_items 和 text_item 检查具体返回。
调用图:调用 4 个内部函数(start_mock_server, custom_tool_output_items, run_code_mode_turn, text_item);外部调用 6 个(assert!, assert_eq!, concat!, assert_regex_match, from_str, skip_if_no_network!)。
code_mode_only_restricts_prompt_tools483–515 ↗
async fn code_mode_only_restricts_prompt_tools() -> Result<()>
作用:验证“代码模式 only”下,提示给模型的直接工具列表被收窄。这样模型主要通过代码模式调用工具,而不是乱用外层工具。
数据流:它启用 CodeModeOnly,提交一轮请求 → 读取发给模型的第一份请求正文 → 检查工具名只包含 exec、wait、request_user_input、web_search。
调用关系:它用 tool_names 看工具清单,确认配置开关影响的是提示阶段的工具暴露。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 3 个(assert_eq!, skip_if_no_network!, vec!)。
code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools518–641 ↗
async fn code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools() -> Result<()>
作用:验证代码模式 only 下,模型看不到一大堆应用工具的名字,但可以通过 ALL_TOOLS 找到并延迟调用允许的应用工具。
数据流:它挂上可搜索的 Apps 测试服务器并启用相关功能 → 脚本在 ALL_TOOLS 里找日历工具并调用 → 检查外层工具列表没有直接泄露应用工具,且调用结果成功返回。
调用关系:它把 Apps、搜索能力、代码模式 only 和延迟工具加载放在一起测,使用 custom_tool_output_body_and_success 检查脚本结果。
调用图:调用 7 个内部函数(mount_searchable, mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_body_and_success, create_dummy_chatgpt_auth_for_testing);外部调用 6 个(assert!, assert_eq!, assert_ne!, from_str, skip_if_no_network!, vec!)。
app_only_tools_are_not_visible_or_runnable_by_code_mode_model644–727 ↗
async fn app_only_tools_are_not_visible_or_runnable_by_code_mode_model() -> Result<()>
作用:验证“只给应用直接用”的工具不会被代码模式模型看到或调用。这样可以避免越权调用。
数据流:它挂上一个带 app-only 工具的 Apps 服务器 → 脚本尝试在 ALL_TOOLS 和 tools 对象里找该工具并调用 → 检查它不可见、不可调用,且请求没有真的打到服务器。
调用关系:它使用 Apps 测试服务器和代码模式 only 配置,最后用记录的应用工具调用来确认没有越权穿透。
调用图:调用 6 个内部函数(mount_with_app_only_tool, search_capable_apps_builder, mount_sse_once, sse, start_mock_server, custom_tool_output_body_and_success);外部调用 7 个(assert!, assert_eq!, assert_ne!, format!, from_str, skip_if_no_network!, vec!)。
code_mode_only_can_call_nested_tools731–777 ↗
async fn code_mode_only_can_call_nested_tools() -> Result<()>
作用:验证即使在代码模式 only 下,脚本内部仍能调用嵌套工具,比如 exec_command。
数据流:它启用 CodeModeOnly → 让 exec 脚本调用 tools.exec_command 打印标记 → 检查最终输出就是这个标记且没有失败。
调用关系:它证明“限制外层工具”不等于“脚本里不能用工具”,通过 custom_tool_output_body_and_success 读取结果。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_body_and_success);外部调用 4 个(assert_eq!, assert_ne!, skip_if_no_network!, vec!)。
code_mode_update_plan_nested_tool_result_is_empty_object780–808 ↗
async fn code_mode_update_plan_nested_tool_result_is_empty_object() -> Result<()>
作用:验证代码模式里调用 update_plan 时,返回值是空对象。这样脚本不会收到多余或不稳定的信息。
数据流:它让脚本调用 tools.update_plan 并把结果转成 JSON 输出 → 解析返回正文 → 检查结果正好是 {}。
调用关系:它通过 run_code_mode_turn 跑标准流程,用输出检查辅助函数确认嵌套工具返回格式。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn);外部调用 4 个(assert_eq!, assert_ne!, from_str, skip_if_no_network!)。
code_mode_get_context_remaining_returns_structured_result811–849 ↗
async fn code_mode_get_context_remaining_returns_structured_result() -> Result<()>
作用:验证代码模式里调用“剩余上下文”工具时,会得到结构化结果。上下文可以理解成模型还能记住多少输入空间。
数据流:它配置模型上下文窗口和 token 预算功能 → 脚本调用 get_context_remaining → 检查返回 JSON 里有预期的 tokens_left 数字。
调用关系:它使用 run_code_mode_turn_with_config 打开额外功能,测试代码模式和 token 预算工具的连接。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_config);外部调用 4 个(assert_eq!, assert_ne!, from_str, skip_if_no_network!)。
code_mode_nested_tool_calls_can_run_in_parallel853–941 ↗
async fn code_mode_nested_tool_calls_can_run_in_parallel() -> Result<()>
作用:验证代码模式脚本里同时调用多个嵌套工具时,它们真的能并行跑,而不是一个等一个。
数据流:它先做一次预热 → 再让脚本用 Promise.all 同时调用两个同步测试工具 → 用总耗时和返回值判断两次调用是并行完成的。
调用关系:它直接搭多段模拟回复序列,并用时间测量检查运行时调度能力。
调用图:调用 4 个内部函数(mount_sse_sequence, start_mock_server, test_codex, custom_tool_output_items);外部调用 5 个(now, assert!, assert_eq!, skip_if_no_network!, vec!)。
code_mode_exec_command_explicit_max_output_tokens_truncates945–971 ↗
async fn code_mode_exec_command_explicit_max_output_tokens_truncates() -> Result<()>
作用:验证通过代码顶部 @exec 注释设置很小输出上限时,脚本整体输出会被截断。
数据流:它在脚本注释里设置 max_output_tokens: 5 → 命令输出较长数字串 → 检查返回文本包含警告、原始 token 数和截断片段。
调用关系:它和同名的嵌套工具参数测试覆盖不同入口:这里测的是代码模式执行本身的上限声明。
调用图:调用 2 个内部函数(start_mock_server, run_code_mode_turn);外部调用 2 个(assert_eq!, skip_if_no_network!)。
code_mode_exec_explicit_max_above_default_preserves_output975–1006 ↗
async fn code_mode_exec_explicit_max_above_default_preserves_output() -> Result<()>
作用:验证当脚本声明更高的输出上限,并且实际输出没超过这个上限时,大输出不会被默认限制错误截断。
数据流:它运行一个打印 5 万个字符的命令,并声明较高 max_output_tokens → 读取输出 → 检查完整字符串被保留下来。
调用关系:它依赖普通代码模式测试流程,同时跳过一些平台上已知不稳定的执行环境。
调用图:调用 2 个内部函数(start_mock_server, run_code_mode_turn);外部调用 3 个(assert_eq!, skip_if_no_network!, skip_if_wine_exec!)。
code_mode_exec_explicit_max_above_default_truncates_larger_output1010–1045 ↗
async fn code_mode_exec_explicit_max_above_default_truncates_larger_output() -> Result<()>
作用:验证显式提高输出上限后,如果输出仍然太大,系统会按新上限截断并说明原始大小。
数据流:它让命令打印更多字符,并在脚本注释里声明较高上限 → 读取输出 → 检查前后部分保留,中间出现“多少 token 被截断”的提示。
调用关系:它和前一个测试配套,确认“提高上限”不是无限制放开,而是按新规则处理。
调用图:调用 2 个内部函数(start_mock_server, run_code_mode_turn);外部调用 3 个(assert_eq!, skip_if_no_network!, skip_if_wine_exec!)。
code_mode_exec_explicit_max_above_truncation_policy_preserves_output1049–1083 ↗
async fn code_mode_exec_explicit_max_above_truncation_policy_preserves_output() -> Result<()>
作用:验证脚本显式声明的输出上限可以覆盖全局较小的截断策略。也就是说这次执行自己要更多空间时,应该被尊重。
数据流:它把全局工具输出限制设得很低 → 脚本声明更高的 max_output_tokens 并输出大文本 → 检查输出没有被全局小限制截断。
调用关系:它通过 run_code_mode_turn_with_config 改配置,检查局部执行设置和全局策略之间的优先级。
调用图:调用 2 个内部函数(start_mock_server, run_code_mode_turn_with_config);外部调用 3 个(assert_eq!, skip_if_no_network!, skip_if_wine_exec!)。
code_mode_exec_without_max_preserves_output_beyond_default1087–1117 ↗
async fn code_mode_exec_without_max_preserves_output_beyond_default() -> Result<()>
作用:验证如果脚本注释给了执行级别的输出上限,即使 exec_command 本身没传最大值,也能保留较大输出。
数据流:它在代码顶部写 @exec 上限 → 命令输出 5 万字符但调用参数不写 max_output_tokens → 检查结果完整。
调用关系:它确认代码模式外层执行预算能传递给内部命令输出处理。
调用图:调用 2 个内部函数(start_mock_server, run_code_mode_turn);外部调用 3 个(assert_eq!, skip_if_no_network!, skip_if_wine_exec!)。
code_mode_exec_without_max_preserves_output_beyond_truncation_policy1121–1154 ↗
async fn code_mode_exec_without_max_preserves_output_beyond_truncation_policy() -> Result<()>
作用:验证执行级别的输出上限同样能覆盖很低的全局工具输出限制,即使嵌套命令没有单独写上限。
数据流:它把全局输出限制设低 → 代码注释声明更高执行上限 → 命令输出大量文字 → 检查完整输出保留。
调用关系:它使用带配置的辅助入口,补齐“没有嵌套 max 参数”这一种边界情况。
调用图:调用 2 个内部函数(start_mock_server, run_code_mode_turn_with_config);外部调用 3 个(assert_eq!, skip_if_no_network!, skip_if_wine_exec!)。
code_mode_exec_explicit_max_output_tokens_truncates1158–1183 ↗
async fn code_mode_exec_explicit_max_output_tokens_truncates() -> Result<()>
调用图:调用 2 个内部函数(start_mock_server, run_code_mode_turn);外部调用 2 个(assert_eq!, skip_if_no_network!)。
code_mode_returns_accumulated_output_when_script_fails1186–1225 ↗
async fn code_mode_returns_accumulated_output_when_script_fails() -> Result<()>
作用:验证脚本中途抛错时,之前已经输出的内容不会丢。这样模型能看到出错前发生了什么。
数据流:它让脚本先输出两段文字,再抛出 boom 错误 → 读取内容项 → 检查有失败头、两段旧输出和错误堆栈。
调用关系:它用标准代码模式流程,重点检查运行时错误处理和输出累积逻辑。
调用图:调用 4 个内部函数(start_mock_server, custom_tool_output_items, run_code_mode_turn, text_item);外部调用 4 个(assert_eq!, concat!, assert_regex_match, skip_if_no_network!)。
code_mode_exec_surfaces_handler_errors_as_exceptions1229–1264 ↗
async fn code_mode_exec_surfaces_handler_errors_as_exceptions() -> Result<()>
作用:验证嵌套工具处理失败时,会在脚本里表现成异常。这样脚本可以用 try/catch 自己处理。
数据流:它让脚本错误地调用 tools.exec_command({}) → 脚本捕获异常并输出 caught: → 检查没有走到成功分支。
调用关系:它证明工具层错误会正确传回 JavaScript 运行时,而不是被吞掉或伪装成正常结果。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn);外部调用 3 个(assert!, assert_ne!, skip_if_no_network!)。
code_mode_can_yield_and_resume_with_wait1268–1410 ↗
async fn code_mode_can_yield_and_resume_with_wait() -> Result<()>
作用:验证长脚本可以主动让出控制权,之后通过 wait 继续跑。就像任务先暂停,把编号交给你,之后凭编号回来取进度。
数据流:它让脚本输出 phase 1 后 yield_control,再等两个文件闸门 → 测试依次写文件并调用 wait → 检查每次只返回新阶段输出,最后完成。
调用关系:它使用 wait_for_file_source 控制脚本进度,用 extract_running_cell_id 把第一次返回的编号传给后续 wait。
调用图:调用 9 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_items, extract_running_cell_id, function_tool_output_items, text_item, wait_for_file_source);外部调用 7 个(assert_eq!, concat!, assert_regex_match, format!, write, skip_if_no_network!, vec!)。
code_mode_yield_and_termination_are_not_starved_by_runtime_output1414–1507 ↗
async fn code_mode_yield_and_termination_are_not_starved_by_runtime_output() -> Result<()>
作用:验证脚本疯狂输出时,暂停和终止指令仍然能被处理,不会被输出队列堵死。
数据流:它运行一个大量输出后死循环的脚本 → 第一次请求应快速拿到“仍在运行”的状态 → 再用 wait 发送终止指令 → 检查返回“已终止”。
调用关系:它覆盖高压力场景,确保代码模式控制通道比普通输出有足够优先级。
调用图:调用 8 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_items, extract_running_cell_id, function_tool_output_items, text_item);外部调用 8 个(from_secs, assert!, assert_eq!, concat!, assert_regex_match, skip_if_no_network!, timeout, vec!)。
code_mode_can_run_multiple_yielded_sessions1511–1675 ↗
async fn code_mode_can_run_multiple_yielded_sessions() -> Result<()>
作用:验证可以同时存在多个暂停中的代码模式脚本,并且它们的编号和输出互不串。
数据流:它先启动 session a,再启动 session b,两者都暂停 → 分别写各自闸门文件并调用对应编号的 wait → 检查 a 和 b 各自完成自己的输出。
调用关系:它大量使用编号提取和 wait 输出检查,确认运行时能同时追踪多个后台脚本。
调用图:调用 9 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_items, extract_running_cell_id, function_tool_output_items, text_item, wait_for_file_source);外部调用 8 个(assert_eq!, assert_ne!, concat!, assert_regex_match, format!, write, skip_if_no_network!, vec!)。
code_mode_concurrent_cells_merge_only_the_stored_values_they_write1679–1828 ↗
async fn code_mode_concurrent_cells_merge_only_the_stored_values_they_write() -> Result<()>
作用:验证多个并发脚本写持久变量时,只合并各自写过的键,不会用旧快照覆盖别人更新的值。
数据流:它先存入 a=1、b=2 → 一个暂停脚本把 a 改成 3,另一个脚本把 b 改成 4 → 等第一个完成后再读取 → 检查最终是 a=3、b=4。
调用关系:它测试 store/load 在并发暂停脚本下的合并规则,防止后台脚本恢复时覆盖最新状态。
调用图:调用 9 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_items, custom_tool_output_last_non_empty_text, extract_running_cell_id, text_item, wait_for_file_source);外部调用 6 个(assert_eq!, format!, write, from_str, skip_if_no_network!, vec!)。
code_mode_wait_can_terminate_and_continue1832–1955 ↗
async fn code_mode_wait_can_terminate_and_continue() -> Result<()>
作用:验证通过 wait 终止一个暂停脚本后,代码模式还能继续执行新的脚本。
数据流:它启动一个暂停脚本并拿到编号 → 用 wait 发送 terminate → 检查终止结果 → 再提交一个新 exec,确认新脚本正常完成。
调用关系:它连接了暂停、终止和后续新执行,确认终止不会把整个代码模式运行环境弄坏。
调用图:调用 9 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_items, extract_running_cell_id, function_tool_output_items, text_item, wait_for_file_source);外部调用 6 个(assert_eq!, concat!, assert_regex_match, format!, skip_if_no_network!, vec!)。
code_mode_wait_returns_error_for_unknown_session1958–2015 ↗
async fn code_mode_wait_returns_error_for_unknown_session() -> Result<()>
作用:验证等待一个不存在的脚本编号时,会返回清楚的错误,而不是静默成功或卡住。
数据流:它直接让模型调用 wait,传入不存在的 cell_id → 读取函数工具输出 → 检查成功标记不是 true,内容说明找不到该执行单元。
调用关系:它专门测试 wait 的错误路径,使用 function_tool_output_items 检查普通函数工具输出。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, function_tool_output_items, text_item);外部调用 6 个(assert_eq!, assert_ne!, concat!, assert_regex_match, skip_if_no_network!, vec!)。
code_mode_wait_terminate_returns_completed_session_if_it_finished_after_yield_control2019–2211 ↗
async fn code_mode_wait_terminate_returns_completed_session_if_it_finished_after_yield_control() -> Result<()>
作用:验证脚本让出控制后如果自己后来已经跑完,再发送终止请求时,系统能返回合理结果,而不是崩溃。
数据流:它启动两个暂停脚本,让 session a 后台完成、session b 仍等待 → 等确认 a 写出完成标记 → 对 a 发终止请求 → 接受“已完成”或“已终止”的合理返回。
调用关系:它覆盖一个竞态情况:用户想终止时脚本可能刚好完成。测试保证这种时序下行为可接受。
调用图:调用 9 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_items, extract_running_cell_id, function_tool_output_items, text_item, wait_for_file_source);外部调用 12 个(from_millis, assert!, assert_eq!, concat!, assert_regex_match, format!, write, panic!, try_join, skip_if_no_network! (+2 more))。
code_mode_background_keeps_running_on_later_turn_without_wait2215–2304 ↗
async fn code_mode_background_keeps_running_on_later_turn_without_wait() -> Result<()>
作用:验证脚本 yield_control 后,即使用户下一轮没有调用 wait,后台脚本仍会继续运行。
数据流:它启动一个暂停后会写文件的脚本 → 下一轮让模型调用普通 exec_command 等这个文件出现 → 检查文件确实被后台脚本写出来。
调用关系:它说明暂停不是冻结整个脚本,而是把控制权还给模型,同时脚本可以在后台恢复执行。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_items, text_item);外部调用 8 个(assert!, assert_eq!, concat!, assert_regex_match, format!, try_join, skip_if_no_network!, vec!)。
code_mode_wait_uses_its_own_max_tokens_budget2308–2404 ↗
async fn code_mode_wait_uses_its_own_max_tokens_budget() -> Result<()>
作用:验证 wait 取回脚本后续输出时,可以使用自己传入的输出预算,而不是只能沿用最初脚本的预算。
数据流:它启动脚本,先用较高执行预算输出 phase 1 并暂停 → 后续 wait 传入更小 max_tokens → 检查完成输出被按 wait 的预算截断。
调用关系:它测试暂停恢复时的输出限制归属,确保等待阶段可以单独控制返回长度。
调用图:调用 9 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_items, extract_running_cell_id, function_tool_output_items, text_item, wait_for_file_source);外部调用 7 个(assert_eq!, concat!, assert_regex_match, format!, write, skip_if_no_network!, vec!)。
code_mode_can_output_serialized_text_via_global_helper2407–2434 ↗
async fn code_mode_can_output_serialized_text_via_global_helper() -> Result<()>
作用:验证全局 text() 帮手可以输出对象,并自动序列化成 JSON 字符串。序列化就是把对象变成可传输的文字。
数据流:它让脚本执行 text({ json: true }) → 读取工具输出正文 → 检查结果是 {"json":true}。
调用关系:它检查代码模式提供给脚本的全局输出函数,使用标准辅助入口和输出正文辅助函数。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn);外部调用 4 个(assert_eq!, assert_ne!, eprintln!, skip_if_no_network!)。
code_mode_can_resume_after_set_timeout2437–2461 ↗
async fn code_mode_can_resume_after_set_timeout() -> Result<()>
作用:验证脚本里的 setTimeout 定时等待能正常恢复执行。这样异步 JavaScript 不会卡死。
数据流:它让脚本等待 10 毫秒的 Promise → 然后输出 timer done → 检查返回成功且文字正确。
调用关系:它测试代码模式 JavaScript 运行时对定时器和异步任务的支持。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn);外部调用 3 个(assert_eq!, assert_ne!, skip_if_no_network!)。
code_mode_notify_injects_additional_exec_tool_output_into_active_context2464–2498 ↗
async fn code_mode_notify_injects_additional_exec_tool_output_into_active_context() -> Result<()>
作用:验证脚本调用 notify() 时,可以把额外消息注入当前对话上下文。也就是脚本中途提醒模型一些信息。
数据流:它让脚本先 notify 一个标记,再调用测试工具并输出完成 → 检查下一次发给模型的输入里包含这个通知标记。
调用关系:它不只看最终输出,还检查请求输入项,确认通知被作为 exec 的工具输出插入到活跃上下文中。
调用图:调用 2 个内部函数(start_mock_server, run_code_mode_turn);外部调用 2 个(assert!, skip_if_no_network!)。
code_mode_exit_stops_script_immediately2501–2536 ↗
async fn code_mode_exit_stops_script_immediately() -> Result<()>
作用:验证全局 exit() 会立刻结束脚本,后面的代码不会执行。
数据流:它让脚本输出 before,调用 exit(),再写一行不会执行的输出 → 检查最终只有 before,并且状态是完成。
调用关系:它测试代码模式提供的控制流帮手,使用内容项和正文两种方式确认结果。
调用图:调用 5 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_items, run_code_mode_turn, text_item);外部调用 5 个(assert_eq!, assert_ne!, concat!, assert_regex_match, skip_if_no_network!)。
code_mode_surfaces_text_stringify_errors2539–2576 ↗
async fn code_mode_surfaces_text_stringify_errors() -> Result<()>
作用:验证 text() 遇到无法转成 JSON 的对象时,会把错误清楚地返回给模型。典型例子是循环引用对象。
数据流:它创建一个自己指向自己的对象并传给 text → 读取工具输出 → 检查脚本失败,并说明 Converting circular structure to JSON。
调用关系:它覆盖输出序列化失败的错误路径,确保这类错误不会被隐藏。
调用图:调用 4 个内部函数(start_mock_server, custom_tool_output_items, run_code_mode_turn, text_item);外部调用 6 个(assert!, assert_eq!, assert_ne!, concat!, assert_regex_match, skip_if_no_network!)。
code_mode_can_output_images_via_global_helper2579–2618 ↗
async fn code_mode_can_output_images_via_global_helper() -> Result<()>
作用:验证全局 image() 帮手可以输出图片内容项。图片必须作为 base64 data URI,也就是把图片数据编码进字符串。
数据流:它让脚本调用 image("data:image/png;base64,AAA") → 读取内容项 → 检查第二项是 input_image,带图片 URL 和 high 细节级别。
调用关系:它测试代码模式多模态输出的基础能力,后续图片缩放和图片来源测试都建立在这个能力上。
调用图:调用 5 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_items, run_code_mode_turn, text_item);外部调用 5 个(assert_eq!, assert_ne!, concat!, assert_regex_match, skip_if_no_network!)。
resize_all_images_replaces_malformed_code_mode_image2621–2649 ↗
async fn resize_all_images_replaces_malformed_code_mode_image() -> Result<()>
作用:验证开启“缩放所有图片”后,格式坏掉的代码模式图片会被替换成文字说明,而不是让系统崩掉。
数据流:它启用 ResizeAllImages → 输出一个无效的 base64 图片 → 检查最终内容项变成“图片无法处理,已省略”的文字。
调用关系:它使用带配置的辅助入口,测试图片处理开关对代码模式图片输出的影响。
调用图:调用 4 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_items, run_code_mode_turn_with_config);外部调用 3 个(assert_eq!, assert_ne!, skip_if_no_network!)。
resize_all_images_resizes_explicit_original_code_mode_image2652–2704 ↗
async fn resize_all_images_resizes_explicit_original_code_mode_image() -> Result<()>
作用:验证开启图片缩放后,即使脚本要求 original 细节,过大的图片也会被按规则缩小并保留原始细节标记。
数据流:它生成一张 6401×100 的 PNG,转成 data URI → 脚本用 image(..., "original") 输出 → 解码返回图片,检查尺寸变成 6000×94。
调用关系:它连接真实图片编码/解码和代码模式输出,确保尺寸限制在模型支持范围内。
调用图:调用 5 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_items, run_code_mode_turn_with_model_and_config, new);外部调用 9 个(ImageRgba8, from_pixel, new, assert_eq!, assert_ne!, format!, Rgba, load_from_memory, skip_if_no_network!)。
code_mode_image_helper_rejects_remote_url2707–2744 ↗
async fn code_mode_image_helper_rejects_remote_url() -> Result<()>
作用:验证 image() 不接受远程图片网址。这样工具输出不会让模型或系统去外部随意抓图。
数据流:它让脚本调用 image("https://example.com/image.jpg") → 读取输出 → 检查脚本失败,并提示要传 base64 data URI。
调用关系:它测试图片输出的安全限制,和允许 data URI 的测试形成对照。
调用图:调用 5 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_items, run_code_mode_turn, text_item);外部调用 5 个(assert_eq!, assert_ne!, concat!, assert_regex_match, skip_if_no_network!)。
code_mode_can_use_view_image_result_with_image_helper2747–2827 ↗
async fn code_mode_can_use_view_image_result_with_image_helper() -> Result<()>
作用:验证脚本可以先调用 view_image 读取本地图片,再把这个结果交给 image() 输出给模型。
数据流:它在测试目录写入一张小 PNG → 脚本调用 tools.view_image 获取图片数据,再调用 image(out) → 检查最终输出是 input_image data URI 且细节为 original。
调用关系:它测试内置图片查看工具和代码模式图片输出帮手的衔接。
调用图:调用 7 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_body_and_success, custom_tool_output_items, text_item);外部调用 10 个(assert!, assert_eq!, assert_ne!, concat!, assert_regex_match, format!, write, to_string, skip_if_no_network!, vec!)。
code_mode_can_use_mcp_image_result_with_image_helper2830–2883 ↗
async fn code_mode_can_use_mcp_image_result_with_image_helper() -> Result<()>
作用:验证来自 MCP 工具的图片结果也能交给 image() 输出。也就是外部工具返回的图片能被代码模式转成模型可看的图片项。
数据流:它让 MCP 测试工具返回图片内容 → 脚本取出图片项并调用 image(imageItem) → 检查输出是 data URI 图片且细节为 original。
调用关系:它使用 MCP 配套测试入口,连接外部工具结果和代码模式图片帮手。
调用图:调用 5 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_items, run_code_mode_turn_with_rmcp_model, text_item);外部调用 6 个(assert!, assert_eq!, assert_ne!, concat!, assert_regex_match, skip_if_no_network!)。
code_mode_can_apply_patch_via_nested_tool2886–2923 ↗
async fn code_mode_can_apply_patch_via_nested_tool() -> Result<()>
作用:验证代码模式脚本可以调用 apply_patch 修改工作区文件。这个工具常用于自动改代码。
数据流:它构造一个新增文件的补丁 → 脚本调用 tools.apply_patch 并输出结果 → 检查工具返回 {},再确认文件真的写到了磁盘。
调用关系:它测试代码模式对文件修改工具的嵌套调用能力,既检查模型输出,也检查实际文件系统变化。
调用图:调用 4 个内部函数(start_mock_server, custom_tool_output_items, run_code_mode_turn, text_item);外部调用 6 个(assert_eq!, assert_ne!, concat!, assert_regex_match, format!, skip_if_no_network!)。
code_mode_can_print_structured_mcp_tool_result_fields2926–2961 ↗
async fn code_mode_can_print_structured_mcp_tool_result_fields() -> Result<()>
作用:验证代码模式脚本能读取 MCP 工具返回的结构化字段,比如 structuredContent 和 isError。
数据流:它调用 MCP echo 工具 → 脚本把结构化 echo、环境变量、错误标记和内容长度拼成文本 → 检查输出完全匹配预期。
调用关系:它使用 MCP 辅助入口,确认外部工具的结构化结果没有在代码模式中丢失。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp);外部调用 3 个(assert_eq!, assert_ne!, skip_if_no_network!)。
code_mode_only_can_call_mcp_tool2964–2991 ↗
async fn code_mode_only_can_call_mcp_tool() -> Result<()>
作用:验证代码模式 only 下,脚本内部仍然可以调用 MCP 工具。
数据流:它启用 only 模式并连接 MCP 测试服务器 → 脚本调用 echo 工具 → 检查返回的 echo 文本正确。
调用关系:它通过 run_code_mode_turn_with_rmcp_mode 搭场景,补充测试 only 模式下的外部工具访问。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp_mode);外部调用 3 个(assert_eq!, assert_ne!, skip_if_no_network!)。
code_mode_exposes_mcp_tools_on_global_tools_object2994–3032 ↗
async fn code_mode_exposes_mcp_tools_on_global_tools_object() -> Result<()>
作用:验证 MCP 工具会出现在脚本的全局 tools 对象上,并且是可调用函数。
数据流:它让脚本检查 tools 里是否有 mcp__rmcp__echo,再调用它 → 输出函数类型、echo 结果、错误标记和内容长度 → 检查全部正确。
调用关系:它确认 MCP 工具不是只在元数据里存在,而是真的挂到了 JavaScript 可调用对象上。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp);外部调用 3 个(assert_eq!, assert_ne!, skip_if_no_network!)。
code_mode_uses_non_prefixed_mcp_tool_names_when_feature_enabled3035–3076 ↗
async fn code_mode_uses_non_prefixed_mcp_tool_names_when_feature_enabled() -> Result<()>
作用:验证打开“非前缀 MCP 工具名”功能后,脚本使用 rmcp__echo 而不是 mcp__rmcp__echo。
数据流:它启用对应功能 → 脚本检查非前缀和前缀名字的函数存在性,并调用非前缀工具 → 解析 JSON,确认只有非前缀可用且结果正确。
调用关系:它调用最完整的 MCP 配置辅助函数,专门测试工具命名规则开关。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp_config);外部调用 4 个(assert_eq!, assert_ne!, from_str, skip_if_no_network!)。
code_mode_exposes_namespaced_mcp_tools_on_global_tools_object3079–3112 ↗
async fn code_mode_exposes_namespaced_mcp_tools_on_global_tools_object() -> Result<()>
作用:验证代码模式全局 tools 同时暴露内置工具和带命名空间的 MCP 工具。命名空间就是给工具名前加一段来源标识,避免撞名。
数据流:它让脚本检查 tools.exec_command 和 tools.mcp__rmcp__echo 的函数类型 → 输出 JSON → 检查 MCP 工具存在,内置命令工具按平台情况存在。
调用关系:它用 MCP 辅助入口,关注 tools 对象的整体可见性。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp);外部调用 4 个(assert_eq!, assert_ne!, from_str, skip_if_no_network!)。
code_mode_exposes_normalized_illegal_mcp_tool_names3115–3141 ↗
async fn code_mode_exposes_normalized_illegal_mcp_tool_names() -> Result<()>
作用:验证 MCP 里不适合直接当 JavaScript 名字的工具名,会被规范化后暴露出来。
数据流:它调用规范化后的 tools.mcp__rmcp__echo_tool → 输出结构化 echo 字段 → 检查结果正确。
调用关系:它保证外部工具命名不规范时,代码模式仍能给脚本一个合法、稳定的调用名。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp);外部调用 3 个(assert_eq!, assert_ne!, skip_if_no_network!)。
code_mode_lists_global_scope_items3144–3252 ↗
async fn code_mode_lists_global_scope_items() -> Result<()>
作用:验证代码模式脚本的全局作用域里只出现预期对象和帮手。全局作用域就是脚本不用导入就能直接用的名字集合。
数据流:它让脚本列出 globalThis 上的属性名 → 转成集合 → 检查每个出现的名字都在白名单里。
调用关系:它使用 MCP 环境跑,因为要覆盖工具对象和 ALL_TOOLS 等注入项,防止意外暴露危险或杂乱的全局对象。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp);外部调用 3 个(assert!, assert_ne!, skip_if_no_network!)。
code_mode_exports_all_tools_metadata_for_builtin_tools3255–3288 ↗
async fn code_mode_exports_all_tools_metadata_for_builtin_tools() -> Result<()>
作用:验证 ALL_TOOLS 会导出内置工具的说明和 TypeScript 风格声明。TypeScript 声明可以理解成告诉脚本“这个工具要什么参数、返回什么”。
数据流:它让脚本在 ALL_TOOLS 找 view_image → 输出该工具元数据 JSON → 检查名称、描述和声明文本完整准确。
调用关系:它测试代码模式给模型提供“工具目录”的能力,帮助模型在脚本里正确调用工具。
调用图:调用 4 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_last_non_empty_text, run_code_mode_turn);外部调用 4 个(assert_eq!, assert_ne!, from_str, skip_if_no_network!)。
code_mode_exports_all_tools_metadata_for_namespaced_mcp_tools3291–3334 ↗
async fn code_mode_exports_all_tools_metadata_for_namespaced_mcp_tools() -> Result<()>
作用:验证 ALL_TOOLS 也会导出带命名空间的 MCP 工具元数据,包括服务器说明、工具说明和调用声明。
数据流:它让脚本查找 mcp__rmcp__echo 的元数据 → 输出 JSON → 检查描述里包含命名空间说明、echo 工具说明和类型声明。
调用关系:它和内置工具元数据测试配套,确认外部 MCP 工具也能被模型发现并正确理解。
调用图:调用 4 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_last_non_empty_text, run_code_mode_turn_with_rmcp);外部调用 4 个(assert_eq!, assert_ne!, from_str, skip_if_no_network!)。
code_mode_excludes_configured_nested_tool_namespaces3505–3594 ↗
async fn code_mode_excludes_configured_nested_tool_namespaces() -> Result<()>
作用:验证配置里排除的工具命名空间,不会被代码模式脚本当作嵌套工具使用,也不会出现在 ALL_TOOLS 里。
数据流:它创建一个名为 excluded 的动态工具命名空间,并在代码模式配置里排除它 → 脚本检查该工具和正常工具的可见性 → 检查排除工具不可见,正常工具仍可见。
调用关系:它同时确认混合代码模式下,被排除命名空间仍可作为外层直接工具出现,但不会进入代码模式嵌套工具集合。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_body_and_success);外部调用 5 个(assert!, assert_eq!, assert_ne!, skip_if_no_network!, vec!)。
code_mode_can_print_content_only_mcp_tool_result_fields3597–3637 ↗
async fn code_mode_can_print_content_only_mcp_tool_result_fields() -> Result<()>
作用:验证 MCP 工具只返回普通内容、没有结构化内容时,代码模式脚本仍能正确读取字段。
数据流:它调用 MCP 图片场景工具的纯文本场景 → 脚本读取第一项内容类型、文字、结构化内容和错误标记 → 检查输出符合预期。
调用关系:它补齐 MCP 返回格式的一种常见情况:只有 content,没有 structuredContent。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp);外部调用 3 个(assert_eq!, assert_ne!, skip_if_no_network!)。
code_mode_can_print_error_mcp_tool_result_fields3640–3676 ↗
async fn code_mode_can_print_error_mcp_tool_result_fields() -> Result<()>
作用:验证 MCP 工具返回错误结果时,代码模式脚本能看到 isError 和错误内容,而不是把它当成脚本异常。
数据流:它故意缺少必填参数调用 echo 工具 → 脚本检查错误内容是否提到缺少 message → 输出错误标记、内容长度和结构化内容状态 → 检查全部正确。
调用关系:它区分“工具业务上返回错误”和“代码模式脚本执行失败”,确保脚本可以自己观察 MCP 错误结果。
调用图:调用 3 个内部函数(start_mock_server, custom_tool_output_body_and_success, run_code_mode_turn_with_rmcp);外部调用 3 个(assert_eq!, assert_ne!, skip_if_no_network!)。
code_mode_can_store_and_load_values_across_turns3679–3769 ↗
async fn code_mode_can_store_and_load_values_across_turns() -> Result<()>
作用:验证代码模式的 store 和 load 可以跨对话轮次保存和读取值。像一个小记事本,脚本下一轮还能拿到之前存的东西。
数据流:第一轮脚本存入一个对象并输出 stored → 第二轮脚本读取同一个键并输出 JSON → 测试解析 JSON,确认对象内容完整保留。
调用关系:它检查代码模式运行环境的持久状态能力,和并发合并测试一起覆盖 store/load 的安全性。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, custom_tool_output_body_and_success, custom_tool_output_last_non_empty_text);外部调用 5 个(assert_eq!, assert_ne!, from_str, skip_if_no_network!, vec!)。
code_mode_can_compare_elapsed_time_around_set_timeout3772–3816 ↗
async fn code_mode_can_compare_elapsed_time_around_set_timeout() -> Result<()>
作用:验证代码模式里的时间函数和定时器能配合使用,等待时间真的达到预期。
数据流:脚本记录开始时间,等待 100 毫秒,再记录结束时间并输出差值 → 测试解析 JSON → 检查 elapsed_ms 至少 100,且等待标记为 true。
调用关系:它进一步确认异步定时器不只是能恢复,还能和 Date.now() 这类时间 API 正常配合。
调用图:调用 4 个内部函数(start_mock_server, custom_tool_output_body_and_success, custom_tool_output_last_non_empty_text, run_code_mode_turn);外部调用 5 个(assert!, assert_eq!, assert_ne!, from_str, skip_if_no_network!)。
core/tests/suite/items.rs源码 ↗
这个文件不是真正给用户运行的功能,而是用假服务器模拟模型返回的流式消息,然后观察 Codex 对外发出的事件是否正确。这里的“事件”可以理解成系统广播的小纸条,比如“用户消息开始了”“助手消息完成了”“计划内容更新了”。测试会先启动 mock server(假服务器),挂上一段 SSE(Server-Sent Events,服务器一条条推送文本的方式)响应,再向 Codex 提交用户输入,最后等待并检查收到的事件。它特别关注一些容易出错的边角:计划标签被拆在多段流里、引用标签要被剥掉、图片保存失败也要正常发结束事件、推理原文只有开关打开时才外发。文件开头限制非 Windows 运行,说明这些测试依赖的路径或环境行为不适合 Windows。
disabled_plan_turn47–72 ↗
fn disabled_plan_turn(
text: &str,
_model: String,
collaboration_mode: CollaborationMode,
) -> anyhow::Result<Op>
作用:这个辅助函数用来快速造一个“计划模式”的用户输入操作,并且把权限和沙箱设成比较安全、不会真的要审批的状态。多个计划模式测试都靠它准备同一种输入,避免每个测试重复写一大段配置。
数据流:进去的是用户文字、模型名和协作模式 → 它读取当前工作目录,算出本轮使用的本地环境、沙箱策略和权限配置,再把文字包装成 Op::UserInput → 出来的是一个可以直接提交给 Codex 的操作;它不发送请求,只负责组装测试数据。
调用关系:它被所有计划模式相关测试调用。测试先用它造好一次“请规划”的回合,再交给 codex.submit;后面的事件解析和断言都建立在这个计划模式输入之上。
调用图:调用 2 个内部函数(local_selections, turn_permission_fields);被 5 处调用(plan_mode_emits_plan_item_from_proposed_plan_block, plan_mode_handles_missing_plan_close_tag, plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done, plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_parsed, plan_mode_strips_plan_from_agent_messages);外部调用 4 个(default, Ok, current_dir, vec!)。
image_generation_artifact_path74–96 ↗
fn image_generation_artifact_path(codex_home: &Path, session_id: &str, call_id: &str) -> PathBuf
作用:这个辅助函数算出图片生成结果应该保存到哪个文件路径。它还会把会话 ID 和调用 ID 里不适合当文件名的字符替换掉,避免路径出问题。
数据流:进去的是 Codex 的 home 目录、会话 ID 和图片调用 ID → 它把 ID 清洗成只含字母数字、横线、下划线的安全名字,再拼成 generated_images/会话/调用.png → 出来的是测试期望看到的图片文件路径。
调用关系:两个图片生成测试会先调用它算出目标文件。成功保存的测试用这个路径检查文件内容;保存失败的测试用这个路径确认没有错误地产生文件。
调用图:被 2 处调用(image_generation_call_event_is_emitted, image_generation_call_event_is_emitted_when_image_save_fails);外部调用 2 个(join, format!)。
user_message_item_is_emitted99–157 ↗
async fn user_message_item_is_emitted() -> anyhow::Result<()>
作用:这个测试确认:用户提交一条消息后,Codex 会把它作为一个正式的“用户消息项目”发出开始和完成事件,同时还保留旧版的用户消息事件。
数据流:测试启动假服务器并准备一个空完成响应 → 向 Codex 提交带 text_elements 标注的用户文字 → 等待 ItemStarted、ItemCompleted 和旧版 UserMessage 事件 → 检查开始和完成用的是同一个项目 ID,文字和标注没有丢。
调用关系:它通过 start_mock_server、mount_sse_once 和 sse 搭好假模型响应,用 test_codex 创建被测对象,再用 wait_for_event_match 从事件流里挑出目标事件。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 6 个(default, Ok, assert_eq!, wait_for_event_match, skip_if_no_network!, vec!)。
assistant_message_item_is_emitted160–212 ↗
async fn assistant_message_item_is_emitted() -> anyhow::Result<()>
作用:这个测试确认:模型返回助手回复时,Codex 会发出助手消息项目的开始和完成事件,并且最终文字正确。
数据流:进去的是一段模拟响应:创建响应、助手消息“all done”、完成 → 测试提交用户请求 → Codex 读取假服务器流并转成事件 → 测试取出 AgentMessage 的开始和完成项目,确认 ID 一致且内容是“all done”。
调用关系:它验证模型流里的 ev_assistant_message 会被系统翻译成 TurnItem::AgentMessage。事件等待仍然交给 wait_for_event_match。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 7 个(default, Ok, assert_eq!, wait_for_event_match, panic!, skip_if_no_network!, vec!)。
reasoning_item_is_emitted215–276 ↗
async fn reasoning_item_is_emitted() -> anyhow::Result<()>
作用:这个测试确认:模型返回推理信息时,Codex 会把摘要和原始推理内容放进一个“推理项目”里发出来。
数据流:测试构造一个带两条摘要和一条详细推理的假 reasoning 事件 → 提交用户输入 → 等待 Reasoning 项目的开始和完成 → 检查完成项目里 summary_text 和 raw_content 正好等于假服务器给的内容。
调用关系:它依赖 ev_reasoning_item 生成模拟模型事件,用来验证核心系统把模型协议里的推理块转换成统一的 TurnItem::Reasoning。
调用图:调用 5 个内部函数(ev_reasoning_item, mount_sse_once, sse, start_mock_server, test_codex);外部调用 6 个(default, Ok, assert_eq!, wait_for_event_match, skip_if_no_network!, vec!)。
web_search_item_is_emitted279–348 ↗
async fn web_search_item_is_emitted() -> anyhow::Result<()>
作用:这个测试确认:当模型触发网页搜索时,Codex 会发出网页搜索开始、旧版开始事件,以及完成事件,并记录搜索词。
数据流:测试准备“网页搜索调用已添加”和“网页搜索完成”的假流 → 提交用户输入 → 从事件流里拿到 ItemStarted、WebSearchBegin、ItemCompleted → 检查调用 ID 对得上、时间戳存在、完成后的搜索动作里有 query“weather seattle”。
调用关系:它连接了新的通用项目事件和旧的 WebSearchBegin 事件,确保两套事件都还能被上层界面或调用方使用。
调用图:调用 6 个内部函数(ev_web_search_call_added_partial, ev_web_search_call_done, mount_sse_once, sse, start_mock_server, test_codex);外部调用 7 个(default, Ok, assert!, assert_eq!, wait_for_event_match, skip_if_no_network!, vec!)。
image_generation_call_event_is_emitted351–436 ↗
async fn image_generation_call_event_is_emitted() -> anyhow::Result<()>
作用:这个测试确认:图片生成成功时,Codex 不只会发开始和结束事件,还会把返回的 base64 图片数据保存成文件,并在事件里告诉调用方文件路径。
数据流:测试先算出预期图片路径并清掉旧文件 → 假服务器返回一个图片生成完成事件,结果字符串“Zm9v”代表 base64 编码的 foo → 提交请求后等待图片项目开始、ImageGenerationBegin、项目完成和 ImageGenerationEnd → 检查状态、提示词、结果、保存路径和磁盘上的文件内容。
调用关系:它调用 image_generation_artifact_path 算预期路径,借助 ev_image_generation_call 模拟模型返回。这个测试覆盖事件流和磁盘写文件两件事。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, image_generation_artifact_path);外部调用 8 个(default, Ok, assert!, assert_eq!, wait_for_event_match, skip_if_no_network!, remove_file, vec!)。
image_generation_call_event_is_emitted_when_image_save_fails439–497 ↗
async fn image_generation_call_event_is_emitted_when_image_save_fails() -> anyhow::Result<()>
作用:这个测试确认:图片数据坏掉、保存不了文件时,Codex 仍然要正常发图片生成结束事件,而不是崩掉或卡住。
数据流:测试算出理论保存路径并删除旧文件 → 假服务器返回一个看起来完成、但图片内容无法正常解码的结果“_-8” → 提交请求后等待开始和结束事件 → 检查结束事件仍带有状态、提示词和原始结果,但 saved_path 是空,并且磁盘上没有文件。
调用关系:它和成功保存测试是一对:同样使用 image_generation_artifact_path 和图片生成假事件,但重点验证失败分支也能给上层一个清楚结果。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, image_generation_artifact_path);外部调用 8 个(default, Ok, assert!, assert_eq!, wait_for_event_match, skip_if_no_network!, remove_file, vec!)。
agent_message_content_delta_has_item_metadata500–565 ↗
async fn agent_message_content_delta_has_item_metadata() -> anyhow::Result<()>
作用:这个测试确认:助手消息在流式输出每一小段文字时,事件里会带上线程 ID、回合 ID 和项目 ID。这样前端才能知道这段文字应该追加到哪条消息上。
数据流:假服务器先添加一个消息项目,再推送一段文字增量,最后给完整助手消息 → 测试提交输入 → 等到助手项目开始、文字增量事件和完成项目 → 检查增量事件里的 thread_id、turn_id、item_id 都能和对应项目对上,delta 是“streamed response”。
调用关系:它验证 ev_message_item_added 与 ev_output_text_delta 组合时,Codex 能把流式文字绑定到正确的 AgentMessage 项目。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 6 个(default, Ok, assert_eq!, wait_for_event_match, skip_if_no_network!, vec!)。
plan_mode_emits_plan_item_from_proposed_plan_block568–630 ↗
async fn plan_mode_emits_plan_item_from_proposed_plan_block() -> anyhow::Result<()>
作用:这个测试确认:在计划模式下,助手回复里被 <proposed_plan> 包起来的内容会被识别成单独的计划项目,而不是普通聊天文字。
数据流:假服务器返回一段包含 Intro、计划标签、Outro 的助手消息 → 测试用 disabled_plan_turn 提交计划模式输入 → 等待 PlanDelta 和 Plan 项目完成 → 检查计划增量和完成文本都只包含标签内部的两步计划。
调用关系:它是计划模式解析的基础测试,调用 disabled_plan_turn 准备带 CollaborationMode::Plan 的输入,再观察 Codex 是否发出 PlanDelta 和 TurnItem::Plan。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_plan_turn);外部调用 6 个(Ok, assert_eq!, wait_for_event_match, format!, skip_if_no_network!, vec!)。
plan_mode_strips_plan_from_agent_messages633–717 ↗
async fn plan_mode_strips_plan_from_agent_messages() -> anyhow::Result<()>
作用:这个测试确认:计划模式下,计划内容会从普通助手消息里剥离出来。也就是说,用户看到的聊天文字不应该重复显示计划块。
数据流:假服务器返回 Intro + proposed_plan 块 + Outro → 测试提交计划模式输入 → 持续收集助手文字增量、计划增量、助手完成项和计划完成项 → 最后检查助手文字只有“Intro\nOutro”,计划文字只有两步计划。
调用关系:它在 plan_mode_emits_plan_item_from_proposed_plan_block 的基础上进一步检查“拆分”是否干净:计划交给 Plan 项目,普通文字留给 AgentMessage。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_plan_turn);外部调用 7 个(new, Ok, assert_eq!, wait_for_event, format!, skip_if_no_network!, vec!)。
plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done720–897 ↗
async fn plan_mode_streaming_citations_are_stripped_across_added_deltas_and_done() -> anyhow::Result<()>
作用:这个测试检查一个很容易出错的流式场景:引用标签和计划标签被拆成很多小片段时,Codex 仍然要正确识别计划,并把引用标记删掉。
数据流:测试把助手文本拆成初始 added_text 和多段 delta,其中 <oai-mem-citation> 引用标签、<proposed_plan> 计划标签都跨片段断开 → 提交计划模式输入后按顺序收集项目开始、增量、完成和 TurnComplete → 检查助手文本变成“Intro \nOutro”,计划文本变成两步计划,所有输出都不含引用标签,并且开始、增量、完成、回合完成的顺序合理。
调用关系:它调用 disabled_plan_turn 进入计划模式,用多次 ev_output_text_delta 模拟真实网络流里“标签被切碎”的情况,是对计划解析器和事件顺序的压力测试。
调用图:调用 8 个内部函数(ev_assistant_message, ev_completed, ev_output_text_delta, mount_sse_once, sse, start_mock_server, test_codex, disabled_plan_turn);外部调用 8 个(new, Ok, assert!, assert_eq!, wait_for_event, format!, skip_if_no_network!, vec!)。
plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_parsed900–1005 ↗
async fn plan_mode_streaming_proposed_plan_tag_split_across_added_and_delta_is_parsed() -> anyhow::Result<()>
作用:这个测试确认:如果 <proposed_plan> 这个开始标签一半在初始消息里、一半在后续增量里,Codex 也能把它拼起来识别。
数据流:假响应把“<proposed”放在 item added 文本里,把“_plan>...”放在后续 delta 里 → 测试提交计划模式输入并收集助手和计划的开始、增量、完成 → 检查助手最终只剩 Intro 和 Outro,计划最终是“- Step 1\n”。
调用关系:它专门补上标签跨事件边界的情况,确保计划解析不是只在单个文本块里简单查字符串。
调用图:调用 8 个内部函数(ev_assistant_message, ev_completed, ev_output_text_delta, mount_sse_once, sse, start_mock_server, test_codex, disabled_plan_turn);外部调用 7 个(new, Ok, assert_eq!, wait_for_event, format!, skip_if_no_network!, vec!)。
plan_mode_handles_missing_plan_close_tag1008–1085 ↗
async fn plan_mode_handles_missing_plan_close_tag() -> anyhow::Result<()>
作用:这个测试确认:如果模型忘了写 </proposed_plan> 结束标签,Codex 也能把后面已经进入计划块的内容当成计划,而不是丢掉。
数据流:假服务器返回“Intro\n<proposed_plan>\n- Step 1\n”,没有关闭标签 → 测试提交计划模式输入 → 等到计划增量、计划完成和助手完成 → 检查计划内容是 Step 1,助手内容只保留进入计划块之前的 Intro。
调用关系:它调用 disabled_plan_turn 进入计划模式,验证解析器面对不完整标签时的容错行为,避免模型输出稍不规范就破坏事件流。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_plan_turn);外部调用 5 个(Ok, assert_eq!, wait_for_event, skip_if_no_network!, vec!)。
reasoning_content_delta_has_item_metadata1088–1135 ↗
async fn reasoning_content_delta_has_item_metadata() -> anyhow::Result<()>
作用:这个测试确认:推理摘要的流式增量事件会带上对应推理项目的 ID。这样界面才能把“step one”追加到正确的推理块里。
数据流:假服务器先添加一个 reasoning 项目,再推送 reasoning summary 的文字增量,最后给完整 reasoning 项目 → 测试提交输入 → 等到推理项目开始和 ReasoningContentDelta → 检查增量事件的 item_id 等于开始项目的 id,delta 是“step one”。
调用关系:它验证 ev_reasoning_item_added 与 ev_reasoning_summary_text_delta 的配合,重点不是推理内容本身,而是内容和项目身份的绑定。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 6 个(default, Ok, assert_eq!, wait_for_event_match, skip_if_no_network!, vec!)。
reasoning_raw_content_delta_respects_flag1138–1190 ↗
async fn reasoning_raw_content_delta_respects_flag() -> anyhow::Result<()>
作用:这个测试确认:只有配置打开 show_raw_agent_reasoning 时,原始推理内容的流式增量才会对外发出。原始推理就是更底层、更详细的推理文本,默认通常不展示。
数据流:测试创建 Codex 时先把 show_raw_agent_reasoning 设为 true → 假服务器推送 raw reasoning delta“raw detail”并给完整 reasoning 项目 → 提交输入后等待推理项目开始和 ReasoningRawContentDelta → 检查原始增量绑定到正确 item_id,内容没有被吞掉。
调用关系:它通过 test_codex().with_config 改测试配置,再用 reasoning 相关假事件触发流程,验证配置开关会影响事件是否外发。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 6 个(default, Ok, assert_eq!, wait_for_event_match, skip_if_no_network!, vec!)。
core/tests/suite/user_shell_cmd.rs源码 ↗
这个文件像一套体检表,用来检查 Codex 里的“用户 shell 命令”功能。shell 命令就是用户让系统在本机命令行里执行的指令,比如 ls、cat、seq。测试会搭一个假的模型服务器,创建临时目录或伪造模型回复,然后把命令提交给 Codex,观察它发出的事件:命令开始、输出片段、命令结束、报错、回合完成等。它重点验证几件事:命令确实在正确目录运行;没有本地环境时要明确报错;长命令可以被打断;用户命令不能顶掉模型正在进行的回合;命令历史会保存并在后续发给模型;网络沙箱相关环境变量不会错误地传给用户命令;超长输出会被截断,而且只截断一次。简单说,它保证用户按下“运行命令”后,系统既好用,又不会把对话流程和安全边界搞乱。
user_shell_cmd_ls_and_cat_in_temp_dir39–101 ↗
async fn user_shell_cmd_ls_and_cat_in_temp_dir()
作用:这个测试确认用户发起的 shell 命令会在指定工作目录里运行,并且能拿到真实输出。它用 ls 看文件是否存在,再用 cat 看文件内容是否原样返回。
数据流:进去的是一个新建的临时目录,里面放了 hello.txt 和固定文本;测试把 Codex 的当前目录改到这个临时目录,然后提交 ls 命令,检查结束事件里的退出码和输出;接着提交 cat hello.txt,检查输出内容。出来的结果是两条断言:命令成功退出,并且输出和预期一致;在 Windows 上还会把换行符统一一下,避免系统差异影响测试。
调用关系:它先用 start_mock_server 准备假的服务器,再用 test_codex 建一个测试会话;提交命令后,通过 wait_for_event 等待 ExecCommandEnd 事件。这个测试站在最基础的位置,证明用户 shell 命令这条通路从提交到执行再到返回结果是通的。
调用图:调用 2 个内部函数(start_mock_server, test_codex);外部调用 8 个(new, assert!, assert_eq!, cfg!, wait_for_event, format!, write, unreachable!)。
user_shell_command_without_local_environment_emits_error104–135 ↗
async fn user_shell_command_without_local_environment_emits_error() -> anyhow::Result<()>
作用:这个测试确认:如果当前会话没有可用的本地运行环境,用户不能偷偷执行 shell 命令。系统应该给出清楚的错误,而不是静默失败或乱跑。
数据流:进去的是一个正常创建的测试会话;测试随后通过 submit_thread_settings 把环境选择改成没有任何本地环境;再提交 echo shell。系统没有执行命令,而是发出 Error 事件。出来的是错误消息“shell is unavailable in this session”,并且没有额外的 codex 错误信息。
调用关系:它用 start_mock_server 和 test_codex 搭好会话,再把线程设置改成无本地环境。之后 wait_for_event 专门等错误事件。这个测试保护的是权限和环境选择的边界:没有本地环境时,RunUserShellCommand 不能被执行。
调用图:调用 3 个内部函数(start_mock_server, test_codex, new);外部调用 6 个(default, assert_eq!, submit_thread_settings, wait_for_event, unreachable!, vec!)。
user_shell_cmd_can_be_interrupted138–176 ↗
async fn user_shell_cmd_can_be_interrupted()
作用:这个测试确认用户启动的长时间 shell 命令可以被中断。没有这个能力,用户一旦运行了卡住的命令,就只能干等。
数据流:进去的是一个测试会话和一条 sleep 5 这样的长命令;测试先提交命令,等到 ExecCommandBegin 说明命令已经开始;然后提交 Interrupt。系统随后发出 TurnAborted 事件。出来的结果是中止原因必须是 Interrupted,也就是“用户主动打断”。
调用关系:它通过 wait_for_event_match 先确认用户 shell 命令确实启动了,再发 Op::Interrupt,最后用 wait_for_event_with_timeout 等待中断结果。这个测试覆盖的是运行中控制流程,确保命令执行器能响应外部打断。
调用图:调用 2 个内部函数(start_mock_server, test_codex);外部调用 5 个(from_secs, assert_eq!, wait_for_event_match, wait_for_event_with_timeout, unreachable!)。
user_shell_command_does_not_replace_active_turn179–298 ↗
async fn user_shell_command_does_not_replace_active_turn() -> anyhow::Result<()>
作用:这个测试确认:当模型正在处理一个回合时,用户临时运行 shell 命令不会把那个模型回合挤掉。也就是说,用户命令可以插进来跑,但不能把正在进行的对话任务取消。
数据流:进去的是一个假模型服务器,它先返回一次模型发起的 shell_command 调用,再返回最终回答;测试启动一个用户输入回合,让模型的命令先开始运行。此时再提交一个用户 shell 命令。测试持续读取事件,记录有没有看到“被替换而中止”、有没有看到用户命令结束、有没有看到整个回合完成。出来的结果要求:模型回合最终完成,用户命令也结束,但不能出现 Replaced 这种“回合被顶替”的中止原因;同时假服务器应收到两次请求,说明模型流程继续走完了。
调用关系:它用 mount_sse_sequence 预设模型服务器的两段流式回复,用 turn_permission_fields 和 local_selections 配好允许本地执行的回合设置。它等待 Agent 来源的命令开始后,再插入 UserShell 来源的命令。这个测试专门守住“用户命令”和“模型回合”并行时的调度规则。
调用图:调用 6 个内部函数(mount_sse_sequence, sse, start_mock_server, local_selections, test_codex, turn_permission_fields);外部调用 9 个(default, from_secs, assert!, assert_eq!, cfg!, wait_for_event_match, json!, timeout, vec!)。
user_shell_command_does_not_set_network_sandbox_env_var384–426 ↗
async fn user_shell_command_does_not_set_network_sandbox_env_var() -> anyhow::Result<()>
作用:这个测试确认用户自己运行的 shell 命令不会被错误地塞入网络沙箱环境变量。网络沙箱可以理解成限制网络访问的安全开关;这里要保证这个开关不会以错误方式暴露给用户命令。
数据流:进去的是一个把网络策略设为 Restricted 的测试配置,以及一条读取 CODEX_SANDBOX_NETWORK_DISABLED 环境变量的命令;测试提交命令后等待 ExecCommandEnd。出来的结果要求命令成功退出,并且 stdout 是 not-set,表示这个环境变量没有被设置。
调用关系:它用 test_codex 的配置钩子调整权限配置,再通过 start_mock_server 创建测试服务器。随后直接提交用户 shell 命令,并用 wait_for_event_match 拿到结束事件。这个测试保护的是用户命令运行环境,防止内部沙箱标记泄漏或误用。
调用图:调用 2 个内部函数(start_mock_server, test_codex);外部调用 2 个(assert_eq!, wait_for_event_match)。
user_shell_command_output_is_truncated_in_history430–490 ↗
async fn user_shell_command_output_is_truncated_in_history() -> anyhow::Result<()>
作用:这个测试确认:用户 shell 命令如果输出太长,保存到历史并发给模型时会被缩短。这样可以避免一大坨终端输出塞爆模型上下文,浪费空间甚至导致请求失败。
数据流:进去的是一个把 tool_output_token_limit 设为 100 的配置,以及一条会输出 1 到 400 的命令;测试先运行命令,确认它成功结束。然后提交下一轮用户消息,查看发给模型的历史记录。出来的结果是历史里的输出不是完整 400 行,而是带有“truncated output”警告、总行数、前半部分和后半部分的压缩版本。
调用关系:它先用 test_codex 设置输出长度限制,再用 wait_for_event_match 等命令结束,用 wait_for_event 等回合完成。之后 mount_sse_sequence 捕获下一次模型请求,并用 assert_regex_match 对历史文本做精确检查。这个测试确保截断发生在“写入模型历史”这个位置,而不是影响用户看到的实际命令结束事件。
调用图:调用 3 个内部函数(mount_sse_sequence, start_mock_server, test_codex);外部调用 7 个(assert_eq!, assert_regex_match, wait_for_event, wait_for_event_match, format!, escape, vec!)。
user_shell_command_is_truncated_only_once493–554 ↗
async fn user_shell_command_is_truncated_only_once() -> anyhow::Result<()>
作用:这个测试确认模型调用 shell_command 得到的超长输出只会被截断一次。它防止同一份输出先被截短、后面又被当成长输出再截一遍,导致提示文字重复、内容更难读。
数据流:进去的是一个设置了输出 token 上限的会话,以及假模型发起的超大输出命令,比如 seq 1 2000;测试让模型触发 shell_command,并捕获第二次发给模型的请求,也就是工具结果回传。出来的结果是工具输出里“Total output lines:”这种截断头只出现一次。
调用关系:它先用 skip_if_no_network 跳过无网络环境,再用 mount_sse_once 准备两次模型流式响应:第一次要求执行 shell_command,第二次给出最终回答。submit_turn_with_permission_profile 触发完整流程后,测试检查 mock2 收到的 function_call_output。这个测试守的是输出整理链路,确保不同阶段不会重复做同一件截断工作。
调用图:调用 4 个内部函数(mount_sse_once, sse, start_mock_server, test_codex);外部调用 5 个(assert_eq!, cfg!, json!, skip_if_no_network!, vec!)。
core/tests/suite/view_image.rs源码 ↗
这个测试文件像一套验收清单,确认 Codex 在非 Windows 系统上处理图片时不会出错。它会临时造出 PNG 图片或假文件,启动一个假的模型服务器,让服务器假装返回“请调用 view_image 工具”之类的指令,然后检查 Codex 发回给模型的请求里到底有没有正确的图片内容。图片内容会被转成 data URL,也就是把图片字节用 Base64(一种把二进制变成文本的编码)塞进字符串里。文件还特别测试大图会不会按规则缩小、目录和 JSON 文件会不会被拒绝、沙箱权限(一套限制程序能读哪些文件的规则)会不会生效、远程和本地环境会不会选对,以及模型不支持图片时有没有清楚报错。没有这些测试,图片可能被错误上传、绕过权限、尺寸太大,或者失败时让用户和模型都看不懂原因。
disabled_user_turn76–99 ↗
fn disabled_user_turn(test: &TestCodex, items: Vec<UserInput>, model: String) -> Op
作用:组装一个测试用的“用户发起一轮对话”的操作,并把权限设成很宽松的禁用配置。测试里用它来避免真实审批流程干扰图片功能本身。
数据流:进去的是测试对象、用户输入列表和模型名 → 它读取测试工作目录,调用权限字段生成工具,填好沙箱、审批策略和模型设置 → 出来的是一个可以提交给 Codex 的 Op::UserInput,不直接发请求,只是把这一轮对话打包好。
调用关系:它是很多测试的共同开场道具。图片缩放测试、view_image 工具测试、错误处理测试都会先用它造出一轮用户输入;它内部把权限细节交给 turn_permission_fields,并用默认值补齐不关心的设置。
调用图:调用 1 个内部函数(turn_permission_fields);被 13 处调用(assert_user_turn_local_image_resizes_to, replaces_invalid_local_image_after_bad_request, resize_all_images_turns_invalid_view_image_into_placeholder, view_image_tool_attaches_local_image, view_image_tool_can_preserve_original_resolution_when_requested_on_gpt5_3_codex, view_image_tool_does_not_force_original_resolution_with_capability_only, view_image_tool_errors_clearly_for_unsupported_detail_values, view_image_tool_errors_for_non_image_files, view_image_tool_errors_when_file_missing, view_image_tool_errors_when_path_is_directory (+3 more));外部调用 1 个(default)。
image_messages101–122 ↗
fn image_messages(body: &Value) -> Vec<&Value>
作用:从发给模型的 JSON 请求里找出包含图片输入的消息。它让测试不用手写一堆重复的 JSON 查找代码。
数据流:进去的是一份 JSON 请求体 → 它查看 input 数组,筛出 type 是 message 且 content 里含有 input_image 的项目 → 出来的是这些图片消息的列表;请求体本身不会被改动。
调用关系:它是检查图片是否被塞进请求里的底层小工具。find_image_message 会调用它,再从结果里取第一个,测试函数则用这些结果判断图片有没有按预期出现或消失。
调用图:被 1 处调用(find_image_message);外部调用 1 个(get)。
find_image_message124–126 ↗
fn find_image_message(body: &Value) -> Option<&Value>
作用:拿到请求里的第一个图片消息。测试只关心“有没有一张图片被发送”时,用它最方便。
数据流:进去的是 JSON 请求体 → 它调用 image_messages 找出所有图片消息,然后取第一个 → 出来的是可选的 JSON 项;有图片就返回 Some,没有就返回 None。
调用关系:它站在具体断言和底层 JSON 搜索之间。缩放测试和其他请求检查会通过它快速判断图片消息是否存在,而真正的筛选工作交给 image_messages。
调用图:调用 1 个内部函数(image_messages);被 1 处调用(assert_user_turn_local_image_resizes_to)。
png_bytes128–133 ↗
fn png_bytes(width: u32, height: u32, rgba: [u8; 4]) -> anyhow::Result<Vec<u8>>
作用:临时生成一张纯色 PNG 图片的字节内容。测试用它造图片文件,不需要依赖仓库里现成的图片资源。
数据流:进去的是宽、高和 RGBA 颜色;RGBA 就是红、绿、蓝、透明度四个数 → 它创建一张每个像素都同色的图片,并编码成 PNG → 出来的是 PNG 文件的二进制字节。
调用关系:它是造测试图片的基础零件。write_workspace_png 会调用它再写入文件;本地环境、远程环境和沙箱相关测试也会直接或间接用它准备图片。
调用图:调用 1 个内部函数(new);被 4 处调用(view_image_routes_to_selected_local_environment, view_image_routes_to_selected_remote_environment, view_image_tool_applies_local_sandbox_read_denies, write_workspace_png);外部调用 4 个(ImageRgba8, from_pixel, new, Rgba)。
create_workspace_directory135–146 ↗
async fn create_workspace_directory(test: &TestCodex, rel_path: &str) -> anyhow::Result<PathBuf>
作用:在测试工作区里创建一个目录。它主要用来测试“把目录当图片传给 view_image”时,系统能否给出正确错误。
数据流:进去的是测试对象和相对路径 → 它把相对路径拼成工作区内的绝对路径,转成路径 URI(一种能跨本地/远程文件系统表达路径的格式),调用测试文件系统创建目录 → 出来的是创建好的绝对路径。
调用关系:它服务于目录错误场景。view_image_tool_errors_when_path_is_directory 先用它造一个文件夹,再让模型尝试把这个文件夹当图片读取。
调用图:调用 2 个内部函数(fs, from_path);被 1 处调用(view_image_tool_errors_when_path_is_directory)。
write_workspace_file148–169 ↗
async fn write_workspace_file(
test: &TestCodex,
rel_path: &str,
contents: Vec<u8>,
) -> anyhow::Result<PathBuf>
作用:把任意字节写成测试工作区里的文件。图片、JSON 假文件、坏图片文件都靠它落地。
数据流:进去的是测试对象、相对路径和文件内容字节 → 它先确保父目录存在,再把路径转成 URI,调用测试文件系统写文件 → 出来的是写好的绝对路径,同时磁盘或远程测试文件系统中多了这个文件。
调用关系:它是准备测试文件的通用工具。write_workspace_png 会借它写 PNG;非图片文件、沙箱拒绝、本地环境路由和占位符测试也会直接使用它。
调用图:调用 2 个内部函数(fs, from_path);被 5 处调用(resize_all_images_turns_invalid_view_image_into_placeholder, view_image_routes_to_selected_local_environment, view_image_tool_applies_local_sandbox_read_denies, view_image_tool_errors_for_non_image_files, write_workspace_png)。
write_workspace_png171–179 ↗
async fn write_workspace_png(
test: &TestCodex,
rel_path: &str,
width: u32,
height: u32,
rgba: [u8; 4],
) -> anyhow::Result<PathBuf>
作用:在测试工作区里写一张指定大小和颜色的 PNG 图片。它把“造图片”和“写文件”合成一步。
数据流:进去的是测试对象、相对路径、宽高和颜色 → 它先调用 png_bytes 生成 PNG 字节,再调用 write_workspace_file 写进工作区 → 出来的是图片文件的绝对路径。
调用关系:它是大多数 view_image 正常路径测试的图片准备器。测试函数用它快速创建本地图片,然后把后续的读取、缩放、原图保留、模型能力判断交给 Codex 流程。
调用图:调用 2 个内部函数(png_bytes, write_workspace_file);被 8 处调用(replaces_invalid_local_image_after_bad_request, view_image_tool_attaches_local_image, view_image_tool_can_preserve_original_resolution_when_requested_on_gpt5_3_codex, view_image_tool_does_not_force_original_resolution_with_capability_only, view_image_tool_errors_clearly_for_unsupported_detail_values, view_image_tool_resizes_when_model_lacks_original_detail_support, view_image_tool_returns_unsupported_message_for_text_only_model, view_image_tool_treats_null_detail_as_omitted)。
assert_user_turn_local_image_resizes_to181–264 ↗
async fn assert_user_turn_local_image_resizes_to(
original_dimensions: (u32, u32),
expected_dimensions: (u32, u32),
resize_policy: TestImageResizePolicy,
) -> anyhow::Result<()>
作用:验证用户直接附加的本地图片会被缩放到指定尺寸。它把重复的“造图、提交、抓请求、解码图片、量尺寸”流程封装起来。
数据流:进去的是原始尺寸、期望尺寸和测试用缩放策略 → 它启动假服务器,配置测试 Codex,造一张本地 PNG,提交带图片的用户输入,等这一轮完成,再从发给模型的请求里取出 Base64 图片并解码 → 出来是测试断言结果;如果尺寸不对,测试失败。
调用关系:它是三条用户图片缩放测试的核心助手。上层测试只给不同尺寸和策略;它内部调用 disabled_user_turn、find_image_message 和 mock SSE 响应工具,把完整链路跑一遍。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, find_image_message);被 3 处调用(resize_all_images_applies_patch_budget_to_local_user_image, user_turn_with_local_image_attaches_image, user_turn_with_vertical_local_image_resizes_to_square_bounds);外部调用 7 个(from_pixel, assert_eq!, wait_for_event_with_timeout, Rgba, load_from_memory, tempdir, vec!)。
user_turn_with_local_image_attaches_image273–278 ↗
async fn user_turn_with_local_image_attaches_image() -> anyhow::Result<()>
作用:测试横向大图作为用户输入时,会被附加并按旧规则缩小。它确认最常见的截图上传场景可用。
数据流:没有业务输入,测试自己指定一张 2304×864 的图片 → 它跳过无网络环境,然后调用 assert_user_turn_local_image_resizes_to → 期望最终发给模型的是 2048×768 的图片。
调用关系:这是 assert_user_turn_local_image_resizes_to 的一个具体用例。它不关心服务器细节,只声明要验证的尺寸变化。
调用图:调用 1 个内部函数(assert_user_turn_local_image_resizes_to);外部调用 1 个(skip_if_no_network!)。
user_turn_with_vertical_local_image_resizes_to_square_bounds281–290 ↗
async fn user_turn_with_vertical_local_image_resizes_to_square_bounds() -> anyhow::Result<()>
作用:测试竖向长图也能按边界正确缩放。它防止只照顾横图、把竖图尺寸算错。
数据流:测试指定一张 1024×4096 的竖图 → 它在可联网环境下调用公共缩放断言助手 → 期望输出变成 512×2048,比例保持不变。
调用关系:它复用 assert_user_turn_local_image_resizes_to,只换了输入形状。这样能覆盖竖屏截图这类常见情况。
调用图:调用 1 个内部函数(assert_user_turn_local_image_resizes_to);外部调用 1 个(skip_if_no_network!)。
resize_all_images_applies_patch_budget_to_local_user_image293–302 ↗
async fn resize_all_images_applies_patch_budget_to_local_user_image() -> anyhow::Result<()>
作用:测试开启 ResizeAllImages 功能开关后,用户本地图片会采用另一套更严格的缩放预算。它确认新功能开关真的影响图片大小。
数据流:测试给出 2048×2048 的方图和 AllImages 策略 → 公共助手会打开对应特性、提交图片、检查请求里的图片尺寸 → 期望结果是 1600×1600。
调用关系:它是图片缩放策略的开关验证。具体搭环境和解码检查仍交给 assert_user_turn_local_image_resizes_to。
调用图:调用 1 个内部函数(assert_user_turn_local_image_resizes_to);外部调用 1 个(skip_if_no_network!)。
view_image_tool_attaches_local_image305–450 ↗
async fn view_image_tool_attaches_local_image() -> anyhow::Result<()>
作用:测试模型调用 view_image 读取工作区本地图片时,Codex 会把图片作为工具结果返回给模型。它还确认不会额外伪造一条独立图片消息。
数据流:测试先在工作区写一张大 PNG,再让假服务器返回一次 view_image 工具调用 → Codex 执行工具后再次请求模型,测试读取第二次请求里的函数输出,解码其中的图片 → 期望工具输出只有一个 input_image,图片被缩到 2048×768,并且事件里记录了开始和完成。
调用关系:这是 view_image 工具的主流程测试。它调用 write_workspace_png 准备文件,用 disabled_user_turn 发起对话,再通过 mock SSE 驱动“模型要求看图→工具返回图片→模型完成”的完整回合。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_png);外部调用 8 个(assert!, assert_eq!, wait_for_event_with_timeout, load_from_memory, panic!, json!, skip_if_no_network!, vec!)。
view_image_routes_to_selected_local_environment453–516 ↗
async fn view_image_routes_to_selected_local_environment() -> anyhow::Result<()>
作用:测试当一轮对话指定了本地环境时,view_image 会去这个本地环境的工作目录找图。它防止多环境时读错地方。
数据流:测试在本地工作区写 local.png,并让模型调用 view_image 时带上本地环境 ID → Codex 按环境 ID 找到图片并把它编码进工具输出 → 测试确认输出里有 data:image/png;base64 开头的图片 URL。
调用关系:它检查环境路由里的本地分支。图片文件由 png_bytes 和 write_workspace_file 准备,请求流程由 mount_sse_sequence 模拟连续两次模型响应。
调用图:调用 5 个内部函数(mount_sse_sequence, start_mock_server, test_codex, png_bytes, write_workspace_file);外部调用 4 个(assert!, assert_eq!, skip_if_no_network!, vec!)。
view_image_tool_applies_local_sandbox_read_denies519–592 ↗
async fn view_image_tool_applies_local_sandbox_read_denies() -> anyhow::Result<()>
作用:测试本地沙箱禁止读取某个图片时,view_image 不会偷偷绕过限制。沙箱可以理解成给程序划的“哪些文件能碰、哪些不能碰”的边界。
数据流:测试先写一个 PNG,再把这条路径加入拒绝读取规则 → 模型请求 view_image 后,Codex 尝试读取但被权限策略挡住 → 发给模型的工具输出变成错误文字,不包含 input_image。
调用关系:它验证 view_image 和权限系统的配合。文件由 write_workspace_file 准备,权限配置用 from_runtime_permissions 生成,最后通过假服务器收到的请求判断是否守住了边界。
调用图:调用 7 个内部函数(mount_sse_sequence, start_mock_server, test_codex, png_bytes, write_workspace_file, from_runtime_permissions, default);外部调用 4 个(assert!, format!, skip_if_no_network!, vec!)。
view_image_routes_to_selected_remote_environment595–695 ↗
async fn view_image_routes_to_selected_remote_environment() -> anyhow::Result<()>
作用:测试 view_image 能按环境 ID 去远程环境读图片,而不是误读本机同名文件。它对同时存在本地和远程工作区的场景很重要。
数据流:测试在本地放一个同名假文件,在远程路径创建真正 PNG,然后让模型调用 view_image 并指定远程环境 ID → Codex 通过远程文件系统读取图片并编码返回 → 测试确认工具输出是图片 URL,最后清理远程目录。
调用关系:它覆盖环境路由里的远程分支。它用 local 构造本地选择,用 PathUri 表达远程路径,用 mount_sse_sequence 推动工具调用和后续完成。
调用图:调用 7 个内部函数(mount_sse_sequence, start_mock_server, local, test_codex, png_bytes, from_abs_path, from_path);外部调用 10 个(from, new, assert!, assert_eq!, get_remote_test_env, format!, write, skip_if_no_network!, skip_if_wine_exec!, vec!)。
view_image_tool_can_preserve_original_resolution_when_requested_on_gpt5_3_codex698–787 ↗
async fn view_image_tool_can_preserve_original_resolution_when_requested_on_gpt5_3_codex() -> anyhow::Result<()>
作用:测试支持原图细节的模型在请求 detail 为 original 时,view_image 会保留原始分辨率。它确保“我要看原图”不会被默认缩小。
数据流:测试选择 gpt-5.3-codex,写入一张 2304×864 图片,让模型调用 view_image 并传 detail: original → Codex 返回工具输出,里面标明 original 并携带图片 → 测试解码图片,确认宽高仍是原始值。
调用关系:它验证模型能力和工具参数一起生效。图片准备交给 write_workspace_png,对话发起交给 disabled_user_turn,完成后直接检查工具输出。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_png);外部调用 6 个(assert_eq!, wait_for_event_with_timeout, load_from_memory, json!, skip_if_no_network!, vec!)。
view_image_tool_errors_clearly_for_unsupported_detail_values790–865 ↗
async fn view_image_tool_errors_clearly_for_unsupported_detail_values() -> anyhow::Result<()>
作用:测试 view_image 收到不支持的 detail 值时,会给出明确错误。这样模型或开发者能知道应该用 high、original,或者干脆不填。
数据流:测试写一张图片,让模型调用 view_image 时传 detail: low → Codex 不生成图片输出,而是返回一段解释性错误文字 → 测试确认错误内容完全符合预期,并且请求中没有 input_image。
调用关系:它是参数校验测试。正常的图片准备和对话流程仍用 write_workspace_png、disabled_user_turn 和 mock SSE,重点断言放在工具输出错误上。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_png);外部调用 6 个(assert!, assert_eq!, wait_for_event_with_timeout, json!, skip_if_no_network!, vec!)。
view_image_tool_treats_null_detail_as_omitted868–955 ↗
async fn view_image_tool_treats_null_detail_as_omitted() -> anyhow::Result<()>
作用:测试 detail 写成 null 时等同于没写。它防止模型生成 JSON 时带 null 导致工具莫名失败。
数据流:测试写一张大图,让模型调用 view_image 并传 detail: null → Codex 按默认 high 处理,缩放图片并返回工具输出 → 测试确认 detail 变成 high,图片尺寸是 2048×768。
调用关系:它验证参数兼容性。流程和其他 view_image 测试一样,由 disabled_user_turn 发起,由假服务器给出工具调用,最后检查工具输出。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_png);外部调用 6 个(assert_eq!, wait_for_event_with_timeout, load_from_memory, json!, skip_if_no_network!, vec!)。
view_image_tool_resizes_when_model_lacks_original_detail_support958–1048 ↗
async fn view_image_tool_resizes_when_model_lacks_original_detail_support() -> anyhow::Result<()>
作用:测试不支持 original 细节的模型会走默认缩放路径。它避免旧模型或低能力模型收到过大的原图。
数据流:测试选择 gpt-5.2,写入一张 2304×864 图片,模型调用 view_image 但不请求 original → Codex 返回 high 细节图片,并按规则缩小 → 测试解码后确认尺寸为 2048×768。
调用关系:它关注模型能力不足时的安全默认行为。图片和对话工具由 write_workspace_png、disabled_user_turn 和 mock SSE 配合完成。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_png);外部调用 6 个(assert_eq!, wait_for_event_with_timeout, load_from_memory, json!, skip_if_no_network!, vec!)。
view_image_tool_does_not_force_original_resolution_with_capability_only1051–1139 ↗
async fn view_image_tool_does_not_force_original_resolution_with_capability_only() -> anyhow::Result<()>
作用:测试模型虽然支持 original,也不会在用户没要求时强行发送原图。能力只是“可以”,不是“默认必须”。
数据流:测试选择支持 original 的 gpt-5.3-codex,写一张大图,但 view_image 参数里没有 detail: original → Codex 按默认 high 返回缩小后的图片 → 测试确认 detail 是 high,尺寸是 2048×768。
调用关系:它和保留原图测试互相补充。前者证明请求 original 会保留,这个证明不请求 original 就仍会缩放。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_png);外部调用 6 个(assert_eq!, wait_for_event_with_timeout, load_from_memory, json!, skip_if_no_network!, vec!)。
view_image_tool_errors_when_path_is_directory1142–1209 ↗
async fn view_image_tool_errors_when_path_is_directory() -> anyhow::Result<()>
作用:测试把目录路径交给 view_image 时会得到清楚错误,而不是把目录当图片处理。它防止奇怪路径造成误导或崩溃。
数据流:测试先创建一个 assets 目录,再让模型调用 view_image 读取这个路径 → Codex 发现它不是文件 → 工具输出返回“image path ... is not a file”,请求里没有图片内容。
调用关系:它使用 create_workspace_directory 准备错误输入,再走标准的 mock 模型工具调用流程。它验证的是文件类型检查这一关。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, create_workspace_directory, disabled_user_turn);外部调用 7 个(assert!, assert_eq!, wait_for_event_with_timeout, format!, json!, skip_if_no_network!, vec!)。
view_image_tool_errors_for_non_image_files1212–1286 ↗
async fn view_image_tool_errors_for_non_image_files() -> anyhow::Result<()>
作用:测试普通文本或 JSON 文件不能被 view_image 当图片返回。它保证工具只处理真正的图片。
数据流:测试把一段 JSON 写成 example.json,让模型要求用 view_image 读它 → Codex 读取后识别出文件类型不是图片 → 工具输出包含 unsupported image application/json 之类的错误,并且没有 input_image。
调用关系:它用 write_workspace_file 准备非图片文件。后续通过假服务器的第二次请求检查工具输出,确认错误被传回模型而不是变成图片。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_file);外部调用 7 个(assert!, assert_eq!, wait_for_event_with_timeout, format!, json!, skip_if_no_network!, vec!)。
resize_all_images_turns_invalid_view_image_into_placeholder1289–1352 ↗
async fn resize_all_images_turns_invalid_view_image_into_placeholder() -> anyhow::Result<()>
作用:测试开启 ResizeAllImages 后,坏图片不会让工具输出复杂错误,而是变成一个固定的文字占位。这个占位告诉模型:图片内容因为无法处理被省略了。
数据流:测试开启功能开关,写入一个 JSON 假装图片,让模型调用 view_image → Codex 处理失败后不附加图片,而是在工具输出里放入 input_text 占位内容 → 测试确认输出数组正好是这条占位文字。
调用关系:它验证新图片处理策略下的失败降级行为。文件准备靠 write_workspace_file,对话发起靠 disabled_user_turn,响应检查靠第二个 mock 请求。
调用图:调用 6 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_file);外部调用 5 个(assert_eq!, wait_for_event_with_timeout, json!, skip_if_no_network!, vec!)。
view_image_tool_errors_when_file_missing1355–1426 ↗
async fn view_image_tool_errors_when_file_missing() -> anyhow::Result<()>
作用:测试图片路径不存在时,view_image 会明确说找不到文件。它防止模型以为图片已经被成功读取。
数据流:测试构造一个不存在的相对路径,不创建文件,然后让模型调用 view_image → Codex 尝试定位失败 → 工具输出以 unable to locate image at ... 开头,且请求里不包含图片。
调用关系:它覆盖最常见的路径错误。它仍使用 disabled_user_turn 和 mock SSE 跑完整工具调用流程,只是故意不准备对应文件。
调用图:调用 5 个内部函数(mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn);外部调用 6 个(assert!, wait_for_event_with_timeout, format!, json!, skip_if_no_network!, vec!)。
view_image_tool_returns_unsupported_message_for_text_only_model1429–1554 ↗
async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> anyhow::Result<()>
作用:测试纯文本模型调用 view_image 时会被拒绝,并得到清楚提示。纯文本模型就是只接受文字输入、不接受图片输入的模型。
数据流:测试先搭一个自定义假模型列表,里面的模型 input_modalities 只有 Text,再写一张 PNG 并让模型调用 view_image → Codex 看到当前模型不支持图片输入 → 工具输出返回“view_image is not allowed because you do not support image inputs”。
调用关系:它专门绕开默认模型信息,自己挂载模型接口返回纯文本模型。随后仍用 disabled_user_turn 发起对话,用 write_workspace_png 准备图片,用 mock SSE 驱动工具调用。
调用图:调用 8 个内部函数(mount_models_once, mount_sse_once, sse, test_codex, disabled_user_turn, write_workspace_png, create_dummy_chatgpt_auth_for_testing, bytes);外部调用 9 个(Limited, default, builder, new, assert_eq!, wait_for_event_with_timeout, json!, skip_if_no_network!, vec!)。
replaces_invalid_local_image_after_bad_request1558–1630 ↗
async fn replaces_invalid_local_image_after_bad_request() -> anyhow::Result<()>
作用:测试当第一次把本地图片发给服务端被判定为坏图片后,Codex 会重试并把图片替换成文字提示。它避免一次图片错误让整轮对话卡死。
数据流:测试让假服务器第一次在看到 input_image 时返回 400 错误,错误文字说图片无效;然后准备第二次成功响应 → Codex 先发送图片,收到坏图片错误后再次发送请求,这次不再带图片,而是带上用户文字 Invalid image → 测试分别检查第一次和第二次请求。
调用关系:它只在非调试构建中运行。它用 write_workspace_png 准备图片,用 disabled_user_turn 提交本地图片输入,用一个错误响应和一个成功 SSE 响应模拟服务端先拒绝再接受的过程。
调用图:调用 7 个内部函数(mount_response_once_match, mount_sse_once, sse, start_mock_server, test_codex, disabled_user_turn, write_workspace_png);外部调用 6 个(new, assert!, wait_for_event_with_timeout, skip_if_no_network!, vec!, body_string_contains)。