use std::{ env, ffi::{OsStr, OsString}, fs, path::{Path, PathBuf}, process::{Command, Output}, sync::atomic::{AtomicUsize, Ordering}, }; static NEXT_TEMP_ID: AtomicUsize = AtomicUsize::new(0); const EXPECTED_OUTPUT: &str = "test \"standard library path list discovery\" ... ok\n1 test(s) passed\n"; #[test] fn slovo_std_path_accepts_os_path_list() { let root = temp_root(); let first_std = root.join("first-empty-std"); let second_std = root.join("second-std"); let project = root.join("project"); let source_dir = project.join("src"); create_dir(&first_std); create_dir(&second_std); create_dir(&source_dir); write( &second_std.join("path_list_probe.slo"), "(module path_list_probe (export value))\n\n(fn value () -> i32\n 51)\n", ); write( &project.join("slovo.toml"), concat!( "[project]\n", "name = \"standard-library-path-list-alpha\"\n", "source_root = \"src\"\n", "entry = \"main\"\n", ), ); write( &source_dir.join("main.slo"), concat!( "(module main)\n\n", "(import std.path_list_probe (value))\n\n", "(fn main () -> i32\n", " (value))\n\n", "(test \"standard library path list discovery\"\n", " (= (value) 51))\n", ), ); let std_path = env::join_paths([first_std.as_os_str(), second_std.as_os_str()]) .expect("join std path list"); let check = run_glagol([OsStr::new("check"), project.as_os_str()], &root, &std_path); assert_success_stdout(check, "", "standard library path list check"); let test = run_glagol([OsStr::new("test"), project.as_os_str()], &root, &std_path); assert_success_stdout(test, EXPECTED_OUTPUT, "standard library path list test"); } fn run_glagol(args: I, cwd: &Path, std_path: &OsString) -> Output where I: IntoIterator, S: AsRef, { Command::new(env!("CARGO_BIN_EXE_glagol")) .args(args) .current_dir(cwd) .env("SLOVO_STD_PATH", std_path) .output() .expect("run 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)); } fn temp_root() -> PathBuf { let path = env::temp_dir().join(format!( "glagol-standard-library-path-list-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); }