mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: server CLI 骨架,Server 子命令十变体分派加 start 与 cleanup 最小实现
This commit is contained in:
Generated
+18
-18
@@ -1160,7 +1160,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_builtins"
|
name = "oxide_builtins"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_bytecode"
|
name = "oxide_bytecode"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1193,7 +1193,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cfg"
|
name = "oxide_cfg"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_emit",
|
"oxide_emit",
|
||||||
@@ -1204,7 +1204,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_cli"
|
name = "oxide_cli"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"ansi_term",
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
@@ -1225,7 +1225,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_code_cache"
|
name = "oxide_code_cache"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru",
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
@@ -1234,7 +1234,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_compiler"
|
name = "oxide_compiler"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1253,7 +1253,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_dce"
|
name = "oxide_dce"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1267,7 +1267,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_emit"
|
name = "oxide_emit"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
@@ -1282,7 +1282,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_ir"
|
name = "oxide_ir"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_log",
|
"oxide_log",
|
||||||
@@ -1291,7 +1291,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_kernel"
|
name = "oxide_kernel"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1306,7 +1306,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_liveness"
|
name = "oxide_liveness"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1318,7 +1318,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_log"
|
name = "oxide_log"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
@@ -1327,7 +1327,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_parser"
|
name = "oxide_parser"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_ast",
|
"oxc_ast",
|
||||||
@@ -1342,7 +1342,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_regalloc"
|
name = "oxide_regalloc"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_cfg",
|
"oxide_cfg",
|
||||||
@@ -1358,7 +1358,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_runtime_api"
|
name = "oxide_runtime_api"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
@@ -1370,7 +1370,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_test262"
|
name = "oxide_test262"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
"oxide_compiler",
|
"oxide_compiler",
|
||||||
@@ -1387,7 +1387,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_types"
|
name = "oxide_types"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
@@ -1399,7 +1399,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_vm"
|
name = "oxide_vm"
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.8.35"
|
version = "0.8.36"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -4,13 +4,15 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use ansi_term::Colour::Red;
|
use ansi_term::Colour::Red;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
use oxide_cli::format_js_value;
|
||||||
|
use oxide_cli::server::cleanup::{well_known_cleanup, CleanupOutcome};
|
||||||
|
use oxide_cli::server::server::{run_server, run_server_rm, RmServerConfig, ServerConfig};
|
||||||
use oxide_compiler::compiler::{compiled_module_hash, Compiler};
|
use oxide_compiler::compiler::{compiled_module_hash, Compiler};
|
||||||
use oxide_compiler::compiler_error;
|
use oxide_compiler::compiler_error;
|
||||||
use oxide_cli::format_js_value;
|
|
||||||
use oxide_kernel::kernel::{KernelConfig, KernelCore};
|
use oxide_kernel::kernel::{KernelConfig, KernelCore};
|
||||||
use oxide_kernel::shape_forge::ShapeForge;
|
use oxide_kernel::shape_forge::ShapeForge;
|
||||||
use oxide_kernel::string_forge::PermInterner;
|
use oxide_kernel::string_forge::PermInterner;
|
||||||
@@ -95,6 +97,47 @@ enum Commands {
|
|||||||
Test {
|
Test {
|
||||||
suite: Option<String>,
|
suite: Option<String>,
|
||||||
},
|
},
|
||||||
|
/// 持久 server:常驻进程,复用预热池,近零 spawn 成本。
|
||||||
|
Server {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: ServerCommands,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// server 子命令:持久 server 进程管理与 --rm 独立模式。
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum ServerCommands {
|
||||||
|
/// 启动 server。持久形态前台常驻(阻塞至关闭请求、信号或 yield 触发退出);
|
||||||
|
/// --rm 为独立形态(进程唯一 socket、不注册 sidecar、空闲超时或断开即退出)。
|
||||||
|
Start {
|
||||||
|
/// 独立模式:进程唯一 socket 路径,不注册 sidecar,空闲超时或断开即自动退出。
|
||||||
|
#[arg(long)]
|
||||||
|
rm: bool,
|
||||||
|
/// 空闲超时(秒),仅对 --rm 有效;窗口内无连接或连接上无数据即自动退出。
|
||||||
|
#[arg(long, default_value = "30")]
|
||||||
|
idle_timeout: u64,
|
||||||
|
/// 常驻 worker 线程数;缺省取宿主核数。
|
||||||
|
#[arg(long)]
|
||||||
|
workers: Option<u32>,
|
||||||
|
},
|
||||||
|
/// 向持久 server 发送关闭请求。
|
||||||
|
Stop,
|
||||||
|
/// 查询 server 状态(池与运行时长)。
|
||||||
|
Status,
|
||||||
|
/// 健康检查。
|
||||||
|
Health,
|
||||||
|
/// 查询 server 信息(版本、socket 路径、进程号)。
|
||||||
|
Info,
|
||||||
|
/// 查询 server 版本。
|
||||||
|
Version,
|
||||||
|
/// 清理残留文件(无 sidecar、陈旧 socket、杀刻度匹配进程)。
|
||||||
|
Cleanup,
|
||||||
|
/// 向持久 server 发送 yield 请求(排空退出后重新接管 socket 路径)。
|
||||||
|
Restart,
|
||||||
|
/// 查看 server 日志。
|
||||||
|
Log,
|
||||||
|
/// 查询内部状态(代码、对象、字符串、属性)。
|
||||||
|
Forge,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
@@ -156,10 +199,88 @@ fn main() -> ExitCode {
|
|||||||
bench::run_benchmarks(config, kernel, pool)
|
bench::run_benchmarks(config, kernel, pool)
|
||||||
}
|
}
|
||||||
Some(Commands::Test { .. }) => not_implemented("test"),
|
Some(Commands::Test { .. }) => not_implemented("test"),
|
||||||
|
Some(Commands::Server { command }) => server_command(command),
|
||||||
None => repl(),
|
None => repl(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// server 子命令分派:start(两形态)与 cleanup 为最小实现,其余八臂为
|
||||||
|
/// not_implemented 占位。
|
||||||
|
///
|
||||||
|
/// # 步骤
|
||||||
|
/// 1. Start:rm 为假调持久 server 入口(well-known 路径),rm 为真调独立
|
||||||
|
/// 模式入口(进程唯一路径);`--workers` 为 Some 时覆盖 worker 数。
|
||||||
|
/// 2. Cleanup:调 well-known 路径清理入口,四态结果映射退出码(无残留与
|
||||||
|
/// 已清理退 0,拒绝与杀进程失败退 1)并打印结果。
|
||||||
|
/// 3. 其余八臂:not_implemented 占位(退出码 2)。
|
||||||
|
///
|
||||||
|
/// # 边界与前提
|
||||||
|
/// - `--idle-timeout` 不带 --rm 时静默忽略(语义门控归后续任务)。
|
||||||
|
/// - 全局旗标 -v / -q / --profile 在 server 臂为空操作。
|
||||||
|
///
|
||||||
|
/// # 副作用
|
||||||
|
/// - start 阻塞当前进程至 server 退出(持久形态为前台常驻)。
|
||||||
|
/// - cleanup 可能向 sidecar 记录的进程发 SIGTERM 并删除残留文件。
|
||||||
|
fn server_command(command: ServerCommands) -> ExitCode {
|
||||||
|
match command {
|
||||||
|
ServerCommands::Start { rm, idle_timeout, workers } => {
|
||||||
|
if rm {
|
||||||
|
let mut config = RmServerConfig {
|
||||||
|
idle_timeout: Duration::from_secs(idle_timeout),
|
||||||
|
..RmServerConfig::default()
|
||||||
|
};
|
||||||
|
if let Some(n) = workers {
|
||||||
|
config.worker_count = n as usize;
|
||||||
|
}
|
||||||
|
start_result(run_server_rm(&config))
|
||||||
|
} else {
|
||||||
|
let mut config = ServerConfig::well_known();
|
||||||
|
if let Some(n) = workers {
|
||||||
|
config.worker_count = n as usize;
|
||||||
|
}
|
||||||
|
start_result(run_server(&config))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ServerCommands::Cleanup => match well_known_cleanup() {
|
||||||
|
CleanupOutcome::Clean => {
|
||||||
|
println!("无残留:sidecar 与 socket 文件均不存在。");
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
}
|
||||||
|
CleanupOutcome::Removed => {
|
||||||
|
println!("残留已清理:孤儿文件已删除。");
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
}
|
||||||
|
CleanupOutcome::RefusedLiveServer => {
|
||||||
|
eprintln!("存在存活 server 且进程号无法可靠确定,未触碰文件,需人工检查。");
|
||||||
|
ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
CleanupOutcome::KillFailed => {
|
||||||
|
eprintln!("已发终止信号但进程在等待窗口内未退出,未触碰文件。");
|
||||||
|
ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ServerCommands::Stop => not_implemented("server stop"),
|
||||||
|
ServerCommands::Status => not_implemented("server status"),
|
||||||
|
ServerCommands::Health => not_implemented("server health"),
|
||||||
|
ServerCommands::Info => not_implemented("server info"),
|
||||||
|
ServerCommands::Version => not_implemented("server version"),
|
||||||
|
ServerCommands::Restart => not_implemented("server restart"),
|
||||||
|
ServerCommands::Log => not_implemented("server log"),
|
||||||
|
ServerCommands::Forge => not_implemented("server forge"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 启动结果到退出码:成功退 0,错误以红色打印到 stderr 后退 1。
|
||||||
|
fn start_result(result: Result<(), oxide_cli::server::server::ServerError>) -> ExitCode {
|
||||||
|
match result {
|
||||||
|
Ok(()) => ExitCode::SUCCESS,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{}", Red.paint(format!("server 启动失败:{err}")));
|
||||||
|
ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn make_kernel(verbose: bool, quiet: bool) -> Arc<KernelCore> {
|
fn make_kernel(verbose: bool, quiet: bool) -> Arc<KernelCore> {
|
||||||
let mut config = KernelConfig::standard();
|
let mut config = KernelConfig::standard();
|
||||||
if verbose {
|
if verbose {
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
//! server CLI 集成测试:真实二进制的分派面(help 列子命令、占位臂退出码、
|
||||||
|
//! cleanup 幂等、start 端到端)。
|
||||||
|
//!
|
||||||
|
//! start 端到端与 cleanup 走 well-known 全局路径(每用户单例):测试先探活,
|
||||||
|
//! socket 存活即 panic 不抢占存活 server;两枚触碰全局路径的测试经同一把
|
||||||
|
//! 锁串行,避免相互干扰。
|
||||||
|
|
||||||
|
use std::io::{BufReader, Write};
|
||||||
|
use std::os::unix::net::UnixStream;
|
||||||
|
use std::process::{Command, Output};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use oxide_cli::server::protocol::{self, FrameReader, ServerRequest, ServerResponse};
|
||||||
|
use oxide_cli::server::sidecar;
|
||||||
|
|
||||||
|
/// well-known 全局路径互斥锁:同测试二进制内触碰全局路径的测试串行执行。
|
||||||
|
static WELL_KNOWN_LOCK: Mutex<()> = Mutex::new(());
|
||||||
|
|
||||||
|
/// 运行 oxide 二进制并返回输出。
|
||||||
|
fn oxide(args: &[&str]) -> Output {
|
||||||
|
Command::new(env!("CARGO_BIN_EXE_oxide"))
|
||||||
|
.args(args)
|
||||||
|
.output()
|
||||||
|
.expect("failed to run oxide")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送请求帧并读回一帧响应。
|
||||||
|
fn send_and_recv(stream: &mut UnixStream, request: &ServerRequest) -> ServerResponse {
|
||||||
|
stream
|
||||||
|
.write_all(protocol::encode_request(request).as_bytes())
|
||||||
|
.expect("写请求应成功");
|
||||||
|
let mut reader = FrameReader::new(BufReader::new(stream));
|
||||||
|
let frame = reader.read_frame().expect("读响应应成功").expect("应读到响应帧");
|
||||||
|
protocol::parse_response(&frame).expect("响应帧解析应成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// help 列全部十个子命令。
|
||||||
|
#[test]
|
||||||
|
fn server_help_lists_subcommands() {
|
||||||
|
let output = oxide(&["server", "--help"]);
|
||||||
|
assert!(output.status.success(), "server --help 应退 0");
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
for name in [
|
||||||
|
"start", "stop", "status", "health", "info", "version", "cleanup", "restart", "log", "forge",
|
||||||
|
] {
|
||||||
|
assert!(stdout.contains(name), "help 应列出 {name}:{stdout}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 八个占位臂各退 2,stderr 含 not yet implemented。
|
||||||
|
#[test]
|
||||||
|
fn server_stub_subcommands_exit_2() {
|
||||||
|
for sub in ["stop", "status", "health", "info", "version", "restart", "log", "forge"] {
|
||||||
|
let output = oxide(&["server", sub]);
|
||||||
|
assert_eq!(output.status.code(), Some(2), "{sub} 应退 2");
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
assert!(stderr.contains("not yet implemented"), "{sub} 应打印未实现提示:{stderr}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// cleanup 退 0(幂等:有残留则清理、无残留则报无残留),事后全局路径无文件。
|
||||||
|
#[test]
|
||||||
|
fn server_cleanup_exit_0() {
|
||||||
|
let _guard = WELL_KNOWN_LOCK.lock().expect("全局路径锁不应中毒");
|
||||||
|
assert!(
|
||||||
|
!sidecar::is_server_alive(&sidecar::well_known_socket_path()),
|
||||||
|
"存活 server 占用 well-known 路径,测试不抢占"
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = oxide(&["server", "cleanup"]);
|
||||||
|
assert_eq!(output.status.code(), Some(0), "cleanup 应退 0");
|
||||||
|
assert!(!sidecar::well_known_socket_path().exists(), "socket 文件应不存在");
|
||||||
|
assert!(!sidecar::well_known_sidecar_path().exists(), "sidecar 文件应不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// start 端到端:起真实子进程,well-known 路径连接,版本帧断言构建期版本,
|
||||||
|
/// 关闭帧后子进程退 0,socket 与 sidecar 文件被删。
|
||||||
|
#[test]
|
||||||
|
fn server_start_e2e() {
|
||||||
|
let _guard = WELL_KNOWN_LOCK.lock().expect("全局路径锁不应中毒");
|
||||||
|
let socket = sidecar::well_known_socket_path();
|
||||||
|
let sidecar_path = sidecar::well_known_sidecar_path();
|
||||||
|
|
||||||
|
// 探活先行:存活 server 占用全局路径时 panic,不抢占。
|
||||||
|
assert!(!sidecar::is_server_alive(&socket), "存活 server 占用 well-known 路径,测试不抢占");
|
||||||
|
|
||||||
|
// worker 数压到 2:避免按宿主核数预热的成本。
|
||||||
|
let mut child = Command::new(env!("CARGO_BIN_EXE_oxide"))
|
||||||
|
.args(["server", "start", "--workers", "2"])
|
||||||
|
.spawn()
|
||||||
|
.expect("oxide server start 应可启动");
|
||||||
|
|
||||||
|
// 轮询 socket 文件出现(10 秒截止)判就绪。
|
||||||
|
let deadline = Instant::now() + Duration::from_secs(10);
|
||||||
|
while !socket.exists() {
|
||||||
|
assert!(Instant::now() < deadline, "socket 文件未出现");
|
||||||
|
std::thread::sleep(Duration::from_millis(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 版本请求帧:版本应等于构建期版本。
|
||||||
|
let mut stream = UnixStream::connect(&socket).expect("连接 server 应成功");
|
||||||
|
let response = send_and_recv(&mut stream, &ServerRequest::Version);
|
||||||
|
match response {
|
||||||
|
ServerResponse::Version { version } => {
|
||||||
|
assert_eq!(version, env!("CARGO_PKG_VERSION"), "版本应与构建期版本一致");
|
||||||
|
}
|
||||||
|
other => panic!("应得 Version 帧,实得 {other:?}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭请求帧:读回关闭确认帧。
|
||||||
|
let response = send_and_recv(&mut stream, &ServerRequest::Shutdown);
|
||||||
|
assert!(matches!(response, ServerResponse::Shutdown), "应得关闭确认帧:{response:?}");
|
||||||
|
drop(stream);
|
||||||
|
|
||||||
|
// 等子进程以退出码 0 退出(30 秒截止)。
|
||||||
|
let deadline = Instant::now() + Duration::from_secs(30);
|
||||||
|
loop {
|
||||||
|
match child.try_wait() {
|
||||||
|
Ok(Some(status)) => {
|
||||||
|
assert_eq!(status.code(), Some(0), "server 应以退出码 0 退出:{status:?}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
assert!(Instant::now() < deadline, "server 应在有界时间内退出");
|
||||||
|
std::thread::sleep(Duration::from_millis(50));
|
||||||
|
}
|
||||||
|
Err(err) => panic!("等待子进程失败:{err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(!socket.exists(), "socket 文件应被删除");
|
||||||
|
assert!(!sidecar_path.exists(), "sidecar 文件应被删除");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user