@@ -120,6 +120,14 @@ pub struct CmdResult {
120120 stdout : Vec < u8 > ,
121121 /// captured standard error after running the Command
122122 stderr : Vec < u8 > ,
123+ /// arguments used to run the command
124+ args : Vec < OsString > ,
125+ /// environment variables passed to the command
126+ env_vars : Vec < ( OsString , OsString ) > ,
127+ /// current directory used to run the command
128+ current_dir : Option < PathBuf > ,
129+ /// stdin bytes provided to the command (if any)
130+ stdin_bytes : Option < Vec < u8 > > ,
123131}
124132
125133impl CmdResult {
@@ -130,6 +138,10 @@ impl CmdResult {
130138 exit_status : Option < ExitStatus > ,
131139 stdout : U ,
132140 stderr : V ,
141+ args : Vec < OsString > ,
142+ env_vars : Vec < ( OsString , OsString ) > ,
143+ current_dir : Option < PathBuf > ,
144+ stdin_bytes : Option < Vec < u8 > > ,
133145 ) -> Self
134146 where
135147 S : Into < PathBuf > ,
@@ -144,6 +156,10 @@ impl CmdResult {
144156 exit_status,
145157 stdout : stdout. into ( ) ,
146158 stderr : stderr. into ( ) ,
159+ args,
160+ env_vars,
161+ current_dir,
162+ stdin_bytes,
147163 }
148164 }
149165
@@ -160,6 +176,10 @@ impl CmdResult {
160176 self . exit_status ,
161177 function ( & self . stdout ) ,
162178 self . stderr . as_slice ( ) ,
179+ self . args . clone ( ) ,
180+ self . env_vars . clone ( ) ,
181+ self . current_dir . clone ( ) ,
182+ self . stdin_bytes . clone ( ) ,
163183 )
164184 }
165185
@@ -176,6 +196,10 @@ impl CmdResult {
176196 self . exit_status ,
177197 function ( self . stdout_str ( ) ) ,
178198 self . stderr . as_slice ( ) ,
199+ self . args . clone ( ) ,
200+ self . env_vars . clone ( ) ,
201+ self . current_dir . clone ( ) ,
202+ self . stdin_bytes . clone ( ) ,
179203 )
180204 }
181205
@@ -192,6 +216,10 @@ impl CmdResult {
192216 self . exit_status ,
193217 self . stdout . as_slice ( ) ,
194218 function ( & self . stderr ) ,
219+ self . args . clone ( ) ,
220+ self . env_vars . clone ( ) ,
221+ self . current_dir . clone ( ) ,
222+ self . stdin_bytes . clone ( ) ,
195223 )
196224 }
197225
@@ -208,6 +236,10 @@ impl CmdResult {
208236 self . exit_status ,
209237 self . stdout . as_slice ( ) ,
210238 function ( self . stderr_str ( ) ) ,
239+ self . args . clone ( ) ,
240+ self . env_vars . clone ( ) ,
241+ self . current_dir . clone ( ) ,
242+ self . stdin_bytes . clone ( ) ,
211243 )
212244 }
213245
@@ -700,6 +732,119 @@ impl CmdResult {
700732 self . no_stdout ( ) . stderr_is_bytes ( msg)
701733 }
702734
735+ /// Compare output and exit status with the GNU coreutils implementation.
736+ /// If GNU coreutils isn't available, this is a no-op and prints a skip reason.
737+ #[ cfg( unix) ]
738+ #[ track_caller]
739+ pub fn matches_gnu ( & self ) -> & Self {
740+ match self . gnu_result ( ) {
741+ Ok ( expected) => {
742+ self . stdout_is ( expected. stdout_str ( ) ) ;
743+ self . stderr_is ( expected. stderr_str ( ) ) ;
744+ self . code_is ( expected. code ( ) ) ;
745+ }
746+ Err ( error) => {
747+ println ! ( "test skipped: {error}" ) ;
748+ }
749+ }
750+ self
751+ }
752+
753+ /// On non-Unix platforms GNU coreutils may not be available.
754+ #[ cfg( not( unix) ) ]
755+ #[ track_caller]
756+ pub fn matches_gnu ( & self ) -> & Self {
757+ println ! ( "test skipped: GNU comparison is not supported on this platform" ) ;
758+ self
759+ }
760+
761+ #[ cfg( unix) ]
762+ fn gnu_result ( & self ) -> std:: result:: Result < CmdResult , String > {
763+ let util_name = self . util_name . as_ref ( ) . ok_or_else ( || {
764+ format ! ( "{UUTILS_WARNING}: matches_gnu requires a utility name" )
765+ } ) ?;
766+ println ! ( "{}" , check_coreutil_version( util_name, VERSION_MIN ) ?) ;
767+ let gnu_name = host_name_for ( util_name) ;
768+
769+ let mut args = self . args . clone ( ) ;
770+ if let Some ( first) = args. first ( ) {
771+ if first == & OsString :: from ( util_name) {
772+ args. remove ( 0 ) ;
773+ }
774+ }
775+
776+ let mut command = Command :: new ( gnu_name. as_ref ( ) ) ;
777+ command. env_clear ( ) ;
778+ command. args ( & args) ;
779+
780+ if let Some ( current_dir) = & self . current_dir {
781+ command. current_dir ( current_dir) ;
782+ } else if let Some ( tmpd) = & self . tmpd {
783+ command. current_dir ( tmpd. path ( ) ) ;
784+ }
785+
786+ command
787+ . env ( "PATH" , PATH )
788+ . envs ( DEFAULT_ENV )
789+ . envs ( self . env_vars . iter ( ) . cloned ( ) ) ;
790+
791+ if let Some ( ld_preload) = env:: var_os ( "LD_PRELOAD" ) {
792+ command. env ( "LD_PRELOAD" , ld_preload) ;
793+ }
794+
795+ if let Some ( profile) = env:: var_os ( "LLVM_PROFILE_FILE" ) {
796+ command. env ( "LLVM_PROFILE_FILE" , profile) ;
797+ }
798+
799+ let output = if let Some ( stdin_bytes) = & self . stdin_bytes {
800+ let mut child = command
801+ . stdin ( Stdio :: piped ( ) )
802+ . stdout ( Stdio :: piped ( ) )
803+ . stderr ( Stdio :: piped ( ) )
804+ . spawn ( )
805+ . map_err ( |e| format ! ( "{UUTILS_WARNING}: {e}" ) ) ?;
806+ if let Some ( mut stdin) = child. stdin . take ( ) {
807+ stdin
808+ . write_all ( stdin_bytes)
809+ . map_err ( |e| format ! ( "{UUTILS_WARNING}: {e}" ) ) ?;
810+ }
811+ child
812+ . wait_with_output ( )
813+ . map_err ( |e| format ! ( "{UUTILS_WARNING}: {e}" ) ) ?
814+ } else {
815+ command
816+ . stdin ( Stdio :: null ( ) )
817+ . output ( )
818+ . map_err ( |e| format ! ( "{UUTILS_WARNING}: {e}" ) ) ?
819+ } ;
820+
821+ let ( stdout, stderr) = if cfg ! ( target_os = "linux" ) {
822+ (
823+ String :: from_utf8_lossy ( & output. stdout ) . to_string ( ) ,
824+ String :: from_utf8_lossy ( & output. stderr ) . to_string ( ) ,
825+ )
826+ } else {
827+ let from = gnu_name. to_string ( ) + ":" ;
828+ let to = & from[ 1 ..] ;
829+ (
830+ String :: from_utf8_lossy ( & output. stdout ) . replace ( & from, to) ,
831+ String :: from_utf8_lossy ( & output. stderr ) . replace ( & from, to) ,
832+ )
833+ } ;
834+
835+ Ok ( CmdResult :: new (
836+ gnu_name. to_string ( ) ,
837+ Some ( util_name. clone ( ) ) ,
838+ self . tmpd . clone ( ) ,
839+ Some ( output. status ) ,
840+ stdout. as_bytes ( ) ,
841+ stderr. as_bytes ( ) ,
842+ args,
843+ self . env_vars . clone ( ) ,
844+ self . current_dir . clone ( ) ,
845+ self . stdin_bytes . clone ( ) ,
846+ ) )
847+ }
703848 #[ track_caller]
704849 pub fn fails_silently ( & self ) -> & Self {
705850 assert ! ( !self . succeeded( ) ) ;
@@ -2198,6 +2343,10 @@ impl<'a> UChildAssertion<'a> {
21982343 exit_status,
21992344 stdout,
22002345 stderr,
2346+ self . uchild . args . clone ( ) ,
2347+ self . uchild . env_vars . clone ( ) ,
2348+ self . uchild . current_dir . clone ( ) ,
2349+ self . uchild . stdin_bytes . clone ( ) ,
22012350 )
22022351 }
22032352
@@ -2272,6 +2421,10 @@ pub struct UChild {
22722421 raw : Child ,
22732422 bin_path : PathBuf ,
22742423 util_name : Option < String > ,
2424+ args : Vec < OsString > ,
2425+ env_vars : Vec < ( OsString , OsString ) > ,
2426+ current_dir : Option < PathBuf > ,
2427+ stdin_bytes : Option < Vec < u8 > > ,
22752428 captured_stdout : Option < CapturedOutput > ,
22762429 captured_stderr : Option < CapturedOutput > ,
22772430 stdin_pty : Option < File > ,
@@ -2294,6 +2447,10 @@ impl UChild {
22942447 raw : child,
22952448 bin_path : ucommand. bin_path . clone ( ) . unwrap ( ) ,
22962449 util_name : ucommand. util_name . clone ( ) ,
2450+ args : ucommand. args . iter ( ) . cloned ( ) . collect ( ) ,
2451+ env_vars : ucommand. env_vars . clone ( ) ,
2452+ current_dir : ucommand. current_dir . clone ( ) ,
2453+ stdin_bytes : ucommand. bytes_into_stdin . clone ( ) ,
22972454 captured_stdout,
22982455 captured_stderr,
22992456 stdin_pty,
@@ -2475,10 +2632,14 @@ impl UChild {
24752632 ///
24762633 /// Returns the error from the call to `wait_with_output` if any
24772634 pub fn wait ( self ) -> io:: Result < CmdResult > {
2478- let ( bin_path, util_name, tmpd) = (
2635+ let ( bin_path, util_name, tmpd, args , env_vars , current_dir , stdin_bytes ) = (
24792636 self . bin_path . clone ( ) ,
24802637 self . util_name . clone ( ) ,
24812638 self . tmpd . clone ( ) ,
2639+ self . args . clone ( ) ,
2640+ self . env_vars . clone ( ) ,
2641+ self . current_dir . clone ( ) ,
2642+ self . stdin_bytes . clone ( ) ,
24822643 ) ;
24832644
24842645 let output = self . wait_with_output ( ) ?;
@@ -2490,6 +2651,10 @@ impl UChild {
24902651 exit_status : Some ( output. status ) ,
24912652 stdout : output. stdout ,
24922653 stderr : output. stderr ,
2654+ args,
2655+ env_vars,
2656+ current_dir,
2657+ stdin_bytes,
24932658 } )
24942659 }
24952660
@@ -3075,6 +3240,10 @@ pub fn gnu_cmd_result(
30753240 result. exit_status ,
30763241 stdout. as_bytes ( ) ,
30773242 stderr. as_bytes ( ) ,
3243+ result. args . clone ( ) ,
3244+ result. env_vars . clone ( ) ,
3245+ result. current_dir . clone ( ) ,
3246+ result. stdin_bytes . clone ( ) ,
30783247 ) )
30793248}
30803249
0 commit comments