2026-05-21 19:48:02 +00:00
|
|
|
use std::process::{Command, ExitCode};
|
2026-02-15 17:03:31 +00:00
|
|
|
use std::time::Instant;
|
|
|
|
|
|
2026-02-16 15:39:27 +00:00
|
|
|
use clap::{Parser, Subcommand};
|
|
|
|
|
|
|
|
|
|
// ── CLI ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
#[derive(Parser)]
|
|
|
|
|
#[command(name = "xtask", about = "Development task runner")]
|
|
|
|
|
struct Cli {
|
|
|
|
|
#[command(subcommand)]
|
2026-02-24 09:12:28 +00:00
|
|
|
command: Option<Cmd>,
|
|
|
|
|
|
|
|
|
|
/// (Legacy) Test group to run directly without subcommand
|
|
|
|
|
#[arg(hide = true)]
|
|
|
|
|
group: Option<String>,
|
|
|
|
|
|
|
|
|
|
/// Show all groups and the cargo commands they run
|
|
|
|
|
#[arg(long)]
|
|
|
|
|
list: bool,
|
2026-02-16 15:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Subcommand)]
|
|
|
|
|
enum Cmd {
|
2026-06-23 15:42:28 +00:00
|
|
|
/// Run a local swactor node with distribution on localhost
|
2026-02-16 15:39:27 +00:00
|
|
|
Node {
|
2026-02-24 09:12:28 +00:00
|
|
|
/// Dashboard HTTP port
|
|
|
|
|
#[arg(long, default_value = "9090")]
|
|
|
|
|
dashboard_port: u16,
|
2026-02-16 15:39:27 +00:00
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
/// Pass extra args to the swactor binary
|
|
|
|
|
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
|
|
|
|
args: Vec<String>,
|
2026-02-16 15:39:27 +00:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
// ── Test infrastructure ─────────────────────────────────────────────
|
2026-02-16 15:39:27 +00:00
|
|
|
|
2026-02-15 17:03:31 +00:00
|
|
|
struct TestStep {
|
|
|
|
|
label: &'static str,
|
|
|
|
|
args: &'static [&'static str],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct Group {
|
|
|
|
|
name: &'static str,
|
|
|
|
|
description: &'static str,
|
|
|
|
|
steps: &'static [TestStep],
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CORE: Group = Group {
|
|
|
|
|
name: "core",
|
|
|
|
|
description: "Actor runtime, message delivery, property tests",
|
|
|
|
|
steps: &[TestStep {
|
|
|
|
|
label: "actor runtime",
|
|
|
|
|
args: &["test", "-p", "swactor", "--features", "transport"],
|
|
|
|
|
}],
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-23 15:42:28 +00:00
|
|
|
const GROUPS: &[&Group] = &[&CORE];
|
2026-02-24 09:12:28 +00:00
|
|
|
|
2026-02-15 17:03:31 +00:00
|
|
|
fn groups_for(name: &str) -> Option<Vec<&'static Group>> {
|
|
|
|
|
match name {
|
2026-06-23 15:42:28 +00:00
|
|
|
"core" | "essential" | "all" => Some(vec![&CORE]),
|
2026-02-15 17:03:31 +00:00
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn run_step(group_name: &str, step: &TestStep) -> bool {
|
|
|
|
|
println!("\n=== {group_name}: {} ===", step.label);
|
|
|
|
|
println!(" cargo {}", step.args.join(" "));
|
|
|
|
|
println!();
|
|
|
|
|
|
2026-05-21 19:48:02 +00:00
|
|
|
let status = Command::new(cargo_bin()).args(step.args).status();
|
2026-02-15 17:03:31 +00:00
|
|
|
|
|
|
|
|
match status {
|
|
|
|
|
Ok(s) => s.success(),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Failed to execute cargo: {e}");
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn print_usage() {
|
|
|
|
|
println!(
|
|
|
|
|
"\
|
2026-02-24 09:12:28 +00:00
|
|
|
USAGE: cargo xtask <COMMAND|GROUP>
|
|
|
|
|
|
|
|
|
|
COMMANDS:
|
2026-06-23 15:42:28 +00:00
|
|
|
node Run a local swactor node (distribution, localhost)
|
2026-02-24 09:12:28 +00:00
|
|
|
|
|
|
|
|
TEST GROUPS:
|
|
|
|
|
core Actor runtime, message delivery, property tests
|
2026-06-23 15:42:28 +00:00
|
|
|
essential core
|
|
|
|
|
all core
|
2026-02-15 17:03:31 +00:00
|
|
|
|
2026-02-23 04:53:04 +00:00
|
|
|
FLAGS:
|
2026-02-24 09:12:28 +00:00
|
|
|
--list Show all groups and the cargo commands they run"
|
2026-02-15 17:03:31 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn print_list() {
|
|
|
|
|
println!("Available test groups:\n");
|
|
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
for group in GROUPS {
|
|
|
|
|
println!(" {:<22}{}", group.name, group.description);
|
2026-02-15 17:03:31 +00:00
|
|
|
for step in group.steps {
|
2026-02-24 09:12:28 +00:00
|
|
|
println!(" → cargo {}", step.args.join(" "));
|
2026-02-15 17:03:31 +00:00
|
|
|
}
|
|
|
|
|
println!();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-23 15:42:28 +00:00
|
|
|
println!(" {:<22}core", "essential");
|
2026-02-24 09:12:28 +00:00
|
|
|
println!(" {:<22}Every test group", "all");
|
2026-02-15 17:03:31 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
// ── Node runner ─────────────────────────────────────────────────────
|
2026-02-15 17:03:31 +00:00
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
fn run_node(dashboard_port: u16, extra_args: &[String]) {
|
|
|
|
|
eprintln!("Building swactor node...");
|
2026-05-21 19:48:02 +00:00
|
|
|
let build = Command::new(cargo_bin())
|
2026-02-24 09:12:28 +00:00
|
|
|
.args(["build", "-p", "node"])
|
|
|
|
|
.status();
|
|
|
|
|
|
|
|
|
|
match build {
|
|
|
|
|
Ok(s) if s.success() => {}
|
|
|
|
|
Ok(s) => {
|
|
|
|
|
eprintln!("Build failed");
|
|
|
|
|
std::process::exit(s.code().unwrap_or(1));
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Failed to execute cargo build: {e}");
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
2026-02-15 17:03:31 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
let tmp = std::env::temp_dir().join("swactor-dev-node");
|
|
|
|
|
std::fs::create_dir_all(&tmp).expect("failed to create temp dir");
|
|
|
|
|
|
|
|
|
|
let identity_dir = tmp.join("identity");
|
|
|
|
|
let auth_dir = tmp.join("auth");
|
|
|
|
|
|
|
|
|
|
eprintln!();
|
|
|
|
|
|
|
|
|
|
let mut cmd = Command::new("target/debug/swactor");
|
|
|
|
|
cmd.args([
|
2026-05-21 19:48:02 +00:00
|
|
|
"--transport",
|
|
|
|
|
"iroh",
|
|
|
|
|
"--dashboard-port",
|
|
|
|
|
&dashboard_port.to_string(),
|
|
|
|
|
"--identity-dir",
|
|
|
|
|
&identity_dir.to_string_lossy(),
|
|
|
|
|
"--auth-dir",
|
|
|
|
|
&auth_dir.to_string_lossy(),
|
2026-02-24 09:12:28 +00:00
|
|
|
"--no-relay",
|
|
|
|
|
]);
|
|
|
|
|
cmd.env("HOME", tmp.to_string_lossy().as_ref());
|
|
|
|
|
cmd.args(extra_args);
|
|
|
|
|
|
|
|
|
|
let status = cmd.status();
|
|
|
|
|
match status {
|
|
|
|
|
Ok(s) => std::process::exit(s.code().unwrap_or(0)),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("Failed to execute swactor: {e}");
|
2026-02-16 15:39:27 +00:00
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
2026-02-24 09:12:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-16 15:39:27 +00:00
|
|
|
|
2026-05-21 19:48:02 +00:00
|
|
|
fn cargo_bin() -> String {
|
|
|
|
|
option_env!("CARGO")
|
|
|
|
|
.map(str::to_string)
|
|
|
|
|
.unwrap_or_else(|| "cargo".to_string())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
// ── Dispatch ────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
fn run_test_groups(group_name: &str) {
|
|
|
|
|
let groups = match groups_for(group_name) {
|
2026-02-16 15:39:27 +00:00
|
|
|
Some(g) => g,
|
|
|
|
|
None => {
|
2026-02-24 09:12:28 +00:00
|
|
|
eprintln!("Unknown command or test group: {group_name}\n");
|
2026-02-15 17:03:31 +00:00
|
|
|
print_usage();
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
let mut passed = 0usize;
|
|
|
|
|
let mut failed = 0usize;
|
|
|
|
|
|
|
|
|
|
for group in &groups {
|
|
|
|
|
for step in group.steps {
|
|
|
|
|
if run_step(group.name, step) {
|
|
|
|
|
passed += 1;
|
|
|
|
|
} else {
|
|
|
|
|
failed += 1;
|
|
|
|
|
let elapsed = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"\n--- FAILED after {:.1}s ({passed} passed, {failed} failed) ---",
|
|
|
|
|
elapsed.as_secs_f64()
|
|
|
|
|
);
|
|
|
|
|
std::process::exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let elapsed = start.elapsed();
|
|
|
|
|
println!(
|
|
|
|
|
"\n--- All {passed} step(s) passed in {:.1}s ---",
|
|
|
|
|
elapsed.as_secs_f64()
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-02-16 15:39:27 +00:00
|
|
|
|
2026-05-21 19:48:02 +00:00
|
|
|
fn main() -> ExitCode {
|
2026-02-24 09:12:28 +00:00
|
|
|
let cli = Cli::parse();
|
2026-02-16 15:39:27 +00:00
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
if cli.list {
|
|
|
|
|
print_list();
|
2026-05-21 19:48:02 +00:00
|
|
|
return ExitCode::SUCCESS;
|
2026-02-16 15:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
if let Some(cmd) = cli.command {
|
2026-05-21 19:48:02 +00:00
|
|
|
return match cmd {
|
|
|
|
|
Cmd::Node {
|
|
|
|
|
dashboard_port,
|
|
|
|
|
args,
|
|
|
|
|
} => {
|
|
|
|
|
run_node(dashboard_port, &args);
|
|
|
|
|
ExitCode::SUCCESS
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-02-16 15:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-24 09:12:28 +00:00
|
|
|
match cli.group {
|
2026-05-21 19:48:02 +00:00
|
|
|
Some(name) => {
|
|
|
|
|
run_test_groups(&name);
|
|
|
|
|
ExitCode::SUCCESS
|
|
|
|
|
}
|
2026-02-24 09:12:28 +00:00
|
|
|
None => {
|
|
|
|
|
print_usage();
|
2026-05-21 19:48:02 +00:00
|
|
|
ExitCode::from(1)
|
2026-02-16 15:39:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|