#![cfg(all(feature = "cli", feature = "test-support", feature = "unix-runtime"))]
mod support;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use support::{run_shell, shell_quote, temp_path};
fn assert_success(output: &std::process::Output) {
assert!(
output.status.success(),
"mxsh failed with status {:?}\nstdout:\n{}\nstderr:\n{}",
output.status.code(),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
fn write_executable(dir: &Path, name: &str, output: &str) -> PathBuf {
fs::create_dir_all(dir).expect("temp command dir should be creatable");
let path = dir.join(name);
fs::write(
&path,
format!("#!/bin/sh\nprintf '%s\\n' {}\n", shell_quote(output)),
)
.expect("temp command should be writable");
let mut perms = fs::metadata(&path)
.expect("temp command metadata should be readable")
.permissions();
perms.set_mode(0o755);
fs::set_permissions(&path, perms).expect("temp command should be executable");
path
}
#[test]
fn temporary_path_assignment_is_used_for_command_lookup() {
let dir = temp_path("path-assignment-lookup");
write_executable(&dir, "foo", "ok");
let dir_arg = dir.to_str().expect("temp path should be utf8");
let output = run_shell("mxsh", &["-c", "PATH=\"$1\" foo", "mxsh", dir_arg], "");
assert_success(&output);
assert_eq!(String::from_utf8_lossy(&output.stdout), "ok\n");
fs::remove_dir_all(&dir).expect("temp dir should be removable");
}
#[test]
fn repeated_temporary_path_assignments_use_last_value_then_restore_original() {
let original_dir = temp_path("path-assignment-original");
let stale_dir = temp_path("path-assignment-stale");
let replacement_dir = temp_path("path-assignment-replacement");
write_executable(&original_dir, "foo", "original");
write_executable(&stale_dir, "foo", "stale");
write_executable(&replacement_dir, "foo", "replacement");
let script = format!(
"PATH={}; PATH={} PATH={} foo; foo",
shell_quote(original_dir.to_str().expect("temp path should be utf8")),
shell_quote(stale_dir.to_str().expect("temp path should be utf8")),
shell_quote(replacement_dir.to_str().expect("temp path should be utf8")),
);
let output = run_shell("mxsh", &["-c", &script], "");
assert_success(&output);
assert_eq!(
String::from_utf8_lossy(&output.stdout),
"replacement\noriginal\n"
);
fs::remove_dir_all(&original_dir).expect("temp dir should be removable");
fs::remove_dir_all(&stale_dir).expect("temp dir should be removable");
fs::remove_dir_all(&replacement_dir).expect("temp dir should be removable");
}
#[test]
fn path_assignment_expansion_runs_once_when_lookup_uses_it() {
let dir = temp_path("path-assignment-command-substitution");
write_executable(&dir, "foo", "ok");
let marker = dir.join("marker");
let script = format!(
"PATH=$(printf '%s' {}; printf x >> {}) foo; /bin/cat {}",
shell_quote(dir.to_str().expect("temp path should be utf8")),
shell_quote(marker.to_str().expect("temp path should be utf8")),
shell_quote(marker.to_str().expect("temp path should be utf8")),
);
let output = run_shell("mxsh", &["-c", &script], "");
assert_success(&output);
assert_eq!(String::from_utf8_lossy(&output.stdout), "ok\nx");
fs::remove_dir_all(&dir).expect("temp dir should be removable");
}
#[test]
fn explicit_relative_command_uses_shell_cwd_after_cd() {
let dir = temp_path("explicit-relative-shell-cwd");
write_executable(&dir, "foo", "ok");
let script = format!(
"cd {}; ./foo",
shell_quote(dir.to_str().expect("temp path should be utf8"))
);
let output = run_shell("mxsh", &["-c", &script], "");
assert_success(&output);
assert_eq!(String::from_utf8_lossy(&output.stdout), "ok\n");
fs::remove_dir_all(&dir).expect("temp dir should be removable");
}
#[test]
fn relative_path_entry_uses_shell_cwd_after_cd() {
let dir = temp_path("relative-path-entry-shell-cwd");
write_executable(&dir.join("bin"), "foo", "ok");
let script = format!(
"cd {}; PATH=bin:/usr/bin:/bin foo",
shell_quote(dir.to_str().expect("temp path should be utf8"))
);
let output = run_shell("mxsh", &["-c", &script], "");
assert_success(&output);
assert_eq!(String::from_utf8_lossy(&output.stdout), "ok\n");
fs::remove_dir_all(&dir).expect("temp dir should be removable");
}
#[test]
fn glob_expansion_uses_shell_cwd_after_cd() {
let dir = temp_path("glob-shell-cwd-[literal]");
fs::create_dir_all(&dir).expect("temp dir should be creatable");
fs::write(dir.join("a"), "").expect("glob fixture should be writable");
let script = format!(
"cd {}; printf '<%s>\\n' *",
shell_quote(dir.to_str().expect("temp path should be utf8"))
);
let output = run_shell("mxsh", &["-c", &script], "");
assert_success(&output);
assert_eq!(String::from_utf8_lossy(&output.stdout), "<a>\n");
fs::remove_dir_all(&dir).expect("temp dir should be removable");
}