2021-03-22 10:13:38 +00:00
#![ allow(dead_code) ]
2021-04-01 21:37:22 +00:00
#[ cfg(not(windows)) ]
2021-03-29 11:10:13 +00:00
use libc ;
2021-04-24 14:43:13 +00:00
use pretty_assertions ::assert_eq ;
2015-11-16 05:25:01 +00:00
use std ::env ;
2021-04-01 21:37:22 +00:00
#[ cfg(not(windows)) ]
use std ::ffi ::CString ;
use std ::ffi ::OsStr ;
2016-04-02 21:09:20 +00:00
use std ::fs ::{ self , File , OpenOptions } ;
2018-03-12 08:20:58 +00:00
use std ::io ::{ Read , Result , Write } ;
2015-11-16 05:25:01 +00:00
#[ cfg(unix) ]
2018-11-13 04:55:25 +00:00
use std ::os ::unix ::fs ::{ symlink as symlink_dir , symlink as symlink_file } ;
2015-11-16 05:25:01 +00:00
#[ cfg(windows) ]
2018-11-13 04:55:25 +00:00
use std ::os ::windows ::fs ::{ symlink_dir , symlink_file } ;
2015-11-16 05:25:01 +00:00
use std ::path ::{ Path , PathBuf } ;
2018-03-12 08:20:58 +00:00
use std ::process ::{ Child , Command , Stdio } ;
2015-11-16 05:25:01 +00:00
use std ::rc ::Rc ;
2020-01-28 05:14:11 +00:00
use std ::str ::from_utf8 ;
2016-04-02 21:09:20 +00:00
use std ::thread ::sleep ;
use std ::time ::Duration ;
2021-01-19 07:15:53 +00:00
use tempfile ::TempDir ;
2021-04-25 21:28:42 +00:00
use uucore ::{ Args , InvalidEncodingHandling } ;
2015-11-16 05:25:01 +00:00
2015-12-24 03:59:04 +00:00
#[ cfg(windows) ]
2020-05-24 23:46:16 +00:00
static PROGNAME : & str = concat! ( env! ( " CARGO_PKG_NAME " ) , " .exe " ) ;
2015-12-24 03:59:04 +00:00
#[ cfg(not(windows)) ]
2020-05-24 23:46:16 +00:00
static PROGNAME : & str = env! ( " CARGO_PKG_NAME " ) ;
2016-05-22 19:09:04 +00:00
2020-02-07 04:40:45 +00:00
static TESTS_DIR : & str = " tests " ;
static FIXTURES_DIR : & str = " fixtures " ;
2016-05-22 19:09:04 +00:00
2020-02-07 04:40:45 +00:00
static ALREADY_RUN : & str = " you have already run this UCommand, if you want to run \
2021-04-02 09:56:49 +00:00
another command in the same test , use TestScenario ::new instead of \
testing ( ) ; " ;
2020-02-07 04:40:45 +00:00
static MULTIPLE_STDIN_MEANINGLESS : & str = " Ucommand is designed around a typical use case of: provide args and input stream -> spawn process -> block until completion -> return output streams. For verifying that a particular section of the input stream is what causes a particular behavior, use the Command type directly. " ;
2016-03-26 19:27:54 +00:00
2021-04-09 09:08:31 +00:00
static NO_STDIN_MEANINGLESS : & str = " Setting this flag has no effect if there is no stdin " ;
2021-03-22 10:13:38 +00:00
/// Test if the program is running under CI
2020-06-13 23:54:43 +00:00
pub fn is_ci ( ) -> bool {
std ::env ::var ( " CI " )
. unwrap_or ( String ::from ( " false " ) )
. eq_ignore_ascii_case ( " true " )
}
2021-03-31 09:25:23 +00:00
/// Read a test scenario fixture, returning its bytes
fn read_scenario_fixture < S : AsRef < OsStr > > ( tmpd : & Option < Rc < TempDir > > , file_rel_path : S ) -> Vec < u8 > {
2016-07-29 21:26:32 +00:00
let tmpdir_path = tmpd . as_ref ( ) . unwrap ( ) . as_ref ( ) . path ( ) ;
2021-03-31 09:25:23 +00:00
AtPath ::new ( tmpdir_path ) . read_bytes ( file_rel_path . as_ref ( ) . to_str ( ) . unwrap ( ) )
2016-07-29 21:26:32 +00:00
}
2016-05-22 19:09:04 +00:00
2016-07-17 09:20:33 +00:00
/// A command result is the outputs of a command (streams and status code)
/// within a struct which has convenience assertion functions about those outputs
2021-04-10 18:18:38 +00:00
#[ derive(Debug, Clone) ]
2015-11-16 05:25:01 +00:00
pub struct CmdResult {
2016-07-29 21:26:32 +00:00
//tmpd is used for convenience functions for asserts against fixtures
tmpd : Option < Rc < TempDir > > ,
2021-02-23 09:21:01 +00:00
/// exit status for command (if there is one)
2021-04-17 13:48:23 +00:00
code : Option < i32 > ,
2021-01-18 13:42:44 +00:00
/// zero-exit from running the Command?
/// see [`success`]
2021-04-22 20:37:44 +00:00
success : bool ,
2021-03-31 09:25:23 +00:00
/// captured standard output after running the Command
2021-04-22 20:37:44 +00:00
stdout : String ,
2021-03-31 09:25:23 +00:00
/// captured standard error after running the Command
2021-04-22 20:37:44 +00:00
stderr : String ,
2015-11-16 05:25:01 +00:00
}
2016-02-17 05:03:52 +00:00
impl CmdResult {
2021-03-31 09:25:23 +00:00
/// Returns a reference to the program's standard output as a slice of bytes
pub fn stdout ( & self ) -> & [ u8 ] {
& self . stdout . as_bytes ( )
}
/// Returns the program's standard output as a string slice
pub fn stdout_str ( & self ) -> & str {
& self . stdout
}
/// Returns the program's standard output as a string
/// consumes self
pub fn stdout_move_str ( self ) -> String {
self . stdout
}
/// Returns the program's standard output as a vec of bytes
/// consumes self
pub fn stdout_move_bytes ( self ) -> Vec < u8 > {
Vec ::from ( self . stdout )
}
/// Returns a reference to the program's standard error as a slice of bytes
pub fn stderr ( & self ) -> & [ u8 ] {
& self . stderr . as_bytes ( )
}
/// Returns the program's standard error as a string slice
pub fn stderr_str ( & self ) -> & str {
& self . stderr
}
/// Returns the program's standard error as a string
/// consumes self
pub fn stderr_move_str ( self ) -> String {
self . stderr
}
/// Returns the program's standard error as a vec of bytes
/// consumes self
pub fn stderr_move_bytes ( self ) -> Vec < u8 > {
Vec ::from ( self . stderr )
}
/// Returns the program's exit code
/// Panics if not run
pub fn code ( & self ) -> i32 {
self . code . expect ( " Program must be run first " )
}
2021-04-10 18:24:30 +00:00
pub fn code_is ( & self , expected_code : i32 ) -> & CmdResult {
assert_eq! ( self . code ( ) , expected_code ) ;
self
}
2021-03-31 09:25:23 +00:00
/// Returns the program's TempDir
/// Panics if not present
pub fn tmpd ( & self ) -> Rc < TempDir > {
match & self . tmpd {
Some ( ptr ) = > ptr . clone ( ) ,
2021-04-02 09:56:49 +00:00
None = > panic! ( " Command not associated with a TempDir " ) ,
2021-03-31 09:25:23 +00:00
}
}
/// Returns whether the program succeeded
pub fn succeeded ( & self ) -> bool {
self . success
}
2016-07-17 09:20:33 +00:00
/// asserts that the command resulted in a success (zero) status code
2021-03-31 09:25:23 +00:00
pub fn success ( & self ) -> & CmdResult {
2021-04-10 18:24:30 +00:00
if ! self . success {
panic! (
" Command was expected to succeed. \n stdout = {} \n stderr = {} " ,
self . stdout_str ( ) ,
self . stderr_str ( )
) ;
}
2021-03-31 09:25:23 +00:00
self
2016-02-17 05:03:52 +00:00
}
2016-03-29 01:13:40 +00:00
2016-07-17 09:20:33 +00:00
/// asserts that the command resulted in a failure (non-zero) status code
2021-03-31 09:25:23 +00:00
pub fn failure ( & self ) -> & CmdResult {
2021-04-10 18:24:30 +00:00
if self . success {
panic! (
" Command was expected to fail. \n stdout = {} \n stderr = {} " ,
self . stdout_str ( ) ,
self . stderr_str ( )
) ;
}
2021-03-31 09:25:23 +00:00
self
2016-02-17 05:03:52 +00:00
}
2016-08-06 03:18:34 +00:00
2021-02-23 09:21:01 +00:00
/// asserts that the command's exit code is the same as the given one
2021-03-31 09:25:23 +00:00
pub fn status_code ( & self , code : i32 ) -> & CmdResult {
2021-02-23 09:21:01 +00:00
assert! ( self . code = = Some ( code ) ) ;
2021-03-31 09:25:23 +00:00
self
2021-02-23 09:21:01 +00:00
}
2016-07-17 09:20:33 +00:00
/// asserts that the command resulted in empty (zero-length) stderr stream output
/// generally, it's better to use stdout_only() instead,
/// but you might find yourself using this function if
2021-04-07 09:48:21 +00:00
/// 1. you can not know exactly what stdout will be or
/// 2. you know that stdout will also be empty
2021-03-31 09:25:23 +00:00
pub fn no_stderr ( & self ) -> & CmdResult {
2021-04-10 18:24:30 +00:00
if ! self . stderr . is_empty ( ) {
panic! (
" Expected stderr to be empty, but it's: \n {} " ,
self . stderr_str ( )
) ;
}
2021-03-31 09:25:23 +00:00
self
2016-02-17 05:03:52 +00:00
}
2016-08-06 03:18:34 +00:00
2016-07-17 09:20:33 +00:00
/// asserts that the command resulted in empty (zero-length) stderr stream output
/// unless asserting there was neither stdout or stderr, stderr_only is usually a better choice
/// generally, it's better to use stderr_only() instead,
/// but you might find yourself using this function if
2021-04-07 09:48:21 +00:00
/// 1. you can not know exactly what stderr will be or
/// 2. you know that stderr will also be empty
2021-03-31 09:25:23 +00:00
pub fn no_stdout ( & self ) -> & CmdResult {
2021-04-10 18:24:30 +00:00
if ! self . stdout . is_empty ( ) {
panic! (
" Expected stdout to be empty, but it's: \n {} " ,
self . stderr_str ( )
) ;
}
2021-03-31 09:25:23 +00:00
self
2016-02-17 05:03:52 +00:00
}
2016-03-29 01:13:40 +00:00
2016-07-17 09:20:33 +00:00
/// asserts that the command resulted in stdout stream output that equals the
2019-02-07 20:54:48 +00:00
/// passed in value, trailing whitespace are kept to force strict comparison (#1235)
2016-07-17 09:20:33 +00:00
/// stdout_only is a better choice unless stderr may or will be non-empty
2021-03-31 09:25:23 +00:00
pub fn stdout_is < T : AsRef < str > > ( & self , msg : T ) -> & CmdResult {
2020-01-28 05:14:11 +00:00
assert_eq! ( self . stdout , String ::from ( msg . as_ref ( ) ) ) ;
2021-03-31 09:25:23 +00:00
self
}
2021-04-17 12:01:52 +00:00
/// Like `stdout_is` but newlines are normalized to `\n`.
pub fn normalized_newlines_stdout_is < T : AsRef < str > > ( & self , msg : T ) -> & CmdResult {
let msg = msg . as_ref ( ) . replace ( " \r \n " , " \n " ) ;
assert_eq! ( self . stdout . replace ( " \r \n " , " \n " ) , msg ) ;
self
}
2021-03-31 09:25:23 +00:00
/// asserts that the command resulted in stdout stream output,
/// whose bytes equal those of the passed in slice
pub fn stdout_is_bytes < T : AsRef < [ u8 ] > > ( & self , msg : T ) -> & CmdResult {
assert_eq! ( self . stdout . as_bytes ( ) , msg . as_ref ( ) ) ;
self
2016-02-17 05:03:52 +00:00
}
2016-08-06 03:18:34 +00:00
2016-07-29 21:26:32 +00:00
/// like stdout_is(...), but expects the contents of the file at the provided relative path
2021-03-31 09:25:23 +00:00
pub fn stdout_is_fixture < T : AsRef < OsStr > > ( & self , file_rel_path : T ) -> & CmdResult {
2016-07-29 21:26:32 +00:00
let contents = read_scenario_fixture ( & self . tmpd , file_rel_path ) ;
2021-04-24 14:43:13 +00:00
self . stdout_is ( String ::from_utf8 ( contents ) . unwrap ( ) )
2016-07-29 21:26:32 +00:00
}
2016-03-29 01:13:40 +00:00
2016-07-17 09:20:33 +00:00
/// asserts that the command resulted in stderr stream output that equals the
/// passed in value, when both are trimmed of trailing whitespace
/// stderr_only is a better choice unless stdout may or will be non-empty
2021-03-31 09:25:23 +00:00
pub fn stderr_is < T : AsRef < str > > ( & self , msg : T ) -> & CmdResult {
2018-03-12 08:20:58 +00:00
assert_eq! (
2019-12-17 04:09:20 +00:00
self . stderr . trim_end ( ) ,
String ::from ( msg . as_ref ( ) ) . trim_end ( )
2018-03-12 08:20:58 +00:00
) ;
2021-03-31 09:25:23 +00:00
self
}
/// asserts that the command resulted in stderr stream output,
/// whose bytes equal those of the passed in slice
pub fn stderr_is_bytes < T : AsRef < [ u8 ] > > ( & self , msg : T ) -> & CmdResult {
assert_eq! ( self . stderr . as_bytes ( ) , msg . as_ref ( ) ) ;
self
2016-02-17 05:03:52 +00:00
}
2016-03-29 01:13:40 +00:00
2021-04-10 20:19:53 +00:00
/// Like stdout_is_fixture, but for stderr
pub fn stderr_is_fixture < T : AsRef < OsStr > > ( & self , file_rel_path : T ) -> & CmdResult {
let contents = read_scenario_fixture ( & self . tmpd , file_rel_path ) ;
2021-04-24 14:43:13 +00:00
self . stderr_is ( String ::from_utf8 ( contents ) . unwrap ( ) )
2021-04-10 20:19:53 +00:00
}
2016-07-17 09:20:33 +00:00
/// asserts that
2021-04-07 09:48:21 +00:00
/// 1. the command resulted in stdout stream output that equals the
/// passed in value
/// 2. the command resulted in empty (zero-length) stderr stream output
2021-03-31 09:25:23 +00:00
pub fn stdout_only < T : AsRef < str > > ( & self , msg : T ) -> & CmdResult {
2016-08-08 06:54:13 +00:00
self . no_stderr ( ) . stdout_is ( msg )
2016-02-17 05:03:52 +00:00
}
2016-03-29 01:13:40 +00:00
2021-03-31 09:25:23 +00:00
/// asserts that
/// 1. the command resulted in a stdout stream whose bytes
/// equal those of the passed in value
/// 2. the command resulted in an empty stderr stream
pub fn stdout_only_bytes < T : AsRef < [ u8 ] > > ( & self , msg : T ) -> & CmdResult {
self . no_stderr ( ) . stdout_is_bytes ( msg )
}
2016-07-29 21:26:32 +00:00
/// like stdout_only(...), but expects the contents of the file at the provided relative path
2021-03-31 09:25:23 +00:00
pub fn stdout_only_fixture < T : AsRef < OsStr > > ( & self , file_rel_path : T ) -> & CmdResult {
2016-07-29 21:26:32 +00:00
let contents = read_scenario_fixture ( & self . tmpd , file_rel_path ) ;
2021-03-31 09:25:23 +00:00
self . stdout_only_bytes ( contents )
2016-07-29 21:26:32 +00:00
}
2016-07-17 09:20:33 +00:00
/// asserts that
2021-04-07 09:48:21 +00:00
/// 1. the command resulted in stderr stream output that equals the
/// passed in value, when both are trimmed of trailing whitespace
/// 2. the command resulted in empty (zero-length) stdout stream output
2021-03-31 09:25:23 +00:00
pub fn stderr_only < T : AsRef < str > > ( & self , msg : T ) -> & CmdResult {
2016-08-08 06:54:13 +00:00
self . no_stdout ( ) . stderr_is ( msg )
2016-02-17 05:03:52 +00:00
}
2016-03-29 01:13:40 +00:00
2021-03-31 09:25:23 +00:00
/// asserts that
/// 1. the command resulted in a stderr stream whose bytes equal the ones
/// of the passed value
/// 2. the command resulted in an empty stdout stream
pub fn stderr_only_bytes < T : AsRef < [ u8 ] > > ( & self , msg : T ) -> & CmdResult {
self . no_stderr ( ) . stderr_is_bytes ( msg )
}
pub fn fails_silently ( & self ) -> & CmdResult {
2016-02-17 05:03:52 +00:00
assert! ( ! self . success ) ;
2021-03-31 09:25:23 +00:00
assert! ( self . stderr . is_empty ( ) ) ;
self
}
pub fn stdout_contains < T : AsRef < str > > ( & self , cmp : T ) -> & CmdResult {
assert! ( self . stdout_str ( ) . contains ( cmp . as_ref ( ) ) ) ;
self
}
2021-04-10 18:18:38 +00:00
pub fn stderr_contains < T : AsRef < str > > ( & self , cmp : T ) -> & CmdResult {
2021-03-31 09:25:23 +00:00
assert! ( self . stderr_str ( ) . contains ( cmp . as_ref ( ) ) ) ;
self
2016-02-17 05:03:52 +00:00
}
2021-04-10 18:24:30 +00:00
pub fn stdout_does_not_contain < T : AsRef < str > > ( & self , cmp : T ) -> & CmdResult {
assert! ( ! self . stdout_str ( ) . contains ( cmp . as_ref ( ) ) ) ;
self
}
2021-04-10 18:18:38 +00:00
pub fn stderr_does_not_contain < T : AsRef < str > > ( & self , cmp : T ) -> & CmdResult {
2021-04-10 18:24:30 +00:00
assert! ( ! self . stderr_str ( ) . contains ( cmp . as_ref ( ) ) ) ;
self
}
pub fn stdout_matches ( & self , regex : & regex ::Regex ) -> & CmdResult {
2021-04-22 20:37:44 +00:00
if ! regex . is_match ( self . stdout_str ( ) . trim ( ) ) {
2021-04-10 18:24:30 +00:00
panic! ( " Stdout does not match regex: \n {} " , self . stdout_str ( ) )
}
self
}
pub fn stdout_does_not_match ( & self , regex : & regex ::Regex ) -> & CmdResult {
2021-04-22 20:37:44 +00:00
if regex . is_match ( self . stdout_str ( ) . trim ( ) ) {
2021-04-10 18:24:30 +00:00
panic! ( " Stdout matches regex: \n {} " , self . stdout_str ( ) )
}
self
}
2016-02-17 05:03:52 +00:00
}
2015-11-16 05:25:01 +00:00
pub fn log_info < T : AsRef < str > , U : AsRef < str > > ( msg : T , par : U ) {
println! ( " {} : {} " , msg . as_ref ( ) , par . as_ref ( ) ) ;
}
pub fn recursive_copy ( src : & Path , dest : & Path ) -> Result < ( ) > {
2019-04-28 09:00:19 +00:00
if fs ::metadata ( src ) ? . is_dir ( ) {
2019-12-08 04:02:50 +00:00
for entry in fs ::read_dir ( src ) ? {
2019-04-28 09:00:19 +00:00
let entry = entry ? ;
2015-11-16 05:25:01 +00:00
let mut new_dest = PathBuf ::from ( dest ) ;
new_dest . push ( entry . file_name ( ) ) ;
2019-04-28 09:00:19 +00:00
if fs ::metadata ( entry . path ( ) ) ? . is_dir ( ) {
fs ::create_dir ( & new_dest ) ? ;
recursive_copy ( & entry . path ( ) , & new_dest ) ? ;
2015-11-16 05:25:01 +00:00
} else {
2019-04-28 09:00:19 +00:00
fs ::copy ( & entry . path ( ) , new_dest ) ? ;
2015-11-16 05:25:01 +00:00
}
}
}
Ok ( ( ) )
}
2016-03-29 01:06:31 +00:00
pub fn get_root_path ( ) -> & 'static str {
if cfg! ( windows ) {
" C: \\ "
} else {
" / "
}
}
2016-07-17 09:20:33 +00:00
/// Object-oriented path struct that represents and operates on
/// paths relative to the directory it was constructed for.
2016-08-23 12:57:13 +00:00
#[ derive(Clone) ]
2015-11-16 05:25:01 +00:00
pub struct AtPath {
pub subdir : PathBuf ,
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
impl AtPath {
pub fn new ( subdir : & Path ) -> AtPath {
2018-03-12 08:20:58 +00:00
AtPath {
subdir : PathBuf ::from ( subdir ) ,
}
2015-11-16 05:25:01 +00:00
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn as_string ( & self ) -> String {
self . subdir . to_str ( ) . unwrap ( ) . to_owned ( )
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn plus ( & self , name : & str ) -> PathBuf {
let mut pathbuf = self . subdir . clone ( ) ;
pathbuf . push ( name ) ;
pathbuf
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn plus_as_string ( & self , name : & str ) -> String {
String ::from ( self . plus ( name ) . to_str ( ) . unwrap ( ) )
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
fn minus ( & self , name : & str ) -> PathBuf {
let prefixed = PathBuf ::from ( name ) ;
if prefixed . starts_with ( & self . subdir ) {
let mut unprefixed = PathBuf ::new ( ) ;
2018-03-12 08:20:58 +00:00
for component in prefixed . components ( ) . skip ( self . subdir . components ( ) . count ( ) ) {
2018-01-16 22:58:35 +00:00
unprefixed . push ( component . as_os_str ( ) . to_str ( ) . unwrap ( ) ) ;
2015-11-16 05:25:01 +00:00
}
unprefixed
} else {
prefixed
}
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn minus_as_string ( & self , name : & str ) -> String {
String ::from ( self . minus ( name ) . to_str ( ) . unwrap ( ) )
}
2016-03-29 01:13:40 +00:00
2021-04-10 08:41:59 +00:00
pub fn set_readonly ( & self , name : & str ) {
let metadata = fs ::metadata ( self . plus ( name ) ) . unwrap ( ) ;
let mut permissions = metadata . permissions ( ) ;
permissions . set_readonly ( true ) ;
fs ::set_permissions ( self . plus ( name ) , permissions ) . unwrap ( ) ;
}
2015-11-16 05:25:01 +00:00
pub fn open ( & self , name : & str ) -> File {
log_info ( " open " , self . plus_as_string ( name ) ) ;
File ::open ( self . plus ( name ) ) . unwrap ( )
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn read ( & self , name : & str ) -> String {
let mut f = self . open ( name ) ;
let mut contents = String ::new ( ) ;
2021-03-31 10:54:16 +00:00
f . read_to_string ( & mut contents )
. unwrap_or_else ( | e | panic! ( " Couldn't read {} : {} " , name , e ) ) ;
2015-11-16 05:25:01 +00:00
contents
}
2016-03-29 01:13:40 +00:00
2021-03-31 09:25:23 +00:00
pub fn read_bytes ( & self , name : & str ) -> Vec < u8 > {
let mut f = self . open ( name ) ;
let mut contents = Vec ::new ( ) ;
f . read_to_end ( & mut contents )
. unwrap_or_else ( | e | panic! ( " Couldn't read {} : {} " , name , e ) ) ;
contents
}
2015-11-16 05:25:01 +00:00
pub fn write ( & self , name : & str , contents : & str ) {
2021-03-12 21:26:27 +00:00
log_info ( " open(write) " , self . plus_as_string ( name ) ) ;
2021-03-31 10:54:16 +00:00
std ::fs ::write ( self . plus ( name ) , contents )
. unwrap_or_else ( | e | panic! ( " Couldn't write {} : {} " , name , e ) ) ;
2015-11-16 05:25:01 +00:00
}
2016-03-29 01:13:40 +00:00
2021-03-31 09:25:23 +00:00
pub fn write_bytes ( & self , name : & str , contents : & [ u8 ] ) {
log_info ( " open(write) " , self . plus_as_string ( name ) ) ;
std ::fs ::write ( self . plus ( name ) , contents )
. unwrap_or_else ( | e | panic! ( " Couldn't write {} : {} " , name , e ) ) ;
}
2016-04-02 21:09:20 +00:00
pub fn append ( & self , name : & str , contents : & str ) {
log_info ( " open(append) " , self . plus_as_string ( name ) ) ;
2018-03-12 08:20:58 +00:00
let mut f = OpenOptions ::new ( )
. write ( true )
. append ( true )
. open ( self . plus ( name ) )
. unwrap ( ) ;
2021-03-31 10:54:16 +00:00
f . write ( contents . as_bytes ( ) )
. unwrap_or_else ( | e | panic! ( " Couldn't write {} : {} " , name , e ) ) ;
2016-04-02 21:09:20 +00:00
}
2021-03-31 09:25:23 +00:00
pub fn append_bytes ( & self , name : & str , contents : & [ u8 ] ) {
log_info ( " open(append) " , self . plus_as_string ( name ) ) ;
let mut f = OpenOptions ::new ( )
. write ( true )
. append ( true )
. open ( self . plus ( name ) )
. unwrap ( ) ;
f . write_all ( contents )
. unwrap_or_else ( | e | panic! ( " Couldn't append to {} : {} " , name , e ) ) ;
}
2015-11-16 05:25:01 +00:00
pub fn mkdir ( & self , dir : & str ) {
log_info ( " mkdir " , self . plus_as_string ( dir ) ) ;
fs ::create_dir ( & self . plus ( dir ) ) . unwrap ( ) ;
}
pub fn mkdir_all ( & self , dir : & str ) {
log_info ( " mkdir_all " , self . plus_as_string ( dir ) ) ;
fs ::create_dir_all ( self . plus ( dir ) ) . unwrap ( ) ;
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn make_file ( & self , name : & str ) -> File {
match File ::create ( & self . plus ( name ) ) {
Ok ( f ) = > f ,
Err ( e ) = > panic! ( " {} " , e ) ,
}
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn touch ( & self , file : & str ) {
log_info ( " touch " , self . plus_as_string ( file ) ) ;
File ::create ( & self . plus ( file ) ) . unwrap ( ) ;
}
2016-03-29 01:13:40 +00:00
2021-03-29 11:10:13 +00:00
#[ cfg(not(windows)) ]
pub fn mkfifo ( & self , fifo : & str ) {
let full_path = self . plus_as_string ( fifo ) ;
log_info ( " mkfifo " , & full_path ) ;
unsafe {
let fifo_name : CString = CString ::new ( full_path ) . expect ( " CString creation failed. " ) ;
libc ::mkfifo ( fifo_name . as_ptr ( ) , libc ::S_IWUSR | libc ::S_IRUSR ) ;
}
}
#[ cfg(not(windows)) ]
pub fn is_fifo ( & self , fifo : & str ) -> bool {
unsafe {
let name = CString ::new ( self . plus_as_string ( fifo ) ) . unwrap ( ) ;
let mut stat : libc ::stat = std ::mem ::zeroed ( ) ;
if libc ::stat ( name . as_ptr ( ) , & mut stat ) > = 0 {
libc ::S_IFIFO & stat . st_mode ! = 0
} else {
false
}
}
}
2018-11-13 04:55:25 +00:00
pub fn symlink_file ( & self , src : & str , dst : & str ) {
2018-03-12 08:20:58 +00:00
log_info (
" symlink " ,
& format! ( " {} , {} " , self . plus_as_string ( src ) , self . plus_as_string ( dst ) ) ,
) ;
2015-11-16 05:25:01 +00:00
symlink_file ( & self . plus ( src ) , & self . plus ( dst ) ) . unwrap ( ) ;
}
2016-03-29 01:13:40 +00:00
2018-11-13 04:55:25 +00:00
pub fn symlink_dir ( & self , src : & str , dst : & str ) {
log_info (
" symlink " ,
& format! ( " {} , {} " , self . plus_as_string ( src ) , self . plus_as_string ( dst ) ) ,
) ;
symlink_dir ( & self . plus ( src ) , & self . plus ( dst ) ) . unwrap ( ) ;
}
2015-11-16 05:25:01 +00:00
pub fn is_symlink ( & self , path : & str ) -> bool {
log_info ( " is_symlink " , self . plus_as_string ( path ) ) ;
match fs ::symlink_metadata ( & self . plus ( path ) ) {
Ok ( m ) = > m . file_type ( ) . is_symlink ( ) ,
Err ( _ ) = > false ,
}
}
pub fn resolve_link ( & self , path : & str ) -> String {
log_info ( " resolve_link " , self . plus_as_string ( path ) ) ;
match fs ::read_link ( & self . plus ( path ) ) {
2018-03-12 08:20:58 +00:00
Ok ( p ) = > self . minus_as_string ( p . to_str ( ) . unwrap ( ) ) ,
2015-11-16 05:25:01 +00:00
Err ( _ ) = > " " . to_string ( ) ,
}
}
2016-08-27 23:12:58 +00:00
pub fn symlink_metadata ( & self , path : & str ) -> fs ::Metadata {
match fs ::symlink_metadata ( & self . plus ( path ) ) {
Ok ( m ) = > m ,
Err ( e ) = > panic! ( " {} " , e ) ,
}
}
2015-11-16 05:25:01 +00:00
pub fn metadata ( & self , path : & str ) -> fs ::Metadata {
match fs ::metadata ( & self . plus ( path ) ) {
Ok ( m ) = > m ,
Err ( e ) = > panic! ( " {} " , e ) ,
}
}
pub fn file_exists ( & self , path : & str ) -> bool {
match fs ::metadata ( & self . plus ( path ) ) {
Ok ( m ) = > m . is_file ( ) ,
Err ( _ ) = > false ,
}
}
pub fn dir_exists ( & self , path : & str ) -> bool {
match fs ::metadata ( & self . plus ( path ) ) {
Ok ( m ) = > m . is_dir ( ) ,
Err ( _ ) = > false ,
}
}
2016-01-10 10:47:57 +00:00
pub fn root_dir_resolved ( & self ) -> String {
log_info ( " current_directory_resolved " , " " ) ;
2020-01-28 05:14:11 +00:00
let s = self
. subdir
2018-03-12 08:20:58 +00:00
. canonicalize ( )
. unwrap ( )
. to_str ( )
. unwrap ( )
. to_owned ( ) ;
2016-03-27 07:05:55 +00:00
// Due to canonicalize()'s use of GetFinalPathNameByHandleW() on Windows, the resolved path
// starts with '\\?\' to extend the limit of a given path to 32,767 wide characters.
//
// To address this issue, we remove this prepended string if available.
//
// Source:
// http://stackoverflow.com/questions/31439011/getfinalpathnamebyhandle-without-prepended
let prefix = " \\ \\ ? \\ " ;
if s . starts_with ( prefix ) {
String ::from ( & s [ prefix . len ( ) .. ] )
} else {
s
}
2016-01-10 10:47:57 +00:00
}
2015-11-16 05:25:01 +00:00
}
2016-07-17 09:20:33 +00:00
/// An environment for running a single uutils test case, serves three functions:
/// 1. centralizes logic for locating the uutils binary and calling the utility
2021-03-22 10:13:38 +00:00
/// 2. provides a unique temporary directory for the test case
2016-07-17 09:20:33 +00:00
/// 3. copies over fixtures for the utility to the temporary directory
2021-03-22 10:13:38 +00:00
///
/// Fixtures can be found under `tests/fixtures/$util_name/`
2016-07-29 21:26:32 +00:00
pub struct TestScenario {
2015-11-16 05:25:01 +00:00
bin_path : PathBuf ,
util_name : String ,
pub fixtures : AtPath ,
tmpd : Rc < TempDir > ,
}
2016-03-29 01:13:40 +00:00
2016-07-29 21:26:32 +00:00
impl TestScenario {
pub fn new ( util_name : & str ) -> TestScenario {
2020-06-09 09:30:19 +00:00
let tmpd = Rc ::new ( TempDir ::new ( ) . unwrap ( ) ) ;
2016-07-29 21:26:32 +00:00
let ts = TestScenario {
2015-11-16 05:25:01 +00:00
bin_path : {
2016-05-22 19:09:04 +00:00
// Instead of hardcoding the path relative to the current
// directory, use Cargo's OUT_DIR to find path to executable.
// This allows tests to be run using profiles other than debug.
2016-12-19 19:21:42 +00:00
let target_dir = path_concat! ( env! ( " OUT_DIR " ) , " .. " , " .. " , " .. " , PROGNAME ) ;
2016-11-25 19:14:46 +00:00
PathBuf ::from ( AtPath ::new ( Path ::new ( & target_dir ) ) . root_dir_resolved ( ) )
2015-11-16 05:25:01 +00:00
} ,
util_name : String ::from ( util_name ) ,
2016-11-25 19:14:46 +00:00
fixtures : AtPath ::new ( tmpd . as_ref ( ) . path ( ) ) ,
2021-04-24 14:43:13 +00:00
tmpd ,
2015-11-16 05:25:01 +00:00
} ;
let mut fixture_path_builder = env ::current_dir ( ) . unwrap ( ) ;
2016-05-22 19:09:04 +00:00
fixture_path_builder . push ( TESTS_DIR ) ;
fixture_path_builder . push ( FIXTURES_DIR ) ;
fixture_path_builder . push ( util_name ) ;
2016-11-25 19:14:46 +00:00
if let Ok ( m ) = fs ::metadata ( & fixture_path_builder ) {
if m . is_dir ( ) {
2015-11-16 05:25:01 +00:00
recursive_copy ( & fixture_path_builder , & ts . fixtures . subdir ) . unwrap ( ) ;
2016-11-25 19:14:46 +00:00
}
2015-11-16 05:25:01 +00:00
}
ts
}
2016-03-29 01:13:40 +00:00
2021-03-22 10:13:38 +00:00
/// Returns builder for invoking the target uutils binary. Paths given are
/// treated relative to the environment's unique temporary test directory.
2016-07-29 21:26:32 +00:00
pub fn ucmd ( & self ) -> UCommand {
2015-11-16 05:25:01 +00:00
let mut cmd = self . cmd ( & self . bin_path ) ;
cmd . arg ( & self . util_name ) ;
cmd
}
2016-03-29 01:13:40 +00:00
2021-03-22 10:13:38 +00:00
/// Returns builder for invoking any system command. Paths given are treated
/// relative to the environment's unique temporary test directory.
2015-11-16 05:25:01 +00:00
pub fn cmd < S : AsRef < OsStr > > ( & self , bin : S ) -> UCommand {
UCommand ::new_from_tmp ( bin , self . tmpd . clone ( ) , true )
}
2016-03-29 01:13:40 +00:00
2021-04-01 21:16:47 +00:00
/// Returns builder for invoking any uutils command. Paths given are treated
/// relative to the environment's unique temporary test directory.
pub fn ccmd < S : AsRef < OsStr > > ( & self , bin : S ) -> UCommand {
let mut cmd = self . cmd ( & self . bin_path ) ;
cmd . arg ( bin ) ;
cmd
}
2015-11-16 05:25:01 +00:00
// different names are used rather than an argument
// because the need to keep the environment is exceedingly rare.
2016-07-29 21:26:32 +00:00
pub fn ucmd_keepenv ( & self ) -> UCommand {
2015-11-16 05:25:01 +00:00
let mut cmd = self . cmd_keepenv ( & self . bin_path ) ;
cmd . arg ( & self . util_name ) ;
cmd
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn cmd_keepenv < S : AsRef < OsStr > > ( & self , bin : S ) -> UCommand {
UCommand ::new_from_tmp ( bin , self . tmpd . clone ( ) , false )
}
}
2016-11-25 19:14:46 +00:00
/// A `UCommand` is a wrapper around an individual Command that provides several additional features
2016-07-17 09:20:33 +00:00
/// 1. it has convenience functions that are more ergonomic to use for piping in stdin, spawning the command
/// and asserting on the results.
/// 2. it tracks arguments provided so that in test cases which may provide variations of an arg in loops
/// the test failure can display the exact call which preceded an assertion failure.
/// 3. it provides convenience construction arguments to set the Command working directory and/or clear its environment.
2018-11-21 16:49:12 +00:00
#[ derive(Debug) ]
2015-11-16 05:25:01 +00:00
pub struct UCommand {
pub raw : Command ,
comm_string : String ,
tmpd : Option < Rc < TempDir > > ,
has_run : bool ,
2021-04-09 09:08:31 +00:00
ignore_stdin_write_error : bool ,
2021-04-22 20:37:44 +00:00
stdin : Option < Stdio > ,
stdout : Option < Stdio > ,
stderr : Option < Stdio > ,
bytes_into_stdin : Option < Vec < u8 > > ,
2015-11-16 05:25:01 +00:00
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
impl UCommand {
pub fn new < T : AsRef < OsStr > , U : AsRef < OsStr > > ( arg : T , curdir : U , env_clear : bool ) -> UCommand {
UCommand {
tmpd : None ,
has_run : false ,
raw : {
let mut cmd = Command ::new ( arg . as_ref ( ) ) ;
cmd . current_dir ( curdir . as_ref ( ) ) ;
if env_clear {
2016-02-14 15:34:39 +00:00
if cfg! ( windows ) {
// %SYSTEMROOT% is required on Windows to initialize crypto provider
// ... and crypto provider is required for std::rand
// From procmon: RegQueryValue HKLM\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\Microsoft Strong Cryptographic Provider\Image Path
// SUCCESS Type: REG_SZ, Length: 66, Data: %SystemRoot%\system32\rsaenh.dll"
for ( key , _ ) in env ::vars_os ( ) {
if key . as_os_str ( ) ! = " SYSTEMROOT " {
cmd . env_remove ( key ) ;
}
}
} else {
cmd . env_clear ( ) ;
}
2015-11-16 05:25:01 +00:00
}
cmd
} ,
comm_string : String ::from ( arg . as_ref ( ) . to_str ( ) . unwrap ( ) ) ,
2021-04-09 09:08:31 +00:00
ignore_stdin_write_error : false ,
2021-04-22 20:37:44 +00:00
bytes_into_stdin : None ,
stdin : None ,
stdout : None ,
stderr : None ,
2015-11-16 05:25:01 +00:00
}
}
2016-03-29 01:13:40 +00:00
2015-11-16 05:25:01 +00:00
pub fn new_from_tmp < T : AsRef < OsStr > > ( arg : T , tmpd : Rc < TempDir > , env_clear : bool ) -> UCommand {
let tmpd_path_buf = String ::from ( & ( * tmpd . as_ref ( ) . path ( ) . to_str ( ) . unwrap ( ) ) ) ;
let mut ucmd : UCommand = UCommand ::new ( arg . as_ref ( ) , tmpd_path_buf , env_clear ) ;
ucmd . tmpd = Some ( tmpd ) ;
ucmd
}
2016-03-29 01:13:40 +00:00
2021-04-22 20:37:44 +00:00
pub fn set_stdin < T : Into < Stdio > > ( & mut self , stdin : T ) -> & mut UCommand {
self . stdin = Some ( stdin . into ( ) ) ;
self
}
pub fn set_stdout < T : Into < Stdio > > ( & mut self , stdout : T ) -> & mut UCommand {
self . stdout = Some ( stdout . into ( ) ) ;
self
}
pub fn set_stderr < T : Into < Stdio > > ( & mut self , stderr : T ) -> & mut UCommand {
self . stderr = Some ( stderr . into ( ) ) ;
self
}
2021-03-22 10:13:38 +00:00
/// Add a parameter to the invocation. Path arguments are treated relative
/// to the test environment directory.
2021-03-31 09:25:23 +00:00
pub fn arg < S : AsRef < OsStr > > ( & mut self , arg : S ) -> & mut UCommand {
2015-11-16 05:25:01 +00:00
if self . has_run {
2021-03-31 11:30:06 +00:00
panic! ( " {} " , ALREADY_RUN ) ;
2015-11-16 05:25:01 +00:00
}
self . comm_string . push_str ( " " ) ;
2021-04-25 21:28:42 +00:00
self . comm_string
. push_str ( arg . as_ref ( ) . to_str ( ) . unwrap_or_default ( ) ) ;
2015-11-16 05:25:01 +00:00
self . raw . arg ( arg . as_ref ( ) ) ;
2021-03-31 09:25:23 +00:00
self
2015-11-16 05:25:01 +00:00
}
2021-03-22 10:13:38 +00:00
/// Add multiple parameters to the invocation. Path arguments are treated relative
/// to the test environment directory.
2021-03-31 09:25:23 +00:00
pub fn args < S : AsRef < OsStr > > ( & mut self , args : & [ S ] ) -> & mut UCommand {
2015-11-16 05:25:01 +00:00
if self . has_run {
2021-03-31 11:30:06 +00:00
panic! ( " {} " , MULTIPLE_STDIN_MEANINGLESS ) ;
2015-11-16 05:25:01 +00:00
}
2021-04-25 21:28:42 +00:00
let strings = args
. iter ( )
. map ( | s | s . as_ref ( ) . to_os_string ( ) )
. collect_str ( InvalidEncodingHandling ::Ignore )
. accept_any ( ) ;
for s in strings {
2015-11-16 05:25:01 +00:00
self . comm_string . push_str ( " " ) ;
2021-04-25 21:28:42 +00:00
self . comm_string . push_str ( & s ) ;
2015-11-16 05:25:01 +00:00
}
self . raw . args ( args . as_ref ( ) ) ;
2021-03-31 09:25:23 +00:00
self
2015-11-16 05:25:01 +00:00
}
2016-01-03 13:16:12 +00:00
2016-07-17 09:20:33 +00:00
/// provides stdinput to feed in to the command when spawned
2021-03-31 09:25:23 +00:00
pub fn pipe_in < T : Into < Vec < u8 > > > ( & mut self , input : T ) -> & mut UCommand {
2021-04-22 20:37:44 +00:00
if self . bytes_into_stdin . is_some ( ) {
2021-03-31 11:30:06 +00:00
panic! ( " {} " , MULTIPLE_STDIN_MEANINGLESS ) ;
2016-07-17 09:20:33 +00:00
}
2021-04-22 20:37:44 +00:00
self . bytes_into_stdin = Some ( input . into ( ) ) ;
2021-03-31 09:25:23 +00:00
self
2016-07-17 09:20:33 +00:00
}
2016-07-29 21:26:32 +00:00
/// like pipe_in(...), but uses the contents of the file at the provided relative path as the piped in data
2021-03-31 09:25:23 +00:00
pub fn pipe_in_fixture < S : AsRef < OsStr > > ( & mut self , file_rel_path : S ) -> & mut UCommand {
2016-07-29 21:26:32 +00:00
let contents = read_scenario_fixture ( & self . tmpd , file_rel_path ) ;
self . pipe_in ( contents )
}
2021-04-09 09:08:31 +00:00
/// Ignores error caused by feeding stdin to the command.
/// This is typically useful to test non-standard workflows
/// like feeding something to a command that does not read it
pub fn ignore_stdin_write_error ( & mut self ) -> & mut UCommand {
2021-04-22 20:37:44 +00:00
if self . bytes_into_stdin . is_none ( ) {
2021-04-09 09:08:31 +00:00
panic! ( " {} " , NO_STDIN_MEANINGLESS ) ;
}
self . ignore_stdin_write_error = true ;
self
}
2021-03-31 09:25:23 +00:00
pub fn env < K , V > ( & mut self , key : K , val : V ) -> & mut UCommand
2018-03-12 08:20:58 +00:00
where
K : AsRef < OsStr > ,
V : AsRef < OsStr > ,
{
2016-01-03 13:16:12 +00:00
if self . has_run {
2021-03-31 11:30:06 +00:00
panic! ( " {} " , ALREADY_RUN ) ;
2016-01-03 13:16:12 +00:00
}
self . raw . env ( key , val ) ;
2021-03-31 09:25:23 +00:00
self
2016-01-03 13:16:12 +00:00
}
2016-07-17 09:20:33 +00:00
/// Spawns the command, feeds the stdin if any, and returns the
/// child process immediately.
2016-04-02 14:41:59 +00:00
pub fn run_no_wait ( & mut self ) -> Child {
2016-02-17 05:03:52 +00:00
if self . has_run {
2021-03-31 11:30:06 +00:00
panic! ( " {} " , ALREADY_RUN ) ;
2016-02-17 05:03:52 +00:00
}
2015-11-16 05:25:01 +00:00
self . has_run = true ;
log_info ( " run " , & self . comm_string ) ;
2021-04-09 09:08:31 +00:00
let mut child = self
2020-01-28 05:14:11 +00:00
. raw
2021-04-22 20:37:44 +00:00
. stdin ( self . stdin . take ( ) . unwrap_or_else ( | | Stdio ::piped ( ) ) )
. stdout ( self . stdout . take ( ) . unwrap_or_else ( | | Stdio ::piped ( ) ) )
. stderr ( self . stderr . take ( ) . unwrap_or_else ( | | Stdio ::piped ( ) ) )
2016-04-02 14:23:26 +00:00
. spawn ( )
. unwrap ( ) ;
2021-04-22 20:37:44 +00:00
if let Some ( ref input ) = self . bytes_into_stdin {
2021-04-09 09:08:31 +00:00
let write_result = child
2018-03-12 08:20:58 +00:00
. stdin
2016-04-02 14:23:26 +00:00
. take ( )
2018-03-12 08:20:58 +00:00
. unwrap_or_else ( | | panic! ( " Could not take child process stdin " ) )
2021-04-09 09:08:31 +00:00
. write_all ( input ) ;
if ! self . ignore_stdin_write_error {
if let Err ( e ) = write_result {
panic! ( " failed to write to stdin of child: {} " , e )
}
}
2016-04-02 14:23:26 +00:00
}
2021-04-09 09:08:31 +00:00
child
2016-04-02 14:41:59 +00:00
}
/// Spawns the command, feeds the stdin if any, waits for the result
2016-07-17 09:20:33 +00:00
/// and returns a command result.
/// It is recommended that you instead use succeeds() or fails()
2016-04-02 14:41:59 +00:00
pub fn run ( & mut self ) -> CmdResult {
let prog = self . run_no_wait ( ) . wait_with_output ( ) . unwrap ( ) ;
2016-04-02 14:23:26 +00:00
2015-11-16 05:25:01 +00:00
CmdResult {
2016-07-29 21:26:32 +00:00
tmpd : self . tmpd . clone ( ) ,
2021-02-23 09:21:01 +00:00
code : prog . status . code ( ) ,
2015-11-16 05:25:01 +00:00
success : prog . status . success ( ) ,
stdout : from_utf8 ( & prog . stdout ) . unwrap ( ) . to_string ( ) ,
stderr : from_utf8 ( & prog . stderr ) . unwrap ( ) . to_string ( ) ,
}
}
2016-03-29 01:13:40 +00:00
2016-07-17 09:20:33 +00:00
/// Spawns the command, feeding the passed in stdin, waits for the result
/// and returns a command result.
/// It is recommended that, instead of this, you use a combination of pipe_in()
/// with succeeds() or fails()
2016-02-17 05:03:52 +00:00
pub fn run_piped_stdin < T : Into < Vec < u8 > > > ( & mut self , input : T ) -> CmdResult {
self . pipe_in ( input ) . run ( )
}
2016-03-29 01:13:40 +00:00
2016-07-17 09:20:33 +00:00
/// Spawns the command, feeds the stdin if any, waits for the result,
/// asserts success, and returns a command result.
2016-02-17 05:03:52 +00:00
pub fn succeeds ( & mut self ) -> CmdResult {
let cmd_result = self . run ( ) ;
cmd_result . success ( ) ;
cmd_result
}
2016-03-29 01:13:40 +00:00
2016-07-17 09:20:33 +00:00
/// Spawns the command, feeds the stdin if any, waits for the result,
2019-04-28 09:12:37 +00:00
/// asserts failure, and returns a command result.
2016-02-17 05:03:52 +00:00
pub fn fails ( & mut self ) -> CmdResult {
let cmd_result = self . run ( ) ;
cmd_result . failure ( ) ;
cmd_result
2015-11-16 05:25:01 +00:00
}
}
2016-04-02 21:09:20 +00:00
pub fn read_size ( child : & mut Child , size : usize ) -> String {
let mut output = Vec ::new ( ) ;
output . resize ( size , 0 ) ;
2016-06-17 14:45:35 +00:00
sleep ( Duration ::from_secs ( 1 ) ) ;
2018-03-12 08:20:58 +00:00
child
. stdout
. as_mut ( )
. unwrap ( )
2021-03-31 10:54:16 +00:00
. read_exact ( output . as_mut_slice ( ) )
2018-03-12 08:20:58 +00:00
. unwrap ( ) ;
2016-04-02 21:09:20 +00:00
String ::from_utf8 ( output ) . unwrap ( )
}
2021-04-10 20:19:53 +00:00
pub fn vec_of_size ( n : usize ) -> Vec < u8 > {
let mut result = Vec ::new ( ) ;
for _ in 0 .. n {
result . push ( 'a' as u8 ) ;
}
assert_eq! ( result . len ( ) , n ) ;
result
}
2021-04-10 18:18:38 +00:00
/// Sanity checks for test utils
#[ cfg(test) ]
mod tests {
use super ::* ;
#[ test ]
fn test_code_is ( ) {
let res = CmdResult {
tmpd : None ,
code : Some ( 32 ) ,
success : false ,
stdout : " " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . code_is ( 32 ) ;
}
#[ test ]
#[ should_panic ]
fn test_code_is_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : Some ( 32 ) ,
success : false ,
stdout : " " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . code_is ( 1 ) ;
}
#[ test ]
fn test_failure ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : false ,
stdout : " " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . failure ( ) ;
}
#[ test ]
#[ should_panic ]
fn test_failure_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . failure ( ) ;
}
#[ test ]
fn test_success ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . success ( ) ;
}
#[ test ]
#[ should_panic ]
fn test_success_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : false ,
stdout : " " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . success ( ) ;
}
#[ test ]
fn test_no_std_errout ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . no_stderr ( ) ;
res . no_stdout ( ) ;
}
#[ test ]
#[ should_panic ]
fn test_no_stderr_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " " . into ( ) ,
stderr : " asdfsadfa " . into ( ) ,
} ;
res . no_stderr ( ) ;
}
#[ test ]
#[ should_panic ]
fn test_no_stdout_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " asdfsadfa " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . no_stdout ( ) ;
}
#[ test ]
fn test_std_does_not_contain ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " This is a likely error message \n " . into ( ) ,
stderr : " This is a likely error message \n " . into ( ) ,
} ;
res . stdout_does_not_contain ( " unlikely " ) ;
res . stderr_does_not_contain ( " unlikely " ) ;
}
#[ test ]
#[ should_panic ]
fn test_stdout_does_not_contain_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " This is a likely error message \n " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . stdout_does_not_contain ( " likely " ) ;
}
#[ test ]
#[ should_panic ]
fn test_stderr_does_not_contain_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " " . into ( ) ,
stderr : " This is a likely error message \n " . into ( ) ,
} ;
res . stderr_does_not_contain ( " likely " ) ;
}
#[ test ]
fn test_stdout_matches ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " This is a likely error message \n " . into ( ) ,
stderr : " This is a likely error message \n " . into ( ) ,
} ;
let positive = regex ::Regex ::new ( " .*likely.* " ) . unwrap ( ) ;
let negative = regex ::Regex ::new ( " .*unlikely.* " ) . unwrap ( ) ;
res . stdout_matches ( & positive ) ;
res . stdout_does_not_match ( & negative ) ;
}
#[ test ]
#[ should_panic ]
fn test_stdout_matches_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " This is a likely error message \n " . into ( ) ,
stderr : " This is a likely error message \n " . into ( ) ,
} ;
let negative = regex ::Regex ::new ( " .*unlikely.* " ) . unwrap ( ) ;
res . stdout_matches ( & negative ) ;
}
#[ test ]
#[ should_panic ]
fn test_stdout_not_matches_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " This is a likely error message \n " . into ( ) ,
stderr : " This is a likely error message \n " . into ( ) ,
} ;
let positive = regex ::Regex ::new ( " .*likely.* " ) . unwrap ( ) ;
res . stdout_does_not_match ( & positive ) ;
}
2021-04-17 12:01:52 +00:00
#[ test ]
fn test_normalized_newlines_stdout_is ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " A \r \n B \n C " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . normalized_newlines_stdout_is ( " A \r \n B \n C " ) ;
res . normalized_newlines_stdout_is ( " A \n B \n C " ) ;
res . normalized_newlines_stdout_is ( " A \n B \r \n C " ) ;
}
#[ test ]
#[ should_panic ]
fn test_normalized_newlines_stdout_is_fail ( ) {
let res = CmdResult {
tmpd : None ,
code : None ,
success : true ,
stdout : " A \r \n B \n C " . into ( ) ,
stderr : " " . into ( ) ,
} ;
res . normalized_newlines_stdout_is ( " A \r \n B \n C \n " ) ;
}
2021-04-10 18:18:38 +00:00
}