use std::{ env, ffi::OsStr, fs, path::Path, process::{Command, Output}, sync::atomic::{AtomicUsize, Ordering}, }; static NEXT_TEMP_ID: AtomicUsize = AtomicUsize::new(0); const EXPECTED_OUTPUT: &str = "test \"installed stdlib source discovery\" ... ok\n1 test(s) passed\n"; #[test] fn copied_binary_discovers_installed_share_slovo_std() { let root = temp_root(); let bin_dir = root.join("bin"); let std_dir = root.join("share/slovo/std"); let project = root.join("project"); let source_dir = project.join("src"); let installed_glagol = bin_dir.join("glagol"); create_dir(&bin_dir); create_dir(&std_dir); create_dir(&source_dir); fs::copy(env!("CARGO_BIN_EXE_glagol"), &installed_glagol).unwrap_or_else(|err| { panic!( "copy glagol binary to `{}` failed: {}", installed_glagol.display(), err ) }); make_executable(&installed_glagol); write( &std_dir.join("installed_probe.slo"), "(module installed_probe (export value))\n\n(fn value () -> i32\n 42)\n", ); write( &project.join("slovo.toml"), concat!( "[project]\n", "name = \"installed-stdlib-discovery-alpha\"\n", "source_root = \"src\"\n", "entry = \"main\"\n", ), ); write( &source_dir.join("main.slo"), concat!( "(module main)\n\n", "(import std.installed_probe (value))\n\n", "(fn main () -> i32\n", " (value))\n\n", "(test \"installed stdlib source discovery\"\n", " (= (value) 42))\n", ), ); assert!( !project.join("src/installed_probe.slo").exists(), "fixture must not carry a local installed_probe module copy" ); let check = run_installed_glagol( &installed_glagol, [OsStr::new("check"), project.as_os_str()], &root, ); assert_success_stdout(check, "", "installed stdlib discovery check"); let test = run_installed_glagol( &installed_glagol, [OsStr::new("test"), project.as_os_str()], &root, ); assert_success_stdout(test, EXPECTED_OUTPUT, "installed stdlib discovery test"); } fn run_installed_glagol(binary: &Path, args: I, cwd: &Path) -> Output where I: IntoIterator, S: AsRef, { Command::new(binary) .args(args) .current_dir(cwd) .env_remove("SLOVO_STD_PATH") .output() .expect("run installed glagol") } fn write(path: &Path, content: &str) { fs::write(path, content).unwrap_or_else(|err| panic!("write `{}`: {}", path.display(), err)); } fn create_dir(path: &Path) { fs::create_dir_all(path).unwrap_or_else(|err| panic!("create `{}`: {}", path.display(), err)); } #[cfg(unix)] fn make_executable(path: &Path) { use std::os::unix::fs::PermissionsExt; let mut permissions = fs::metadata(path) .unwrap_or_else(|err| panic!("metadata `{}`: {}", path.display(), err)) .permissions(); permissions.set_mode(permissions.mode() | 0o755); fs::set_permissions(path, permissions) .unwrap_or_else(|err| panic!("chmod `{}`: {}", path.display(), err)); } #[cfg(not(unix))] fn make_executable(_path: &Path) {} fn temp_root() -> std::path::PathBuf { let path = env::temp_dir().join(format!( "glagol-installed-stdlib-discovery-alpha-{}-{}", std::process::id(), NEXT_TEMP_ID.fetch_add(1, Ordering::Relaxed) )); let _ = fs::remove_dir_all(&path); fs::create_dir_all(&path).unwrap_or_else(|err| panic!("create `{}`: {}", path.display(), err)); path } fn assert_success_stdout(output: Output, expected: &str, context: &str) { let stdout = String::from_utf8_lossy(&output.stdout); let stderr = String::from_utf8_lossy(&output.stderr); assert!( output.status.success(), "{} failed\nstatus: {:?}\nstdout:\n{}\nstderr:\n{}", context, output.status.code(), stdout, stderr ); assert_eq!(stdout, expected, "{}", context); assert!(stderr.is_empty(), "{} wrote stderr:\n{}", context, stderr); }