Files
MyAiDesk/nomifun-tauri/crates/agent/nomi-browser-engine/tests/integration_single_tab.rs
T
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

118 lines
5.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! **单受控标签验证**`#[ignore]`,本机/打包 chrome):证明 `--no-startup-window` 消除了
//! 命令行冗余 about:blank 启动标签——启动后浏览器里**恰好一个** `type=="page"` 的 target
//! (引擎 `Target.createTarget` 出来的受控页),不再有命令行起始标签那个孤儿空白页。
//!
//! 旧行为 = 命令行 `about:blank` + createTarget 受控页 = **2** 个 page
//! 新行为 = 仅 createTarget 受控页 = **1** 个 page。
//!
//! 手动跑(本机 Windows 有系统 Chrome):
//! set NOMIFUN_CHROME_BINARY=...\chrome.exe
//! cargo nextest run -p nomi-browser-engine --run-ignored all -E 'test(single_tab)'
//! headful 用例会开一个**可见** chrome 窗口(验 keep-alive 在 headful 下生效)。
//! 跑完核对任务管理器无残留 chromeBuilder kill_on_drop 应自动清)。
mod common;
use common::{build_backend_for_fixture, build_backend_for_fixture_headful};
use nomi_browser_engine::BrowserEngine;
/// headless`--no-startup-window` + `--headless=new` 下,启动后恰好一个受控 page。
#[tokio::test]
#[ignore = "需本机 chromeheadless 启动后恰好一个受控 page(无命令行冗余 about:blank"]
async fn single_tab_headless_has_exactly_one_page_target() {
let backend = build_backend_for_fixture("single-tab-headless").await;
let n = backend
.page_target_count_for_test()
.await
.expect("Target.getTargets");
assert_eq!(
n, 1,
"--no-startup-window 后应恰好一个受控 pagecreateTarget),无命令行 about:blank 孤儿,实得 {n}"
);
}
/// headful(关键风险路径):`--no-startup-window` 下 chrome 被 REMOTE_DEBUGGING keep-alive 拴住、
/// **不无窗口自退**launch_chrome 不报 "chrome exited before DevTools port"+ 恰好一个受控 page
/// + 受控页可正常导航(active_target 链路不受影响)。会开一个可见 chrome 窗口。
#[tokio::test]
#[ignore = "需本机 chrome+显示器:headful --no-startup-window keep-alive + 单受控页 + navigate"]
async fn single_tab_headful_keepalive_one_page_and_navigates() {
// build_backend_for_fixture_headful 内部 launch_chrome(cfg, force_headless=false);若
// --no-startup-window 在 headful 下无窗口自退,这一步就会 panic"launch headful chrome")——
// 即评估唯一未经本机证实的风险点,跑通即证伪。
let backend = build_backend_for_fixture_headful("single-tab-headful").await;
let n = backend
.page_target_count_for_test()
.await
.expect("Target.getTargets");
assert_eq!(n, 1, "headful --no-startup-window 后应恰好一个受控 page,实得 {n}");
// 受控页可正常导航(active_target 解引用链路健康)。
let nav = backend
.navigate("about:blank", false)
.await
.expect("navigate on the single controlled page should succeed");
assert!(
nav.final_url.contains("blank") || nav.final_url.contains("about"),
"unexpected final_url after navigate: {}",
nav.final_url
);
}
/// **父死自清(`--remote-debugging-pipe` keystone**:控制进程一死,内核关闭继承的 fd3/fd4 →
/// Chromium 的 DevTools 管道读到 EOF → 浏览器**自行退出**。这是跨平台父死安全网的最优解(含
/// SIGKILL——内核关 fd 不需父进程跑任何代码,见 docs/superpowers/specs/browser-use/2026-06-19-
/// macos-pdeath-pipe-transport-design.md)。本测试在同进程内**隔离出该机制**:连上后关闭**我们这端
/// 的命令管道**drop Connection → drop pipe Sender,chrome fd3 读到 EOF)但**不** kill child;用
/// `try_wait` 探测(不触发 `kill_on_drop`——child 句柄全程持有)断言 chrome 数秒内自退。
#[cfg(unix)]
#[tokio::test]
#[ignore = "需本机 chrome:验 --remote-debugging-pipe 下父死/管道 EOF → chrome 自退"]
async fn chrome_self_exits_when_command_pipe_closes() {
use nomi_browser_engine::launch::{launch_chrome, LaunchConfig};
use nomi_browser_engine::transport::Connection;
use std::time::Duration;
let chrome = nomi_browser_engine::acquire::resolve_chrome_path(
&std::env::temp_dir().join("nomifun-browser-data"),
None,
)
.await
.expect("resolve chrome (set NOMIFUN_CHROME_BINARY)");
let cfg = LaunchConfig {
chrome_path: chrome,
user_data_dir: std::env::temp_dir().join("nomifun-pipe-selfexit-profile"),
headful: false,
};
let launched = launch_chrome(&cfg, true).await.expect("launch chrome (pipe)");
// 单独持有 child 句柄(不 drop → kill_on_drop 不触发,从而隔离出「管道 EOF 致自退」机制本身)。
let mut child = launched.child;
let conn = Connection::connect_launched(launched.transport)
.await
.expect("connect over pipe");
// 确认管道双向可用(命令发得出、回包收得到)后再测关闭。
conn.enable_auto_attach().await.expect("auto attach over pipe");
assert!(
child.try_wait().expect("try_wait").is_none(),
"chrome should be running before we close the pipe"
);
// 模拟父死:关闭我们这端的命令管道(drop Connection → drop Sender,chrome fd3 读到 EOF,不 kill。
drop(conn);
// chrome 应在数秒内自退。try_wait 探测(不 kill、不阻塞)kill_on_drop 因 child 仍被持有而未触发。
let mut exited = false;
for _ in 0..100 {
if child.try_wait().expect("try_wait").is_some() {
exited = true;
break;
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
assert!(
exited,
"chrome 必须在命令管道关闭后自退(--remote-debugging-pipe 的父死自清,SIGKILL 等价)"
);
}