use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
fn make_temp_dir(label: &str) -> PathBuf {
static COUNTER: AtomicUsize = AtomicUsize::new(0);
let mut dir = std::env::temp_dir();
dir.push(format!(
"nishikaze-integration-{}-{}",
label,
COUNTER.fetch_add(1, Ordering::Relaxed)
));
if dir.exists() {
fs::remove_dir_all(&dir).expect("clean temp dir");
}
fs::create_dir_all(&dir).expect("create temp dir");
dir
}
fn kaze_bin() -> PathBuf {
if let Some(bin) = std::env::var_os("CARGO_BIN_EXE_kaze") {
return PathBuf::from(bin);
}
let exe = std::env::current_exe().expect("current_exe");
let deps_dir = exe.parent().expect("deps dir");
let debug_dir = deps_dir.parent().expect("debug dir");
let bin_name = if cfg!(windows) { "kaze.exe" } else { "kaze" };
let bin = debug_dir.join(bin_name);
if bin.is_file() {
return bin;
}
panic!("kaze binary not found; tried {}", bin.display());
}
fn write_kaze_toml(dir: &Path, body: &str) {
let path = dir.join("kaze.toml");
fs::write(&path, body).expect("write kaze.toml");
}
fn write_fake_tool(dir: &Path, name: &str, out: &str, err: &str, code: i32) -> PathBuf {
let path = dir.join(name);
let script = format!(
"#!/bin/sh\necho \"{}\"\necho \"{}\" 1>&2\nexit {}\n",
out, err, code
);
fs::write(&path, script).expect("write script");
let mut perms = fs::metadata(&path).expect("stat script").permissions();
perms.set_mode(0o755);
fs::set_permissions(&path, perms).expect("chmod script");
path
}
#[test]
fn kaze_init_creates_config() {
let dir = make_temp_dir("init");
let status = Command::new(kaze_bin())
.arg("--board")
.arg("native_sim")
.arg("init")
.current_dir(&dir)
.status()
.expect("run kaze init");
assert!(status.success());
assert!(dir.join("kaze.toml").is_file());
}
#[test]
fn kaze_clean_fails_without_config() {
let dir = make_temp_dir("clean-missing");
let output = Command::new(kaze_bin())
.arg("clean")
.current_dir(&dir)
.output()
.expect("run kaze clean");
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stderr.contains("project_dir discovery failed"));
}
#[test]
fn kaze_build_logs_command_and_profile_status() {
let dir = make_temp_dir("build-logs");
write_kaze_toml(
&dir,
r#"[project]
board = "native_sim"
"#,
);
let output = Command::new(kaze_bin())
.arg("-vvv")
.arg("-d")
.arg("build")
.env("KAZE_TESTING", "1")
.env("KAZE_LOGS", "1")
.current_dir(&dir)
.output()
.expect("run kaze build");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("kaze: Running command 'build'"));
assert!(stdout.contains("kaze: Profiles not configured - running in profile-less mode"));
assert!(stdout.contains("kaze: Building project"));
}
#[test]
fn kaze_quiet_suppresses_logs_and_cmd_output() {
let dir = make_temp_dir("verbosity-quiet");
let bin_dir = dir.join("bin");
fs::create_dir_all(&bin_dir).expect("create bin dir");
write_fake_tool(&bin_dir, "cmake", "CM_OUT", "CM_ERR", 0);
write_kaze_toml(
&dir,
r#"[project]
board = "native_sim"
"#,
);
let path = std::env::var("PATH").unwrap_or_default();
let output = Command::new(kaze_bin())
.arg("-v")
.arg("conf")
.env("PATH", format!("{}:{}", bin_dir.display(), path))
.current_dir(&dir)
.output()
.expect("run kaze conf");
assert!(output.status.success());
assert!(output.stdout.is_empty());
assert!(output.stderr.is_empty());
}
#[test]
fn kaze_normal_shows_logs_only_on_success() {
let dir = make_temp_dir("verbosity-normal");
let bin_dir = dir.join("bin");
fs::create_dir_all(&bin_dir).expect("create bin dir");
write_fake_tool(&bin_dir, "cmake", "CM_OUT", "CM_ERR", 0);
write_kaze_toml(
&dir,
r#"[project]
board = "native_sim"
"#,
);
let path = std::env::var("PATH").unwrap_or_default();
let output = Command::new(kaze_bin())
.arg("conf")
.env("PATH", format!("{}:{}", bin_dir.display(), path))
.current_dir(&dir)
.output()
.expect("run kaze conf");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stdout.contains("kaze:"));
assert!(stdout.contains("Configure completed"));
assert!(!stdout.contains("CM_OUT"));
assert!(!stderr.contains("CM_ERR"));
assert!(!stderr.contains("cmake"));
}
#[test]
fn kaze_verbose_always_shows_command_output() {
let dir = make_temp_dir("verbosity-verbose");
let bin_dir = dir.join("bin");
fs::create_dir_all(&bin_dir).expect("create bin dir");
write_fake_tool(&bin_dir, "cmake", "CM_OUT", "CM_ERR", 0);
write_kaze_toml(
&dir,
r#"[project]
board = "native_sim"
"#,
);
let path = std::env::var("PATH").unwrap_or_default();
let output = Command::new(kaze_bin())
.arg("-vvv")
.arg("conf")
.env("PATH", format!("{}:{}", bin_dir.display(), path))
.current_dir(&dir)
.output()
.expect("run kaze conf");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stdout.contains("kaze:"));
assert!(!stdout.contains("..."));
assert!(stdout.contains("CM_OUT"));
assert!(stderr.contains("CM_ERR"));
assert!(stderr.contains("cmake"));
}
#[test]
fn kaze_normal_shows_command_output_on_error() {
let dir = make_temp_dir("verbosity-normal-error");
let bin_dir = dir.join("bin");
fs::create_dir_all(&bin_dir).expect("create bin dir");
write_fake_tool(&bin_dir, "cmake", "CM_OUT", "CM_ERR", 1);
write_kaze_toml(
&dir,
r#"[project]
board = "native_sim"
"#,
);
let path = std::env::var("PATH").unwrap_or_default();
let output = Command::new(kaze_bin())
.arg("conf")
.env("PATH", format!("{}:{}", bin_dir.display(), path))
.current_dir(&dir)
.output()
.expect("run kaze conf");
assert!(!output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stdout.contains("CM_OUT"));
assert!(stderr.contains("CM_ERR"));
assert!(stderr.contains("cmake"));
}