2023-08-21 10:49:27 +02:00
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
2021-05-30 00:10:54 -05:00
// spell-checker:ignore (words) gpghome
2025-03-28 09:51:51 +01:00
use uutests ::at_and_ucmd ;
use uutests ::new_ucmd ;
use uutests ::util ::TestScenario ;
use uutests ::util_name ;
2020-06-09 11:30:19 +02:00
2022-05-05 18:05:16 -04:00
use uucore ::display ::Quotable ;
2022-05-21 23:31:12 -04:00
#[cfg(not(windows))]
use std ::path ::MAIN_SEPARATOR ;
2025-03-24 21:04:32 +01:00
use std ::path ::PathBuf ;
2021-01-19 08:15:53 +01:00
use tempfile ::tempdir ;
2016-01-03 19:10:47 +09:00
2022-05-16 18:05:59 +00:00
#[cfg(unix)]
use std ::os ::unix ::fs ::PermissionsExt ;
2021-05-29 14:32:35 +02:00
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" ;
2024-04-30 18:23:05 +02:00
static TEST_TEMPLATE6 : & str = "tempXXXlate" ;
static TEST_TEMPLATE7 : & str = "XXXtemplate" ;
2016-01-03 19:10:47 +09:00
#[cfg(unix)]
2021-05-29 14:32:35 +02:00
static TEST_TEMPLATE8 : & str = "tempXXXl/ate" ;
2016-01-03 19:10:47 +09:00
#[cfg(windows)]
2021-05-29 14:32:35 +02:00
static TEST_TEMPLATE8 : & str = "tempXXXl \\ ate" ;
2026-01-15 23:45:18 +08:00
static TEST_TEMPLATE9 : & str = "XXX_XX" ;
2016-01-03 19:10:47 +09:00
2021-06-22 17:36:56 +02:00
#[cfg(not(windows))]
2021-05-29 14:32:35 +02:00
const TMPDIR : & str = "TMPDIR" ;
2021-06-22 17:36:56 +02:00
#[cfg(windows)]
const TMPDIR : & str = "TMP" ;
2016-01-03 19:10:47 +09:00
2022-05-26 21:02:12 -04:00
/// 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
);
}};
}
2022-05-21 23:31:12 -04:00
/// Like [`assert_matches_template`] but for the suffix of a string.
#[cfg(windows)]
macro_rules ! assert_suffix_matches_template {
( $template :expr , $s :expr ) => {{
let n = ( $s ). len ();
let m = ( $template ). len ();
let suffix = & $s [ n - m .. n ];
assert! (
matches_template ( $template , suffix ),
2025-04-07 22:56:21 -04:00
" \" {} \" does not end with \" {suffix} \" " ,
2022-05-21 23:31:12 -04:00
$template ,
);
}};
}
2016-01-03 19:10:47 +09:00
#[test]
fn test_mktemp_mktemp () {
2016-08-23 07:52:43 -04:00
let scene = TestScenario ::new ( util_name! ());
2016-01-03 19:10:47 +09:00
2016-07-29 17:26:32 -04:00
let pathname = scene . fixtures . as_string ();
2016-01-03 19:10:47 +09:00
2020-04-13 20:36:03 +02:00
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 ();
2026-01-15 23:45:18 +08:00
scene
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( TEST_TEMPLATE9 )
. fails ();
2016-01-03 19:10:47 +09:00
}
2021-01-02 09:53:47 +01:00
#[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 ();
2021-04-17 15:14:49 +03:00
scene
2021-01-02 09:53:47 +01:00
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( "-t" )
. arg ( TEST_TEMPLATE8 )
2021-04-17 15:14:49 +03:00
. fails ()
. no_stdout ()
2021-06-28 13:45:15 +02:00
. stderr_contains ( "invalid suffix" )
. stderr_contains ( "contains directory separator" );
2026-01-15 23:45:18 +08:00
scene
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( "-t" )
. arg ( TEST_TEMPLATE9 )
. fails ();
2021-01-02 09:53:47 +01:00
}
2016-06-18 17:44:07 +08:00
#[test]
fn test_mktemp_make_temp_dir () {
2016-08-23 07:52:43 -04:00
let scene = TestScenario ::new ( util_name! ());
2016-06-18 17:44:07 +08:00
2016-07-29 17:26:32 -04:00
let pathname = scene . fixtures . as_string ();
2016-06-18 17:44:07 +08:00
2020-04-13 20:36:03 +02:00
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 ();
2026-01-15 23:45:18 +08:00
scene
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( "-d" )
. arg ( TEST_TEMPLATE9 )
. fails ();
2016-06-18 17:44:07 +08:00
}
2016-01-04 02:11:15 +09:00
2016-01-03 19:10:47 +09:00
#[test]
fn test_mktemp_dry_run () {
2016-08-23 07:52:43 -04:00
let scene = TestScenario ::new ( util_name! ());
2016-01-03 19:10:47 +09:00
2016-07-29 17:26:32 -04:00
let pathname = scene . fixtures . as_string ();
2016-01-03 19:10:47 +09:00
2020-04-13 20:36:03 +02:00
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 ();
2026-01-15 23:45:18 +08:00
scene
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( "-u" )
. arg ( TEST_TEMPLATE9 )
. fails ();
2016-01-03 19:10:47 +09:00
}
2016-06-18 17:44:07 +08:00
#[test]
fn test_mktemp_quiet () {
2016-08-23 07:52:43 -04:00
let scene = TestScenario ::new ( util_name! ());
2016-06-18 17:44:07 +08:00
2020-04-13 20:36:03 +02:00
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 ();
2016-06-18 17:44:07 +08:00
}
2016-01-03 19:10:47 +09:00
#[test]
fn test_mktemp_suffix () {
2016-08-23 07:52:43 -04:00
let scene = TestScenario ::new ( util_name! ());
2016-01-03 19:10:47 +09:00
2016-07-29 17:26:32 -04:00
let pathname = scene . fixtures . as_string ();
2016-01-03 19:10:47 +09:00
2020-04-13 20:36:03 +02:00
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 ();
2026-01-15 23:45:18 +08:00
scene
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( "--suffix" )
. arg ( "suf" )
. arg ( TEST_TEMPLATE9 )
. fails ();
2016-01-03 19:10:47 +09:00
}
#[test]
fn test_mktemp_tmpdir () {
2016-08-23 07:52:43 -04:00
let scene = TestScenario ::new ( util_name! ());
2020-06-09 11:30:19 +02:00
let dir = tempdir (). unwrap ();
let path = dir . path (). join ( scene . fixtures . as_string ());
let pathname = path . as_os_str ();
2016-01-03 19:10:47 +09:00
2020-04-13 20:36:03 +02:00
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 ();
2026-01-15 23:45:18 +08:00
scene
. ucmd ()
. arg ( "-p" )
. arg ( pathname )
. arg ( TEST_TEMPLATE9 )
. fails ();
2016-01-03 19:10:47 +09:00
}
2021-02-11 00:03:23 +01:00
2025-08-07 23:38:58 +08:00
#[test]
fn test_mktemp_empty_tmpdir () {
let scene = TestScenario ::new ( util_name! ());
let pathname = scene . fixtures . as_string ();
let result = scene
. ucmd ()
. env ( TMPDIR , & pathname )
. args ( & [ "-p" , "" ])
. succeeds ();
assert! ( result . stdout_str (). trim (). starts_with ( & pathname ));
let result = scene
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( "--tmpdir=" )
. succeeds ();
assert! ( result . stdout_str (). trim (). starts_with ( & pathname ));
}
2021-02-11 00:03:23 +01:00
#[test]
fn test_mktemp_tmpdir_one_arg () {
let scene = TestScenario ::new ( util_name! ());
let result = scene
2023-01-25 03:40:39 +01:00
. ucmd ()
2021-02-11 00:03:23 +01:00
. arg ( "--tmpdir" )
. arg ( "apt-key-gpghome.XXXXXXXXXX" )
. succeeds ();
2021-04-18 02:33:52 +03:00
result . no_stderr (). stdout_contains ( "apt-key-gpghome." );
2021-04-17 15:14:49 +03:00
assert! ( PathBuf ::from ( result . stdout_str (). trim ()). is_file ());
2021-02-11 00:03:23 +01:00
}
#[test]
fn test_mktemp_directory_tmpdir () {
let scene = TestScenario ::new ( util_name! ());
let result = scene
2023-01-25 03:40:39 +01:00
. ucmd ()
2021-02-11 00:03:23 +01:00
. arg ( "--directory" )
. arg ( "--tmpdir" )
. arg ( "apt-key-gpghome.XXXXXXXXXX" )
. succeeds ();
2021-04-17 15:14:49 +03:00
result . no_stderr (). stdout_contains ( "apt-key-gpghome." );
assert! ( PathBuf ::from ( result . stdout_str (). trim ()). is_dir ());
2021-02-11 00:03:23 +01:00
}
2022-05-01 12:03:02 -04:00
2022-05-26 21:02:12 -04:00
/// Test for combining `--tmpdir` and a template with a subdirectory.
#[test]
fn test_tmpdir_template_has_subdirectory () {
let ( at , mut ucmd ) = at_and_ucmd! ();
at . mkdir ( "a" );
#[cfg(not(windows))]
let ( template , joined ) = ( "a/bXXXX" , "./a/bXXXX" );
#[cfg(windows)]
let ( template , joined ) = ( r "a\bXXXX" , r ".\a\bXXXX" );
let result = ucmd . args ( & [ "--tmpdir=." , template ]). succeeds ();
let filename = result . no_stderr (). stdout_str (). trim_end ();
assert_matches_template! ( joined , filename );
assert! ( at . file_exists ( filename ));
}
2022-05-11 22:31:09 -04:00
/// 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! (
2023-01-27 10:29:45 +01:00
"mktemp: invalid template, ' {path} '; with --tmpdir, it may not be absolute \n "
2022-05-11 22:31:09 -04:00
));
}
2022-05-01 12:03:02 -04:00
/// 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
}
/// 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" );
2022-05-01 16:51:25 -04:00
#[cfg(not(windows))]
2022-05-01 12:03:02 -04:00
let template = "d/XXX" ;
2022-05-01 16:51:25 -04:00
#[cfg(windows)]
let template = r "d\XXX" ;
2022-05-01 12:03:02 -04:00
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 ));
}
2022-05-05 18:05:16 -04:00
2022-05-16 18:05:59 +00:00
#[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 );
}
2022-05-16 19:07:18 +01:00
2022-05-05 18:05:16 -04:00
/// Test that a template with a path separator is invalid.
#[test]
fn test_template_path_separator () {
2022-05-28 18:35:34 -04:00
#[cfg(not(windows))]
2022-05-05 18:05:16 -04:00
new_ucmd! ()
. args ( & [ "-t" , "a/bXXX" ])
. fails ()
. stderr_only ( format! (
"mktemp: invalid template, {} , contains directory separator \n " ,
"a/bXXX" . quote ()
));
2022-05-28 18:35:34 -04:00
#[cfg(windows)]
new_ucmd! ()
. args ( & [ "-t" , r "a\bXXX" ])
. fails ()
. stderr_only ( format! (
"mktemp: invalid template, {} , contains directory separator \n " ,
r "a\bXXX" . quote ()
));
2022-05-05 18:05:16 -04:00
}
2022-05-18 18:33:53 -04:00
2023-02-22 20:22:16 -03:00
/// Test that a prefix with a point is valid.
#[test]
fn test_prefix_template_separator () {
2023-05-19 10:42:52 +02:00
new_ucmd! (). args ( & [ "-p" , "." , "-t" , "a.XXXX" ]). succeeds ();
2023-02-22 20:22:16 -03:00
}
2023-02-28 12:38:18 -03:00
#[test]
fn test_prefix_template_with_path_separator () {
#[cfg(not(windows))]
new_ucmd! ()
. args ( & [ "-t" , "a/XXX" ])
. fails ()
. stderr_only ( format! (
"mktemp: invalid template, {} , contains directory separator \n " ,
"a/XXX" . quote ()
));
#[cfg(windows)]
new_ucmd! ()
. args ( & [ "-t" , r "a\XXX" ])
. fails ()
. stderr_only ( format! (
"mktemp: invalid template, {} , contains directory separator \n " ,
r "a\XXX" . quote ()
));
}
2022-05-18 18:33:53 -04:00
/// 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 " );
2022-05-26 21:02:12 -04:00
#[cfg(not(windows))]
new_ucmd! ()
. arg ( "XXX/.." )
. fails ()
. stderr_only ( "mktemp: invalid suffix '/..', contains directory separator \n " );
#[cfg(windows)]
new_ucmd! ()
. arg ( r "XXX\.." )
. fails ()
. stderr_only ( "mktemp: invalid suffix ' \\ ..', contains directory separator \n " );
2022-05-18 18:33:53 -04:00
}
2022-05-21 21:58:38 -04:00
#[test]
fn test_too_few_xs_suffix () {
new_ucmd! ()
. args ( & [ "--suffix=X" , "aXX" ])
. fails ()
2024-03-29 21:48:38 +01:00
. stderr_only ( "mktemp: too few X's in template 'aXX' \n " );
2022-05-21 21:58:38 -04:00
}
#[test]
fn test_too_few_xs_suffix_directory () {
new_ucmd! ()
. args ( & [ "-d" , "--suffix=X" , "aXX" ])
. fails ()
2024-03-29 21:48:38 +01:00
. stderr_only ( "mktemp: too few X's in template 'aXX' \n " );
2022-05-21 21:58:38 -04:00
}
2022-05-30 09:30:47 -04:00
#[test]
fn test_too_many_arguments () {
2022-09-17 16:04:28 -04:00
new_ucmd! ()
. args ( & [ "-q" , "a" , "b" ])
2025-03-01 15:29:11 +01:00
. fails_with_code ( 1 )
2022-09-17 16:04:28 -04:00
. usage_error ( "too many templates" );
2022-05-30 09:30:47 -04:00
}
2022-06-01 09:35:31 -04:00
#[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 ));
}
2022-06-06 09:46:15 -04:00
/// Test that template must end in X even if `--suffix` is the empty string.
#[test]
fn test_suffix_must_end_in_x () {
new_ucmd! ()
. args ( & [ "--suffix=" , "aXXXb" ])
. fails ()
. stderr_is ( "mktemp: with --suffix, template 'aXXXb' must end in X \n " );
}
#[test]
fn test_suffix_empty_template () {
new_ucmd! ()
. args ( & [ "--suffix=aXXXb" , "" ])
. fails ()
. stderr_is ( "mktemp: with --suffix, template '' must end in X \n " );
new_ucmd! ()
. args ( & [ "-d" , "--suffix=aXXXb" , "" ])
. fails ()
. stderr_is ( "mktemp: with --suffix, template '' must end in X \n " );
}
2022-06-09 14:48:05 +08:00
#[test]
fn test_mktemp_with_posixly_correct () {
let scene = TestScenario ::new ( util_name! ());
scene
. ucmd ()
. env ( "POSIXLY_CORRECT" , "1" )
. args ( & [ "aXXXX" , "--suffix=b" ])
. fails ()
2023-01-08 12:39:49 -06:00
. usage_error ( "too many templates" );
2022-06-09 14:48:05 +08:00
scene
. ucmd ()
. env ( "POSIXLY_CORRECT" , "1" )
. args ( & [ "--suffix=b" , "aXXXX" ])
. succeeds ();
}
2022-05-21 23:31:12 -04:00
/// Test that files are created relative to `TMPDIR` environment variable.
#[test]
fn test_tmpdir_env_var () {
// `TMPDIR=. mktemp`
let ( at , mut ucmd ) = at_and_ucmd! ();
let result = ucmd . env ( TMPDIR , "." ). succeeds ();
let filename = result . no_stderr (). stdout_str (). trim_end ();
#[cfg(not(windows))]
{
2023-01-27 10:29:45 +01:00
let template = format! ( ". {MAIN_SEPARATOR} tmp.XXXXXXXXXX" );
2022-05-21 23:31:12 -04:00
assert_matches_template! ( & template , filename );
}
// On Windows, `env::temp_dir()` seems to give an absolute path
// regardless of the value of `TMPDIR`; see
// * https://github.com/uutils/coreutils/pull/3552#issuecomment-1211804981
// * https://doc.rust-lang.org/std/env/fn.temp_dir.html
// * https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppath2w
#[cfg(windows)]
assert_suffix_matches_template! ( "tmp.XXXXXXXXXX" , filename );
assert! ( at . file_exists ( filename ));
2023-01-22 22:23:45 -05:00
// `TMPDIR=. mktemp --tmpdir`
let ( at , mut ucmd ) = at_and_ucmd! ();
let result = ucmd . env ( TMPDIR , "." ). arg ( "--tmpdir" ). succeeds ();
let filename = result . no_stderr (). stdout_str (). trim_end ();
#[cfg(not(windows))]
{
let template = format! ( ". {MAIN_SEPARATOR} tmp.XXXXXXXXXX" );
assert_matches_template! ( & template , filename );
}
#[cfg(windows)]
assert_suffix_matches_template! ( "tmp.XXXXXXXXXX" , filename );
assert! ( at . file_exists ( filename ));
2022-05-21 23:31:12 -04:00
// `TMPDIR=. mktemp --tmpdir XXX`
let ( at , mut ucmd ) = at_and_ucmd! ();
let result = ucmd . env ( TMPDIR , "." ). args ( & [ "--tmpdir" , "XXX" ]). succeeds ();
let filename = result . no_stderr (). stdout_str (). trim_end ();
#[cfg(not(windows))]
{
2023-01-27 10:29:45 +01:00
let template = format! ( ". {MAIN_SEPARATOR} XXX" );
2022-05-21 23:31:12 -04:00
assert_matches_template! ( & template , filename );
}
#[cfg(windows)]
assert_suffix_matches_template! ( "XXX" , filename );
assert! ( at . file_exists ( filename ));
// `TMPDIR=. mktemp XXX` - in this case `TMPDIR` is ignored.
let ( at , mut ucmd ) = at_and_ucmd! ();
let result = ucmd . env ( TMPDIR , "." ). arg ( "XXX" ). succeeds ();
let filename = result . no_stderr (). stdout_str (). trim_end ();
let template = "XXX" ;
assert_matches_template! ( template , filename );
assert! ( at . file_exists ( filename ));
}
2022-09-15 22:49:50 -04:00
#[test]
fn test_nonexistent_tmpdir_env_var () {
#[cfg(not(windows))]
new_ucmd! (). env ( TMPDIR , "no/such/dir" ). fails (). stderr_only ( "mktemp: failed to create file via template 'no/such/dir/tmp.XXXXXXXXXX': No such file or directory \n " );
#[cfg(windows)]
{
let result = new_ucmd! (). env ( TMPDIR , r "no\such\dir" ). fails ();
result . no_stdout ();
let stderr = result . stderr_str ();
assert! (
stderr . starts_with ( "mktemp: failed to create file via template" ),
2025-04-07 22:56:21 -04:00
"{stderr}" ,
2022-09-15 22:49:50 -04:00
);
assert! (
stderr . ends_with ( "no \\ such \\ dir \\ tmp.XXXXXXXXXX': No such file or directory \n " ),
2025-04-07 22:56:21 -04:00
"{stderr}" ,
2022-09-15 22:49:50 -04:00
);
}
#[cfg(not(windows))]
new_ucmd! (). env ( TMPDIR , "no/such/dir" ). arg ( "-d" ). fails (). stderr_only ( "mktemp: failed to create directory via template 'no/such/dir/tmp.XXXXXXXXXX': No such file or directory \n " );
#[cfg(windows)]
{
let result = new_ucmd! (). env ( TMPDIR , r "no\such\dir" ). arg ( "-d" ). fails ();
result . no_stdout ();
let stderr = result . stderr_str ();
assert! (
stderr . starts_with ( "mktemp: failed to create directory via template" ),
2025-04-07 22:56:21 -04:00
"{stderr}" ,
2022-09-15 22:49:50 -04:00
);
assert! (
stderr . ends_with ( "no \\ such \\ dir \\ tmp.XXXXXXXXXX': No such file or directory \n " ),
2025-04-07 22:56:21 -04:00
"{stderr}" ,
2022-09-15 22:49:50 -04:00
);
}
}
2026-01-30 18:07:46 +08:00
#[test]
fn test_empty_tmpdir_env_var () {
2026-01-31 00:02:49 +08:00
#[cfg(not(any(windows, target_os = "android" )))]
2026-01-30 18:07:46 +08:00
{
let result = new_ucmd! (). env ( TMPDIR , "" ). succeeds ();
assert! ( result . stdout_str (). starts_with ( "/tmp" ));
}
2026-01-31 00:02:49 +08:00
#[cfg(any(windows, target_os = "android" ))]
2026-01-30 18:07:46 +08:00
{
let result = new_ucmd! (). env ( TMPDIR , "" ). fails ();
result . no_stdout ();
let stderr = result . stderr_str ();
assert! (
stderr . starts_with ( "mktemp: failed to create file via template" ),
"{stderr}"
);
2026-01-31 00:45:25 +08:00
#[cfg(windows)]
2026-01-30 18:07:46 +08:00
assert! (
2026-01-30 22:28:41 +08:00
stderr . ends_with ( "/tmp \\ tmp.XXXXXXXXXX': No such file or directory \n " ),
2026-01-30 18:07:46 +08:00
"{stderr}" ,
);
2026-01-31 00:45:25 +08:00
#[cfg(target_os = "android" )]
assert! (
stderr . ends_with ( "/tmp/tmp.XXXXXXXXXX': No such file or directory \n " ),
"{stderr}" ,
);
2026-01-30 18:07:46 +08:00
}
2026-01-31 00:02:49 +08:00
#[cfg(not(any(windows, target_os = "android" )))]
2026-01-30 18:07:46 +08:00
{
let result = new_ucmd! (). env ( TMPDIR , "" ). arg ( "-d" ). succeeds ();
assert! ( result . stdout_str (). starts_with ( "/tmp" ));
}
2026-01-31 00:02:49 +08:00
#[cfg(any(windows, target_os = "android" ))]
2026-01-30 18:07:46 +08:00
{
let result = new_ucmd! (). env ( TMPDIR , "" ). arg ( "-d" ). fails ();
result . no_stdout ();
let stderr = result . stderr_str ();
assert! (
stderr . starts_with ( "mktemp: failed to create directory via template" ),
"{stderr}"
);
2026-01-31 00:45:25 +08:00
#[cfg(windows)]
2026-01-30 18:07:46 +08:00
assert! (
2026-01-30 22:28:41 +08:00
stderr . ends_with ( "/tmp \\ tmp.XXXXXXXXXX': No such file or directory \n " ),
2026-01-30 18:07:46 +08:00
"{stderr}" ,
);
2026-01-31 00:45:25 +08:00
#[cfg(target_os = "android" )]
assert! (
stderr . ends_with ( "/tmp/tmp.XXXXXXXXXX': No such file or directory \n " ),
"{stderr}" ,
);
2026-01-30 18:07:46 +08:00
}
}
2022-09-15 22:49:50 -04:00
#[test]
fn test_nonexistent_dir_prefix () {
#[cfg(not(windows))]
new_ucmd! (). arg ( "d/XXX" ). fails (). stderr_only (
"mktemp: failed to create file via template 'd/XXX': No such file or directory \n " ,
);
#[cfg(windows)]
{
let result = new_ucmd! (). arg ( r "d\XXX" ). fails ();
result . no_stdout ();
let stderr = result . stderr_str ();
assert! (
stderr . starts_with ( "mktemp: failed to create file via template" ),
2025-04-07 22:56:21 -04:00
"{stderr}" ,
2022-09-15 22:49:50 -04:00
);
assert! (
stderr . ends_with ( "d \\ XXX': No such file or directory \n " ),
2025-04-07 22:56:21 -04:00
"{stderr}" ,
2022-09-15 22:49:50 -04:00
);
}
#[cfg(not(windows))]
new_ucmd! (). arg ( "-d" ). arg ( "d/XXX" ). fails (). stderr_only (
"mktemp: failed to create directory via template 'd/XXX': No such file or directory \n " ,
);
#[cfg(windows)]
{
let result = new_ucmd! (). arg ( "-d" ). arg ( r "d\XXX" ). fails ();
result . no_stdout ();
let stderr = result . stderr_str ();
assert! (
stderr . starts_with ( "mktemp: failed to create directory via template" ),
2025-04-07 22:56:21 -04:00
"{stderr}" ,
2022-09-15 22:49:50 -04:00
);
assert! (
stderr . ends_with ( "d \\ XXX': No such file or directory \n " ),
2025-04-07 22:56:21 -04:00
"{stderr}" ,
2022-09-15 22:49:50 -04:00
);
}
}
2022-10-30 19:57:39 +01:00
#[test]
fn test_default_missing_value () {
2023-05-19 10:38:04 +02:00
new_ucmd! (). arg ( "-d" ). arg ( "--tmpdir" ). succeeds ();
2022-10-30 19:57:39 +01:00
}
2023-05-05 13:44:50 +02:00
#[test]
fn test_default_issue_4821_t_tmpdir () {
let scene = TestScenario ::new ( util_name! ());
let pathname = scene . fixtures . as_string ();
let result = scene
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( "-t" )
. arg ( "foo.XXXX" )
. succeeds ();
let stdout = result . stdout_str ();
println! ( "stdout = {stdout} " );
assert! ( stdout . contains ( & pathname ));
}
2023-05-15 21:32:35 +02:00
#[test]
fn test_default_issue_4821_t_tmpdir_p () {
let scene = TestScenario ::new ( util_name! ());
let pathname = scene . fixtures . as_string ();
let result = scene
. ucmd ()
. arg ( "-t" )
. arg ( "-p" )
. arg ( & pathname )
. arg ( "foo.XXXX" )
. succeeds ();
let stdout = result . stdout_str ();
println! ( "stdout = {stdout} " );
assert! ( stdout . contains ( & pathname ));
}
2023-05-19 16:53:20 +02:00
#[test]
fn test_t_ensure_tmpdir_has_higher_priority_than_p () {
let scene = TestScenario ::new ( util_name! ());
let pathname = scene . fixtures . as_string ();
let result = scene
. ucmd ()
. env ( TMPDIR , & pathname )
. arg ( "-t" )
. arg ( "-p" )
. arg ( "should_not_attempt_to_write_in_this_nonexisting_dir" )
. arg ( "foo.XXXX" )
. succeeds ();
let stdout = result . stdout_str ();
println! ( "stdout = {stdout} " );
assert! ( stdout . contains ( & pathname ));
}
2023-02-13 00:00:02 -07:00
#[test]
fn test_missing_xs_tmpdir_template () {
let scene = TestScenario ::new ( util_name! ());
scene
. ucmd ()
. arg ( "--tmpdir" )
. arg ( TEST_TEMPLATE3 )
. fails ()
. no_stdout ()
. stderr_contains ( "too few X's in template" );
scene
. ucmd ()
. arg ( "--tmpdir=foobar" )
. fails ()
. no_stdout ()
. stderr_contains ( "failed to create file via template" );
}
#[test]
fn test_both_tmpdir_flags_present () {
let scene = TestScenario ::new ( util_name! ());
2023-07-05 12:51:56 +02:00
#[cfg(not(windows))]
2023-04-11 00:43:15 -06:00
let template = format! ( ". {MAIN_SEPARATOR} foobarXXXX" );
2023-07-05 12:51:56 +02:00
2023-04-11 00:43:15 -06:00
let ( at , mut ucmd ) = at_and_ucmd! ();
let result = ucmd
. env ( TMPDIR , "." )
2023-02-13 00:00:02 -07:00
. arg ( "-p" )
2023-04-11 00:43:15 -06:00
. arg ( "nonsense" )
2023-02-13 00:00:02 -07:00
. arg ( "--tmpdir" )
. arg ( "foobarXXXX" )
2023-04-11 00:43:15 -06:00
. succeeds ();
let filename = result . no_stderr (). stdout_str (). trim_end ();
2023-07-05 12:51:56 +02:00
#[cfg(not(windows))]
2023-04-11 00:43:15 -06:00
assert_matches_template! ( & template , filename );
2023-07-05 12:51:56 +02:00
#[cfg(windows)]
assert_suffix_matches_template! ( "foobarXXXX" , filename );
2023-04-11 00:43:15 -06:00
assert! ( at . file_exists ( filename ));
2023-02-13 00:00:02 -07:00
scene
. ucmd ()
. arg ( "-p" )
. arg ( "." )
2023-07-05 12:51:56 +02:00
. arg ( "--tmpdir=does_not_exist" )
2023-02-13 00:00:02 -07:00
. fails ()
. no_stdout ()
. stderr_contains ( "failed to create file via template" );
2023-04-11 00:43:15 -06:00
let ( at , mut ucmd ) = at_and_ucmd! ();
let result = ucmd
2023-02-13 00:00:02 -07:00
. arg ( "--tmpdir" )
. arg ( "foobarXXXX" )
. arg ( "-p" )
. arg ( "." )
2023-04-11 00:43:15 -06:00
. succeeds ();
let filename = result . no_stderr (). stdout_str (). trim_end ();
2023-07-05 12:51:56 +02:00
#[cfg(not(windows))]
2023-04-11 00:43:15 -06:00
assert_matches_template! ( & template , filename );
2023-07-05 12:51:56 +02:00
#[cfg(windows)]
assert_suffix_matches_template! ( "foobarXXXX" , filename );
2023-04-11 00:43:15 -06:00
assert! ( at . file_exists ( filename ));
2023-02-13 00:00:02 -07:00
}
#[test]
fn test_missing_short_tmpdir_flag () {
let scene = TestScenario ::new ( util_name! ());
scene
. ucmd ()
. arg ( "-p" )
. fails ()
. no_stdout ()
. stderr_contains ( "a value is required for '-p <DIR>' but none was supplied" );
}
2025-08-08 14:52:14 +02:00
#[test]
#[cfg(target_os = "linux" )]
fn test_non_utf8_template () {
use std ::ffi ::OsStr ;
use std ::os ::unix ::ffi ::OsStrExt ;
let ts = TestScenario ::new ( util_name! ());
// Test that mktemp gracefully handles non-UTF-8 templates with an error instead of panicking
let template = OsStr ::from_bytes ( b "test_ \xFF\xFE _XXXXXX" );
ts . ucmd (). arg ( template ). fails (). stderr_contains ( "invalid" );
}
2025-08-12 00:40:02 +02:00
#[test]
#[cfg(target_os = "linux" )]
fn test_non_utf8_tmpdir_path () {
use std ::os ::unix ::ffi ::OsStrExt ;
let ( at , mut ucmd ) = at_and_ucmd! ();
// Create a directory with non-UTF8 bytes
let dir_name = std ::ffi ::OsStr ::from_bytes ( b "test_dir_ \xFF\xFE " );
std ::fs ::create_dir ( at . plus ( dir_name )). unwrap ();
// Test that mktemp can handle non-UTF8 directory paths with -p option
ucmd . arg ( "-p" ). arg ( at . plus ( dir_name )). succeeds ();
}
#[test]
#[cfg(target_os = "linux" )]
fn test_non_utf8_tmpdir_long_option () {
use std ::os ::unix ::ffi ::OsStrExt ;
let ( at , mut ucmd ) = at_and_ucmd! ();
// Create a directory with non-UTF8 bytes
let dir_name = std ::ffi ::OsStr ::from_bytes ( b "test_dir_ \xFF\xFE " );
std ::fs ::create_dir ( at . plus ( dir_name )). unwrap ();
// Test that mktemp can handle non-UTF8 directory paths with --tmpdir option
// Note: Due to test framework limitations with non-UTF8 arguments and --tmpdir= syntax,
// we'll test a more limited scenario that still validates non-UTF8 path handling
ucmd . arg ( "-p" )
. arg ( at . plus ( dir_name ))
. arg ( "tmpXXXXXX" )
. succeeds ();
}
2026-02-08 20:12:01 +01:00
#[test]
#[cfg(target_os = "linux" )]
fn test_invalid_utf8_suffix () {
use std ::os ::unix ::ffi ::OsStrExt ;
let ( at , mut ucmd ) = at_and_ucmd! ();
// Create invalid UTF-8 bytes for suffix
// This mimics the GNU test which tests mktemp with bad unicode characters
let invalid_utf8 = std ::ffi ::OsStr ::from_bytes ( b " \xC3 | \xED\xBA\xAD " );
// Test that mktemp handles invalid UTF-8 in suffix gracefully
// It should succeed and create a file with the lossy conversion of the invalid UTF-8
ucmd . arg ( "-p" )
. arg ( at . as_string ())
. arg ( "--suffix" )
. arg ( invalid_utf8 )
. arg ( "tmpXXXXXX" )
. succeeds ();
}
2025-08-12 00:40:02 +02:00
#[test]
#[cfg(target_os = "linux" )]
fn test_non_utf8_tmpdir_directory_creation () {
use std ::os ::unix ::ffi ::OsStrExt ;
let ( at , mut ucmd ) = at_and_ucmd! ();
// Create a directory with non-UTF8 bytes
let dir_name = std ::ffi ::OsStr ::from_bytes ( b "test_dir_ \xFF\xFE " );
std ::fs ::create_dir ( at . plus ( dir_name )). unwrap ();
// Test directory creation (-d flag) with non-UTF8 directory paths
// We can't easily verify the exact output path because of UTF8 conversion issues,
// but we can verify the command succeeds
ucmd . arg ( "-d" ). arg ( "-p" ). arg ( at . plus ( dir_name )). succeeds ();
}