/
dcr_labs
/
dcr
Обзор
Документация
Войти
/
dcr_labs
/
dcr
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
tests/cli_errors.rs
359 строк
11 KB
Dexoron
fix: make cross-target tests and vscode launch path robust for macOS + Windows
04 авг 2026, 21:21
04 авг 2026, 21:21
bc308d6
Код
Авторство
О чём код?
use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::Once; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; static COUNTER: AtomicUsize = AtomicUsize::new(0); static BUILD_ONCE: Once = Once::new(); fn bin_path() -> PathBuf { if let Ok(exe) = std::env::var("CARGO_BIN_EXE_dcr") { return PathBuf::from(exe); } ensure_bin_built(); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); path.push("target"); path.push("debug"); path.push(format!("dcr{}", std::env::consts::EXE_SUFFIX)); path } fn unique_sandbox_dir(prefix: &str) -> PathBuf { let pid = std::process::id(); let n = COUNTER.fetch_add(1, Ordering::SeqCst); let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_nanos(); let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); path.push("sandbox"); path.push("cli-tests"); path.push(format!("dcr_{prefix}_{pid}_{n}_{now}")); std::fs::create_dir_all(&path).expect("failed to create temp dir"); path } fn run_dcr(args: &[&str], cwd: &Path) -> std::process::Output { let mut cmd = Command::new(bin_path()); cmd.args(args).current_dir(cwd); cmd.output().expect("failed to run dcr") } fn run_dcr_env(args: &[&str], cwd: &Path, envs: &[(&str, &str)]) -> std::process::Output { let mut cmd = Command::new(bin_path()); cmd.args(args).current_dir(cwd); for (k, v) in envs { cmd.env(k, v); } cmd.output().expect("failed to run dcr") } fn ensure_bin_built() { BUILD_ONCE.call_once(|| { let status = Command::new("cargo") .arg("build") .current_dir(env!("CARGO_MANIFEST_DIR")) .status() .expect("failed to run cargo build"); assert!(status.success(), "cargo build failed"); }); } fn available_compiler() -> Option<&'static str> { for candidate in ["clang", "gcc", "cc"] { let ok = Command::new(candidate) .arg("--version") .output() .map(|o| o.status.success()) .unwrap_or(false); if ok { return Some(candidate); } } None } fn host_profile_dir(project_root: &Path, profile: &str) -> PathBuf { let target = project_root.join("target"); if cfg!(any( target_os = "linux", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly" )) { let arch = std::env::consts::ARCH; let triple = if cfg!(target_os = "linux") { let env = if cfg!(target_env = "musl") { "musl" } else { "gnu" }; format!("{arch}-unknown-linux-{env}") } else { format!("{arch}-unknown-{}", std::env::consts::OS) }; target.join(triple).join(profile) } else { target.join(profile) } } // --- Tests --- #[test] fn no_args_shows_help() { let dir = unique_sandbox_dir("noargs"); let out = run_dcr(&[], &dir); // Should succeed (help returns 0) and produce some output let stdout = String::from_utf8_lossy(&out.stdout); assert!( stdout.contains("DCR") || stdout.contains("dcr") || stdout.contains("USAGE"), "no args should show help text" ); } #[test] fn unknown_command_fails() { let dir = unique_sandbox_dir("unknown_cmd"); let out = run_dcr(&["foobar"], &dir); assert!( !out.status.success(), "unknown command should exit non-zero" ); let combined = format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); assert!( combined.contains("error") || combined.contains("Unknown"), "unknown command should print error" ); } #[test] fn build_without_toml_fails() { let dir = unique_sandbox_dir("build_no_toml"); let out = run_dcr(&["build"], &dir); assert!(!out.status.success(), "build without dcr.toml should fail"); let combined = format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); assert!( combined.contains("dcr.toml") || combined.contains("not found"), "should mention missing dcr.toml" ); } #[test] fn run_without_toml_fails() { let dir = unique_sandbox_dir("run_no_toml"); let out = run_dcr(&["run"], &dir); assert!(!out.status.success(), "run without dcr.toml should fail"); } #[test] fn clean_without_toml_fails() { let dir = unique_sandbox_dir("clean_no_toml"); let out = run_dcr(&["clean"], &dir); assert!(!out.status.success(), "clean without dcr.toml should fail"); } #[test] fn new_existing_dir_fails() { let dir = unique_sandbox_dir("new_exist"); // Create the project directory first std::fs::create_dir_all(dir.join("hello")).expect("failed to create dir"); let out = run_dcr(&["new", "hello"], &dir); assert!(!out.status.success(), "dcr new on existing dir should fail"); let combined = format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); assert!( combined.contains("already exists") || combined.contains("error"), "should report dir already exists" ); } #[test] fn init_nonempty_dir_fails() { let dir = unique_sandbox_dir("init_nonempty"); // Put a file in the dir so it's not empty std::fs::write(dir.join("dummy.txt"), "x").expect("failed to write"); let out = run_dcr(&["init"], &dir); assert!( !out.status.success(), "dcr init in non-empty dir should fail" ); let combined = format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); assert!( combined.contains("not empty") || combined.contains("error"), "should report dir not empty" ); } #[test] fn build_unknown_flag_fails() { let dir = unique_sandbox_dir("build_badflag"); // Init a valid project first let out = run_dcr(&["init"], &dir); assert!(out.status.success(), "init should succeed"); let out = run_dcr(&["build", "--foobar"], &dir); assert!(!out.status.success(), "build with unknown flag should fail"); } #[test] fn run_library_project_fails() { let Some(compiler) = available_compiler() else { eprintln!("no compiler found; skipping run_library test"); return; }; let dir = unique_sandbox_dir("run_lib"); let out = run_dcr(&["init"], &dir); assert!(out.status.success(), "init should succeed"); // Edit dcr.toml to set kind = "staticlib" let toml_path = dir.join("dcr.toml"); let toml = std::fs::read_to_string(&toml_path).expect("failed to read dcr.toml"); let updated = toml.replace("kind = \"bin\"", "kind = \"staticlib\""); std::fs::write(&toml_path, updated).expect("failed to write dcr.toml"); let envs = [("DCR_COMPILER", compiler)]; let out = run_dcr_env(&["run"], &dir, &envs); assert!(!out.status.success(), "dcr run on staticlib should fail"); let combined = format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); assert!( combined.contains("library") || combined.contains("Cannot run"), "should report cannot run library" ); } #[test] fn clean_specific_profile() { let dir = unique_sandbox_dir("clean_profile"); let out = run_dcr(&["init"], &dir); assert!(out.status.success(), "init should succeed"); let debug_dir = host_profile_dir(&dir, "debug"); let release_dir = host_profile_dir(&dir, "release"); std::fs::create_dir_all(&debug_dir).expect("create debug"); std::fs::write(debug_dir.join("dummy.o"), "x").expect("write"); std::fs::create_dir_all(&release_dir).expect("create release"); std::fs::write(release_dir.join("dummy.o"), "x").expect("write"); let out = run_dcr(&["clean", "--release"], &dir); assert!(out.status.success(), "clean --release should succeed"); assert!( !release_dir.exists(), "release profile dir should be removed: {}", release_dir.display() ); assert!( debug_dir.is_dir(), "debug profile dir should remain: {}", debug_dir.display() ); } #[test] fn new_no_name_fails() { let dir = unique_sandbox_dir("new_noname"); let out = run_dcr(&["new"], &dir); assert!(!out.status.success(), "dcr new without name should fail"); let combined = format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); assert!( combined.contains("not specified") || combined.contains("error"), "should report name not specified" ); } #[test] fn staticlib_build() { let Some(compiler) = available_compiler() else { eprintln!("no compiler found; skipping staticlib test"); return; }; let dir = unique_sandbox_dir("staticlib"); let out = run_dcr(&["init"], &dir); assert!(out.status.success(), "init should succeed"); let toml_path = dir.join("dcr.toml"); let toml = std::fs::read_to_string(&toml_path).expect("read dcr.toml"); let updated = toml.replace("kind = \"bin\"", "kind = \"staticlib\""); std::fs::write(&toml_path, updated).expect("write dcr.toml"); let envs = [("DCR_COMPILER", compiler)]; let out = run_dcr_env(&["build"], &dir, &envs); assert!(out.status.success(), "staticlib build should succeed"); let lib_path = host_profile_dir(&dir, "debug"); let has_lib = std::fs::read_dir(&lib_path) .map(|entries| { entries.filter_map(|e| e.ok()).any(|e| { let name = e.file_name().to_string_lossy().to_string(); name.ends_with(".a") || name.ends_with(".lib") }) }) .unwrap_or(false); assert!( has_lib, "staticlib should produce a .a/.lib in {}", lib_path.display() ); } #[test] fn build_release_profile() { let Some(compiler) = available_compiler() else { eprintln!("no compiler found; skipping release build test"); return; }; let dir = unique_sandbox_dir("release_build"); let out = run_dcr(&["init"], &dir); assert!(out.status.success(), "init should succeed"); let envs = [("DCR_COMPILER", compiler)]; let out = run_dcr_env(&["build", "--release"], &dir, &envs); assert!(out.status.success(), "release build should succeed"); let release_dir = host_profile_dir(&dir, "release"); assert!( release_dir.is_dir(), "release profile dir should exist: {}", release_dir.display() ); } #[test] fn version_contains_version_string() { let dir = unique_sandbox_dir("version"); let out = run_dcr(&["--version"], &dir); assert!(out.status.success()); let stdout = String::from_utf8_lossy(&out.stdout); // Should contain version like "dcr 0.2.8 (target)" assert!( stdout.contains("dcr "), "version output should start with 'dcr '" ); assert!( stdout.contains('.'), "version output should contain a version number" ); }