mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 07:12:44 +00:00
cff7833bf6
Fix a bug in which `mktemp` would replace everything in the template argument from the first 'X' to the last 'X' with random bytes, instead of just replacing the last contiguous block of 'X's. Before this commit, $ mktemp XXX_XXX 2meCpfM After this commit, $ mktemp XXX_XXX XXX_Rp5 This fixes test cases `suffix2f` and `suffix2d` in `tests/misc/mktemp.pl` in the GNU coreutils test suite.
596 lines
14 KiB
Rust
596 lines
14 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);
|
|
}
|
|
|
|
#[test]
|
|
fn test_two_contiguous_wildcard_blocks() {
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
let template = "XXX_XXX";
|
|
let result = ucmd.arg(template).succeeds();
|
|
let filename = result.no_stderr().stdout_str().trim_end();
|
|
assert_eq!(&filename[..4], "XXX_");
|
|
assert_matches_template!(template, filename);
|
|
assert!(at.file_exists(filename));
|
|
}
|
|
|
|
#[test]
|
|
fn test_three_contiguous_wildcard_blocks() {
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
let template = "XXX_XXX_XXX";
|
|
let result = ucmd.arg(template).succeeds();
|
|
let filename = result.no_stderr().stdout_str().trim_end();
|
|
assert_eq!(&filename[..8], "XXX_XXX_");
|
|
assert_matches_template!(template, filename);
|
|
assert!(at.file_exists(filename));
|
|
}
|