@@ -9,6 +9,7 @@ use crate::{
9
9
list:: { BinaryList , OutputFormat , RustBuildMeta , Styles , TestListState } ,
10
10
reuse_build:: PathMapper ,
11
11
target_runner:: { PlatformRunner , TargetRunner } ,
12
+ test_command:: { LocalExecuteContext , TestCommand } ,
12
13
test_filter:: TestFilterBuilder ,
13
14
} ;
14
15
use camino:: { Utf8Path , Utf8PathBuf } ;
@@ -24,7 +25,7 @@ use nextest_metadata::{
24
25
use once_cell:: sync:: { Lazy , OnceCell } ;
25
26
use owo_colors:: OwoColorize ;
26
27
use std:: {
27
- collections:: { BTreeMap , BTreeSet , HashMap } ,
28
+ collections:: { BTreeMap , BTreeSet } ,
28
29
ffi:: { OsStr , OsString } ,
29
30
io,
30
31
io:: Write ,
@@ -681,47 +682,58 @@ impl<'g> RustTestArtifact<'g> {
681
682
argv. push ( "--ignored" ) ;
682
683
}
683
684
684
- let cmd = make_test_command (
685
+ let mut cmd = TestCommand :: new (
685
686
ctx,
686
687
program. clone ( ) ,
687
688
& argv,
688
689
& self . cwd ,
689
690
& self . package ,
690
691
& self . non_test_binaries ,
691
692
) ;
692
- let mut cmd = tokio:: process:: Command :: from ( cmd) ;
693
- match cmd. output ( ) . await {
694
- Ok ( output) => {
695
- if output. status . success ( ) {
696
- String :: from_utf8 ( output. stdout ) . map_err ( |err| {
697
- CreateTestListError :: CommandNonUtf8 {
698
- binary_id : self . binary_id . clone ( ) ,
699
- command : std:: iter:: once ( program)
700
- . chain ( argv. iter ( ) . map ( |& s| s. to_owned ( ) ) )
701
- . collect ( ) ,
702
- stdout : err. into_bytes ( ) ,
703
- stderr : output. stderr ,
704
- }
705
- } )
706
- } else {
707
- Err ( CreateTestListError :: CommandFail {
708
- binary_id : self . binary_id . clone ( ) ,
709
- command : std:: iter:: once ( program)
710
- . chain ( argv. iter ( ) . map ( |& s| s. to_owned ( ) ) )
711
- . collect ( ) ,
712
- exit_status : output. status ,
713
- stdout : output. stdout ,
714
- stderr : output. stderr ,
715
- } )
716
- }
693
+ // Capture stdout and stderr.
694
+ cmd. command_mut ( )
695
+ . stdout ( std:: process:: Stdio :: piped ( ) )
696
+ . stderr ( std:: process:: Stdio :: piped ( ) ) ;
697
+
698
+ let child = cmd
699
+ . spawn ( )
700
+ . map_err ( |error| CreateTestListError :: CommandExecFail {
701
+ binary_id : self . binary_id . clone ( ) ,
702
+ command : std:: iter:: once ( program. clone ( ) )
703
+ . chain ( argv. iter ( ) . map ( |& s| s. to_owned ( ) ) )
704
+ . collect ( ) ,
705
+ error,
706
+ } ) ?;
707
+
708
+ let output = child. wait_with_output ( ) . await . map_err ( |error| {
709
+ CreateTestListError :: CommandExecFail {
710
+ binary_id : self . binary_id . clone ( ) ,
711
+ command : std:: iter:: once ( program. clone ( ) )
712
+ . chain ( argv. iter ( ) . map ( |& s| s. to_owned ( ) ) )
713
+ . collect ( ) ,
714
+ error,
717
715
}
718
- Err ( error) => Err ( CreateTestListError :: CommandExecFail {
716
+ } ) ?;
717
+
718
+ if output. status . success ( ) {
719
+ String :: from_utf8 ( output. stdout ) . map_err ( |err| CreateTestListError :: CommandNonUtf8 {
719
720
binary_id : self . binary_id . clone ( ) ,
720
721
command : std:: iter:: once ( program)
721
722
. chain ( argv. iter ( ) . map ( |& s| s. to_owned ( ) ) )
722
723
. collect ( ) ,
723
- error,
724
- } ) ,
724
+ stdout : err. into_bytes ( ) ,
725
+ stderr : output. stderr ,
726
+ } )
727
+ } else {
728
+ Err ( CreateTestListError :: CommandFail {
729
+ binary_id : self . binary_id . clone ( ) ,
730
+ command : std:: iter:: once ( program)
731
+ . chain ( argv. iter ( ) . map ( |& s| s. to_owned ( ) ) )
732
+ . collect ( ) ,
733
+ exit_status : output. status ,
734
+ stdout : output. stdout ,
735
+ stderr : output. stderr ,
736
+ } )
725
737
}
726
738
}
727
739
}
@@ -817,12 +829,12 @@ impl<'a> TestInstance<'a> {
817
829
( & self . suite_info . binary_id , self . name )
818
830
}
819
831
820
- /// Creates the command expression for this test instance.
821
- pub ( crate ) fn make_expression (
832
+ /// Creates the command for this test instance.
833
+ pub ( crate ) fn make_command (
822
834
& self ,
823
835
ctx : & TestExecuteContext < ' _ > ,
824
836
test_list : & TestList < ' _ > ,
825
- ) -> std :: process :: Command {
837
+ ) -> TestCommand {
826
838
let platform_runner = ctx
827
839
. target_runner
828
840
. for_build_platform ( self . suite_info . build_platform ) ;
@@ -851,7 +863,7 @@ impl<'a> TestInstance<'a> {
851
863
env : & test_list. env ,
852
864
} ;
853
865
854
- make_test_command (
866
+ TestCommand :: new (
855
867
& ctx,
856
868
program,
857
869
& args,
@@ -872,153 +884,6 @@ pub struct TestExecuteContext<'a> {
872
884
pub target_runner : & ' a TargetRunner ,
873
885
}
874
886
875
- #[ derive( Clone , Debug ) ]
876
- struct LocalExecuteContext < ' a > {
877
- double_spawn : & ' a DoubleSpawnInfo ,
878
- runner : & ' a TargetRunner ,
879
- dylib_path : & ' a OsStr ,
880
- env : & ' a EnvironmentMap ,
881
- }
882
-
883
- /// Create a duct Expression for a test binary with the given arguments, using the specified [`PackageMetadata`].
884
- fn make_test_command (
885
- ctx : & LocalExecuteContext < ' _ > ,
886
- program : String ,
887
- args : & [ & str ] ,
888
- cwd : & Utf8PathBuf ,
889
- package : & PackageMetadata < ' _ > ,
890
- non_test_binaries : & BTreeSet < ( String , Utf8PathBuf ) > ,
891
- ) -> std:: process:: Command {
892
- // This is a workaround for a macOS SIP issue:
893
- // https://github.com/nextest-rs/nextest/pull/84
894
- //
895
- // Basically, if SIP is enabled, macOS removes any environment variables that start with
896
- // "LD_" or "DYLD_" when spawning system-protected processes. This unfortunately includes
897
- // processes like bash -- this means that if nextest invokes a shell script, paths might
898
- // end up getting sanitized.
899
- //
900
- // This is particularly relevant for target runners, which are often shell scripts.
901
- //
902
- // To work around this, re-export any variables that begin with LD_ or DYLD_ as "NEXTEST_LD_"
903
- // or "NEXTEST_DYLD_". Do this on all platforms for uniformity.
904
- //
905
- // Nextest never changes these environment variables within its own process, so caching them is
906
- // valid.
907
- fn is_sip_sanitized ( var : & str ) -> bool {
908
- // Look for variables starting with LD_ or DYLD_.
909
- // https://briandfoy.github.io/macos-s-system-integrity-protection-sanitizes-your-environment/
910
- var. starts_with ( "LD_" ) || var. starts_with ( "DYLD_" )
911
- }
912
-
913
- static LD_DYLD_ENV_VARS : Lazy < HashMap < String , OsString > > = Lazy :: new ( || {
914
- std:: env:: vars_os ( )
915
- . filter_map ( |( k, v) | match k. into_string ( ) {
916
- Ok ( k) => is_sip_sanitized ( & k) . then_some ( ( k, v) ) ,
917
- Err ( _) => None ,
918
- } )
919
- . collect ( )
920
- } ) ;
921
-
922
- // TODO: pass in double spawn option
923
- let mut cmd = if let Some ( current_exe) = ctx. double_spawn . current_exe ( ) {
924
- let mut cmd = std:: process:: Command :: new ( current_exe) ;
925
- cmd. args ( [ DoubleSpawnInfo :: SUBCOMMAND_NAME , "--" , program. as_str ( ) ] ) ;
926
- cmd. arg ( & shell_words:: join ( args) ) ;
927
- cmd
928
- } else {
929
- let mut cmd = std:: process:: Command :: new ( program) ;
930
- cmd. args ( args) ;
931
- cmd
932
- } ;
933
-
934
- // NB: we will always override user-provided environment variables with the
935
- // `CARGO_*` and `NEXTEST_*` variables set directly on `cmd` below.
936
- ctx. env . apply_env ( & mut cmd) ;
937
-
938
- cmd. current_dir ( cwd)
939
- // This environment variable is set to indicate that tests are being run under nextest.
940
- . env ( "NEXTEST" , "1" )
941
- // This environment variable is set to indicate that each test is being run in its own process.
942
- . env ( "NEXTEST_EXECUTION_MODE" , "process-per-test" )
943
- // These environment variables are set at runtime by cargo test:
944
- // https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates
945
- . env (
946
- "CARGO_MANIFEST_DIR" ,
947
- // CARGO_MANIFEST_DIR is set to the *new* cwd after path mapping.
948
- cwd,
949
- )
950
- . env (
951
- "__NEXTEST_ORIGINAL_CARGO_MANIFEST_DIR" ,
952
- // This is a test-only environment variable set to the *old* cwd. Not part of the
953
- // public API.
954
- package. manifest_path ( ) . parent ( ) . unwrap ( ) ,
955
- )
956
- . env ( "CARGO_PKG_VERSION" , format ! ( "{}" , package. version( ) ) )
957
- . env (
958
- "CARGO_PKG_VERSION_MAJOR" ,
959
- format ! ( "{}" , package. version( ) . major) ,
960
- )
961
- . env (
962
- "CARGO_PKG_VERSION_MINOR" ,
963
- format ! ( "{}" , package. version( ) . minor) ,
964
- )
965
- . env (
966
- "CARGO_PKG_VERSION_PATCH" ,
967
- format ! ( "{}" , package. version( ) . patch) ,
968
- )
969
- . env (
970
- "CARGO_PKG_VERSION_PRE" ,
971
- format ! ( "{}" , package. version( ) . pre) ,
972
- )
973
- . env ( "CARGO_PKG_AUTHORS" , package. authors ( ) . join ( ":" ) )
974
- . env ( "CARGO_PKG_NAME" , package. name ( ) )
975
- . env (
976
- "CARGO_PKG_DESCRIPTION" ,
977
- package. description ( ) . unwrap_or_default ( ) ,
978
- )
979
- . env ( "CARGO_PKG_HOMEPAGE" , package. homepage ( ) . unwrap_or_default ( ) )
980
- . env ( "CARGO_PKG_LICENSE" , package. license ( ) . unwrap_or_default ( ) )
981
- . env (
982
- "CARGO_PKG_LICENSE_FILE" ,
983
- package. license_file ( ) . unwrap_or_else ( || "" . as_ref ( ) ) ,
984
- )
985
- . env (
986
- "CARGO_PKG_REPOSITORY" ,
987
- package. repository ( ) . unwrap_or_default ( ) ,
988
- )
989
- . env (
990
- "CARGO_PKG_RUST_VERSION" ,
991
- package. rust_version ( ) . map_or ( String :: new ( ) , |v| {
992
- // A Rust version e.g. "1.58" has v.to_string() generated as "^1.58".
993
- // Remove the prefix ^.
994
- let s = v. to_string ( ) ;
995
- match s. strip_prefix ( '^' ) {
996
- Some ( suffix) => suffix. to_owned ( ) ,
997
- None => s,
998
- }
999
- } ) ,
1000
- )
1001
- . env ( dylib_path_envvar ( ) , ctx. dylib_path ) ;
1002
-
1003
- for ( k, v) in & * LD_DYLD_ENV_VARS {
1004
- if k != dylib_path_envvar ( ) {
1005
- cmd. env ( "NEXTEST_" . to_owned ( ) + k, v) ;
1006
- }
1007
- }
1008
- // Also add the dylib path envvar under the NEXTEST_ prefix.
1009
- if is_sip_sanitized ( dylib_path_envvar ( ) ) {
1010
- cmd. env ( "NEXTEST_" . to_owned ( ) + dylib_path_envvar ( ) , ctx. dylib_path ) ;
1011
- }
1012
-
1013
- // Expose paths to non-test binaries at runtime so that relocated paths work.
1014
- // These paths aren't exposed by Cargo at runtime, so use a NEXTEST_BIN_EXE prefix.
1015
- for ( name, path) in non_test_binaries {
1016
- cmd. env ( format ! ( "NEXTEST_BIN_EXE_{}" , name) , path) ;
1017
- }
1018
-
1019
- cmd
1020
- }
1021
-
1022
887
#[ cfg( test) ]
1023
888
mod tests {
1024
889
use super :: * ;
0 commit comments