@@ -16,8 +16,11 @@ use std::{
16
16
17
17
use enum_map:: { enum_map, Enum , EnumMap } ;
18
18
use once_cell:: sync:: Lazy ;
19
+ use tokio:: runtime:: Builder ;
19
20
use url:: Url ;
20
21
22
+ use crate :: cli:: rustup_mode;
23
+ use crate :: currentprocess;
21
24
use crate :: test as rustup_test;
22
25
use crate :: test:: const_dist_dir;
23
26
use crate :: test:: this_host_triple;
@@ -676,8 +679,13 @@ impl Config {
676
679
I : IntoIterator < Item = A > + Clone + Debug ,
677
680
A : AsRef < OsStr > ,
678
681
{
682
+ let inprocess = allow_inprocess ( name, args. clone ( ) ) ;
679
683
let start = Instant :: now ( ) ;
680
- let out = self . run_subprocess ( name, args. clone ( ) , env) ;
684
+ let out = if inprocess {
685
+ self . run_inprocess ( name, args. clone ( ) , env)
686
+ } else {
687
+ self . run_subprocess ( name, args. clone ( ) , env)
688
+ } ;
681
689
let duration = Instant :: now ( ) - start;
682
690
let output = SanitizedOutput {
683
691
ok : matches ! ( out. status, Some ( 0 ) ) ,
@@ -686,6 +694,7 @@ impl Config {
686
694
} ;
687
695
688
696
println ! ( "ran: {} {:?}" , name, args) ;
697
+ println ! ( "inprocess: {inprocess}" ) ;
689
698
println ! ( "status: {:?}" , out. status) ;
690
699
println ! ( "duration: {:.3}s" , duration. as_secs_f32( ) ) ;
691
700
println ! ( "stdout:\n ====\n {}\n ====\n " , output. stdout) ;
@@ -694,6 +703,55 @@ impl Config {
694
703
output
695
704
}
696
705
706
+ #[ cfg_attr( feature = "otel" , tracing:: instrument( skip_all) ) ]
707
+ pub ( crate ) fn run_inprocess < I , A > ( & self , name : & str , args : I , env : & [ ( & str , & str ) ] ) -> Output
708
+ where
709
+ I : IntoIterator < Item = A > ,
710
+ A : AsRef < OsStr > ,
711
+ {
712
+ // should we use vars_os, or skip over non-stringable vars? This is test
713
+ // code after all...
714
+ let mut vars: HashMap < String , String > = HashMap :: default ( ) ;
715
+ self :: env ( self , & mut vars) ;
716
+ vars. extend ( env. iter ( ) . map ( |( k, v) | ( k. to_string ( ) , v. to_string ( ) ) ) ) ;
717
+ let mut arg_strings: Vec < Box < str > > = Vec :: new ( ) ;
718
+ arg_strings. push ( name. to_owned ( ) . into_boxed_str ( ) ) ;
719
+ for arg in args {
720
+ arg_strings. push (
721
+ arg. as_ref ( )
722
+ . to_os_string ( )
723
+ . into_string ( )
724
+ . unwrap ( )
725
+ . into_boxed_str ( ) ,
726
+ ) ;
727
+ }
728
+ let mut builder = Builder :: new_multi_thread ( ) ;
729
+ builder
730
+ . enable_all ( )
731
+ . worker_threads ( 2 )
732
+ . max_blocking_threads ( 2 ) ;
733
+ let rt = builder. build ( ) . unwrap ( ) ;
734
+ rt. block_on ( async {
735
+ let tp =
736
+ currentprocess:: TestProcess :: new ( & * self . workdir . borrow ( ) , & arg_strings, vars, "" ) ;
737
+ let process_res =
738
+ rustup_mode:: main ( tp. process . current_dir ( ) . unwrap ( ) , & tp. process ) . await ;
739
+ // convert Err's into an ec
740
+ let ec = match process_res {
741
+ Ok ( process_res) => process_res,
742
+ Err ( e) => {
743
+ crate :: cli:: common:: report_error ( & e, & tp. process ) ;
744
+ utils:: ExitCode ( 1 )
745
+ }
746
+ } ;
747
+ Output {
748
+ status : Some ( ec. 0 ) ,
749
+ stderr : tp. stderr ( ) ,
750
+ stdout : tp. stdout ( ) ,
751
+ }
752
+ } )
753
+ }
754
+
697
755
#[ track_caller]
698
756
pub fn run_subprocess < I , A > ( & self , name : & str , args : I , env : & [ ( & str , & str ) ] ) -> Output
699
757
where
@@ -797,6 +855,43 @@ pub fn env<E: rustup_test::Env>(config: &Config, cmd: &mut E) {
797
855
config. env ( cmd)
798
856
}
799
857
858
+ fn allow_inprocess < I , A > ( name : & str , args : I ) -> bool
859
+ where
860
+ I : IntoIterator < Item = A > ,
861
+ A : AsRef < OsStr > ,
862
+ {
863
+ // Only the rustup alias is currently ready for in-process testing:
864
+ // - -init performs self-update which monkey with global external state.
865
+ // - proxies themselves behave appropriately the proxied output needs to be
866
+ // collected for assertions to be made on it as our tests traverse layers.
867
+ // - self update executions cannot run in-process because on windows the
868
+ // process replacement dance would replace the test process.
869
+ // - any command with --version in it is testing to see something was
870
+ // installed properly, so we have to shell out to it to be sure
871
+ if name != "rustup" {
872
+ return false ;
873
+ }
874
+ let mut is_update = false ;
875
+ let mut no_self_update = false ;
876
+ let mut self_cmd = false ;
877
+ let mut run = false ;
878
+ let mut version = false ;
879
+ for arg in args {
880
+ if arg. as_ref ( ) == "update" {
881
+ is_update = true ;
882
+ } else if arg. as_ref ( ) == "--no-self-update" {
883
+ no_self_update = true ;
884
+ } else if arg. as_ref ( ) == "self" {
885
+ self_cmd = true ;
886
+ } else if arg. as_ref ( ) == "run" {
887
+ run = true ;
888
+ } else if arg. as_ref ( ) == "--version" {
889
+ version = true ;
890
+ }
891
+ }
892
+ !( run || self_cmd || version || ( is_update && !no_self_update) )
893
+ }
894
+
800
895
#[ derive( Copy , Clone , Eq , PartialEq ) ]
801
896
enum RlsStatus {
802
897
Available ,
0 commit comments