mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 07:12:44 +00:00
43e9fb73b1
Simplify the logic of computing the file path parameters (the directory, prefix, suffix, and number of random characters) for the temporary file created by `mktemp`. This commits adds an `Options` struct as a layer of indirection between the application logic and `clap`, and a `Params` struct whose associated function is responsible for determining the file path parameters from the `Options`. This is an improvement because the previous code had some logic for determining file path parameters in one place and some in another place.
574 lines
13 KiB
Rust
574 lines
13 KiB
Rust
// spell-checker:ignore (words) gpghome
|
|
|
|
use crate::common::util::*;
|
|
|
|
use uucore::display::Quotable;
|
|
|
|
use std::path::PathBuf;
|
|
use tempfile::tempdir;
|
|
|
|
#[cfg(unix)]
|
|
use std::os::unix::fs::PermissionsExt;
|
|
|
|
static TEST_TEMPLATE1: &str = "tempXXXXXX";
|
|
static TEST_TEMPLATE2: &str = "temp";
|
|
static TEST_TEMPLATE3: &str = "tempX";
|
|
static TEST_TEMPLATE4: &str = "tempXX";
|
|
static TEST_TEMPLATE5: &str = "tempXXX";
|
|
static TEST_TEMPLATE6: &str = "tempXXXlate"; // spell-checker:disable-line
|
|
static TEST_TEMPLATE7: &str = "XXXtemplate"; // spell-checker:disable-line
|
|
#[cfg(unix)]
|
|
static TEST_TEMPLATE8: &str = "tempXXXl/ate";
|
|
#[cfg(windows)]
|
|
static TEST_TEMPLATE8: &str = "tempXXXl\\ate";
|
|
|
|
#[cfg(not(windows))]
|
|
const TMPDIR: &str = "TMPDIR";
|
|
#[cfg(windows)]
|
|
const TMPDIR: &str = "TMP";
|
|
|
|
#[test]
|
|
fn test_mktemp_mktemp() {
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let pathname = scene.fixtures.as_string();
|
|
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg(TEST_TEMPLATE1)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg(TEST_TEMPLATE2)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg(TEST_TEMPLATE3)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg(TEST_TEMPLATE4)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg(TEST_TEMPLATE5)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg(TEST_TEMPLATE6)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg(TEST_TEMPLATE7)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg(TEST_TEMPLATE8)
|
|
.fails();
|
|
}
|
|
|
|
#[test]
|
|
fn test_mktemp_mktemp_t() {
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let pathname = scene.fixtures.as_string();
|
|
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-t")
|
|
.arg(TEST_TEMPLATE1)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-t")
|
|
.arg(TEST_TEMPLATE2)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-t")
|
|
.arg(TEST_TEMPLATE3)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-t")
|
|
.arg(TEST_TEMPLATE4)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-t")
|
|
.arg(TEST_TEMPLATE5)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-t")
|
|
.arg(TEST_TEMPLATE6)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-t")
|
|
.arg(TEST_TEMPLATE7)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-t")
|
|
.arg(TEST_TEMPLATE8)
|
|
.fails()
|
|
.no_stdout()
|
|
.stderr_contains("invalid suffix")
|
|
.stderr_contains("contains directory separator");
|
|
}
|
|
|
|
#[test]
|
|
fn test_mktemp_make_temp_dir() {
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let pathname = scene.fixtures.as_string();
|
|
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-d")
|
|
.arg(TEST_TEMPLATE1)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-d")
|
|
.arg(TEST_TEMPLATE2)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-d")
|
|
.arg(TEST_TEMPLATE3)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-d")
|
|
.arg(TEST_TEMPLATE4)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-d")
|
|
.arg(TEST_TEMPLATE5)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-d")
|
|
.arg(TEST_TEMPLATE6)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-d")
|
|
.arg(TEST_TEMPLATE7)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-d")
|
|
.arg(TEST_TEMPLATE8)
|
|
.fails();
|
|
}
|
|
|
|
#[test]
|
|
fn test_mktemp_dry_run() {
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let pathname = scene.fixtures.as_string();
|
|
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-u")
|
|
.arg(TEST_TEMPLATE1)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-u")
|
|
.arg(TEST_TEMPLATE2)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-u")
|
|
.arg(TEST_TEMPLATE3)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-u")
|
|
.arg(TEST_TEMPLATE4)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-u")
|
|
.arg(TEST_TEMPLATE5)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-u")
|
|
.arg(TEST_TEMPLATE6)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-u")
|
|
.arg(TEST_TEMPLATE7)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("-u")
|
|
.arg(TEST_TEMPLATE8)
|
|
.fails();
|
|
}
|
|
|
|
#[test]
|
|
fn test_mktemp_quiet() {
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg("/definitely/not/exist/I/promise")
|
|
.arg("-q")
|
|
.fails()
|
|
.no_stdout()
|
|
.no_stderr();
|
|
scene
|
|
.ucmd()
|
|
.arg("-d")
|
|
.arg("-p")
|
|
.arg("/definitely/not/exist/I/promise")
|
|
.arg("-q")
|
|
.fails()
|
|
.no_stdout()
|
|
.no_stderr();
|
|
}
|
|
|
|
#[test]
|
|
fn test_mktemp_suffix() {
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let pathname = scene.fixtures.as_string();
|
|
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("--suffix")
|
|
.arg("suf")
|
|
.arg(TEST_TEMPLATE1)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("--suffix")
|
|
.arg("suf")
|
|
.arg(TEST_TEMPLATE2)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("--suffix")
|
|
.arg("suf")
|
|
.arg(TEST_TEMPLATE3)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("--suffix")
|
|
.arg("suf")
|
|
.arg(TEST_TEMPLATE4)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("--suffix")
|
|
.arg("suf")
|
|
.arg(TEST_TEMPLATE5)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("--suffix")
|
|
.arg("suf")
|
|
.arg(TEST_TEMPLATE6)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("--suffix")
|
|
.arg("suf")
|
|
.arg(TEST_TEMPLATE7)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.env(TMPDIR, &pathname)
|
|
.arg("--suffix")
|
|
.arg("suf")
|
|
.arg(TEST_TEMPLATE8)
|
|
.fails();
|
|
}
|
|
|
|
#[test]
|
|
fn test_mktemp_tmpdir() {
|
|
let scene = TestScenario::new(util_name!());
|
|
let dir = tempdir().unwrap();
|
|
let path = dir.path().join(scene.fixtures.as_string());
|
|
let pathname = path.as_os_str();
|
|
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg(pathname)
|
|
.arg(TEST_TEMPLATE1)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg(pathname)
|
|
.arg(TEST_TEMPLATE2)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg(pathname)
|
|
.arg(TEST_TEMPLATE3)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg(pathname)
|
|
.arg(TEST_TEMPLATE4)
|
|
.fails();
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg(pathname)
|
|
.arg(TEST_TEMPLATE5)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg(pathname)
|
|
.arg(TEST_TEMPLATE6)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg(pathname)
|
|
.arg(TEST_TEMPLATE7)
|
|
.succeeds();
|
|
scene
|
|
.ucmd()
|
|
.arg("-p")
|
|
.arg(pathname)
|
|
.arg(TEST_TEMPLATE8)
|
|
.fails();
|
|
}
|
|
|
|
#[test]
|
|
fn test_mktemp_tmpdir_one_arg() {
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let result = scene
|
|
.ucmd_keepenv()
|
|
.arg("--tmpdir")
|
|
.arg("apt-key-gpghome.XXXXXXXXXX")
|
|
.succeeds();
|
|
result.no_stderr().stdout_contains("apt-key-gpghome.");
|
|
assert!(PathBuf::from(result.stdout_str().trim()).is_file());
|
|
}
|
|
|
|
#[test]
|
|
fn test_mktemp_directory_tmpdir() {
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let result = scene
|
|
.ucmd_keepenv()
|
|
.arg("--directory")
|
|
.arg("--tmpdir")
|
|
.arg("apt-key-gpghome.XXXXXXXXXX")
|
|
.succeeds();
|
|
result.no_stderr().stdout_contains("apt-key-gpghome.");
|
|
assert!(PathBuf::from(result.stdout_str().trim()).is_dir());
|
|
}
|
|
|
|
/// Test that an absolute path is disallowed when --tmpdir is provided.
|
|
#[test]
|
|
fn test_tmpdir_absolute_path() {
|
|
#[cfg(windows)]
|
|
let path = r"C:\XXX";
|
|
#[cfg(not(windows))]
|
|
let path = "/XXX";
|
|
new_ucmd!()
|
|
.args(&["--tmpdir=a", path])
|
|
.fails()
|
|
.stderr_only(format!(
|
|
"mktemp: invalid template, '{}'; with --tmpdir, it may not be absolute\n",
|
|
path
|
|
));
|
|
}
|
|
|
|
/// Decide whether a string matches a given template.
|
|
///
|
|
/// In the template, the character `'X'` is treated as a wildcard,
|
|
/// that is, it matches anything. All other characters in `template`
|
|
/// and `s` must match exactly.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```rust,ignore
|
|
/// # These all match.
|
|
/// assert!(matches_template("abc", "abc"));
|
|
/// assert!(matches_template("aXc", "abc"));
|
|
/// assert!(matches_template("XXX", "abc"));
|
|
///
|
|
/// # None of these match
|
|
/// assert!(matches_template("abc", "abcd"));
|
|
/// assert!(matches_template("abc", "ab"));
|
|
/// assert!(matches_template("aXc", "abd"));
|
|
/// assert!(matches_template("XXX", "abcd"));
|
|
/// ```
|
|
///
|
|
fn matches_template(template: &str, s: &str) -> bool {
|
|
if template.len() != s.len() {
|
|
return false;
|
|
}
|
|
for (a, b) in template.chars().zip(s.chars()) {
|
|
if !(a == 'X' || a == b) {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
/// An assertion that uses [`matches_template`] and adds a helpful error message.
|
|
macro_rules! assert_matches_template {
|
|
($template:expr, $s:expr) => {{
|
|
assert!(
|
|
matches_template($template, $s),
|
|
"\"{}\" != \"{}\"",
|
|
$template,
|
|
$s
|
|
);
|
|
}};
|
|
}
|
|
|
|
/// Test that the file is created in the directory given by the template.
|
|
#[test]
|
|
fn test_respect_template() {
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
let template = "XXX";
|
|
let result = ucmd.arg(template).succeeds();
|
|
let filename = result.no_stderr().stdout_str().trim_end();
|
|
assert_matches_template!(template, filename);
|
|
assert!(at.file_exists(filename));
|
|
}
|
|
|
|
/// Test that the file is created in the directory given by the template.
|
|
#[test]
|
|
fn test_respect_template_directory() {
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
at.mkdir("d");
|
|
#[cfg(not(windows))]
|
|
let template = "d/XXX";
|
|
#[cfg(windows)]
|
|
let template = r"d\XXX";
|
|
let result = ucmd.arg(template).succeeds();
|
|
let filename = result.no_stderr().stdout_str().trim_end();
|
|
assert_matches_template!(template, filename);
|
|
assert!(at.file_exists(filename));
|
|
}
|
|
|
|
#[cfg(unix)]
|
|
#[test]
|
|
fn test_directory_permissions() {
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
let result = ucmd.args(&["-d", "XXX"]).succeeds();
|
|
let dirname = result.no_stderr().stdout_str().trim_end();
|
|
assert_matches_template!("XXX", dirname);
|
|
let metadata = at.metadata(dirname);
|
|
assert!(metadata.is_dir());
|
|
assert_eq!(metadata.permissions().mode(), 0o40700);
|
|
}
|
|
|
|
/// Test that a template with a path separator is invalid.
|
|
#[test]
|
|
fn test_template_path_separator() {
|
|
#[cfg(not(windows))]
|
|
new_ucmd!()
|
|
.args(&["-t", "a/bXXX"])
|
|
.fails()
|
|
.stderr_only(format!(
|
|
"mktemp: invalid template, {}, contains directory separator\n",
|
|
"a/bXXX".quote()
|
|
));
|
|
#[cfg(windows)]
|
|
new_ucmd!()
|
|
.args(&["-t", r"a\bXXX"])
|
|
.fails()
|
|
.stderr_only(format!(
|
|
"mktemp: invalid template, {}, contains directory separator\n",
|
|
r"a\bXXX".quote()
|
|
));
|
|
}
|
|
|
|
/// Test that a suffix with a path separator is invalid.
|
|
#[test]
|
|
fn test_suffix_path_separator() {
|
|
#[cfg(not(windows))]
|
|
new_ucmd!()
|
|
.arg("aXXX/b")
|
|
.fails()
|
|
.stderr_only("mktemp: invalid suffix '/b', contains directory separator\n");
|
|
#[cfg(windows)]
|
|
new_ucmd!()
|
|
.arg(r"aXXX\b")
|
|
.fails()
|
|
.stderr_only("mktemp: invalid suffix '\\b', contains directory separator\n");
|
|
}
|
|
|
|
#[test]
|
|
fn test_too_few_xs_suffix() {
|
|
new_ucmd!()
|
|
.args(&["--suffix=X", "aXX"])
|
|
.fails()
|
|
.stderr_only("mktemp: too few X's in template 'aXXX'\n");
|
|
}
|
|
|
|
#[test]
|
|
fn test_too_few_xs_suffix_directory() {
|
|
new_ucmd!()
|
|
.args(&["-d", "--suffix=X", "aXX"])
|
|
.fails()
|
|
.stderr_only("mktemp: too few X's in template 'aXXX'\n");
|
|
}
|
|
|
|
#[test]
|
|
fn test_too_many_arguments() {
|
|
new_ucmd!().args(&["-q", "a", "b"]).fails().code_is(1);
|
|
}
|