/// Launches a process responsible for running the given pot and registers
/// the necessary pipes in the internal poll and the [RunningPot] within
/// `self.observing`. Returns the process id of the launched process.
Launches a process responsible for running the given pot and registers
the necessary pipes in the internal poll and the [RunningPot] within
`self.observing`. Returns the process id of the launched process.
Launches a process responsible for running the given pot and registers the necessary pipes in the internal poll and the [ RunningPot ] within ` self . observing ` . Returns the process id of the launched process .
fn launch(&mut self, p: pot::Pot, cfg: &Config) -> Pid {
        let (stdout_sender, mut stdout_receiver) = pipe::new().unwrap();
        let (stderr_sender, mut stderr_receiver) = pipe::new().unwrap();
        let (mut result_sender, result_receiver) = pipe::new().unwrap();
        stdout_receiver.set_nonblocking(true).unwrap();
        stderr_receiver.set_nonblocking(true).unwrap();
        result_receiver.set_nonblocking(true).unwrap();
        // This two statements have a scope of their own to ensure
        // the lock is released.
        {
            io::stdout().lock().flush().unwrap();
            io::stderr().lock().flush().unwrap();
        }
        let pid = match unsafe { fork() }.expect("failed to fork") {
            ForkResult::Child => {
                // Setting the process group id is important: it enables us to kill the entire
                // process group, which will kill any grand-child process, when cleaning up.
                let self_pid = unistd::getpid();
                unistd::setpgid(self_pid, self_pid).expect("child: failed to set PGID");
                std::mem::drop(stdout_receiver);
                std::mem::drop(stderr_receiver);
                std::mem::drop(result_receiver);
                // When we're not running pots in parallel (cfg.jobs == 1),
                // we inherit stderr and stdout from the parent instead
                // of capturing it.
                if cfg.jobs > 1 {
                    let stdout_fd = std::io::stdout().as_raw_fd();
                    let stderr_fd = std::io::stderr().as_raw_fd();
                    unistd::close(stdout_fd).expect("child: failed to close stdout");
                    unistd::dup2(stdout_sender.as_raw_fd(), stdout_fd).unwrap();
                    unistd::close(stderr_fd).expect("child: failed to close stderr");
                    unistd::dup2(stderr_sender.as_raw_fd(), stderr_fd).unwrap();
                }
                // If the manager configuration provides us with a handle,
                // then we execute composable tests against that handle rather
                // than starting up a manager.
                let res = if let Some(h) = cfg.man_config.request_handle() {
                    println!("Calling p.run_against_handle...");
                    p.run_against_handle(&cfg.pot_config, h)
                } else {
                    println!("Calling p.run_with...");
                    p.run_with(&cfg.pot_config, cfg.man_config.clone())
                };
                serialize_and_write(&mut result_sender, &res).unwrap();
                std::process::exit(if res.is_success() { 0 } else { 1 })
            }
            ForkResult::Parent { child, .. } => child,
        };
        // If we're running more than one test at a time, we'll manage
        // each process stdout/stderr, otherwise it will just be inherited.
        if cfg.jobs > 1 {
            self.poll
                .registry()
                .register(
                    &mut stdout_receiver,
                    make_token(pid, InputSource::Stdout),
                    Interest::READABLE,
                )
                .expect("Registering stdout_receiver failed");
            self.poll
                .registry()
                .register(
                    &mut stderr_receiver,
                    make_token(pid, InputSource::Stderr),
                    Interest::READABLE,
                )
                .expect("Registering stderr receiver failed");
        }
        let obs = RunningPot {
            pid,
            pot_name: p.derived_name,
            test_names: p.test.test_names(),
            result_pipe: result_receiver,
            stdout_pipe: BufferedReader::new(stdout_receiver),
            stderr_pipe: BufferedReader::new(stderr_receiver),
            started_at: Instant::now(),
        };
        self.running.insert(pid, obs);
        pid
    }
fn launch ( & mut self , p : pot :: Pot , cfg : & Config ) -> Pid { let ( stdout_sender , mut stdout_receiver ) = pipe :: new ( ) . unwrap ( ) ; let ( stderr_sender , mut stderr_receiver ) = pipe :: new ( ) . unwrap ( ) ; let ( mut result_sender , result_receiver ) = pipe :: new ( ) . unwrap ( ) ; stdout_receiver . set_nonblocking ( true ) . unwrap ( ) ; stderr_receiver . set_nonblocking ( true ) . unwrap ( ) ; result_receiver . set_nonblocking ( true ) . unwrap ( ) ; // This two statements have a scope of their own to ensure // the lock is released. { io :: stdout ( ) . lock ( ) . flush ( ) . unwrap ( ) ; io :: stderr ( ) . lock ( ) . flush ( ) . unwrap ( ) ; } let pid = match unsafe { fork ( ) } . expect ( " " ) { ForkResult :: Child => { // Setting the process group id is important: it enables us to kill the entire // process group, which will kill any grand-child process, when cleaning up. let self_pid = unistd :: getpid ( ) ; unistd :: setpgid ( self_pid , self_pid ) . expect ( " " ) ; std :: mem :: drop ( stdout_receiver ) ; std :: mem :: drop ( stderr_receiver ) ; std :: mem :: drop ( result_receiver ) ; // When we're not running pots in parallel (cfg.jobs == 1), // we inherit stderr and stdout from the parent instead // of capturing it. if cfg . jobs > 1 { let stdout_fd = std :: io :: stdout ( ) . as_raw_fd ( ) ; let stderr_fd = std :: io :: stderr ( ) . as_raw_fd ( ) ; unistd :: close ( stdout_fd ) . expect ( " " ) ; unistd :: dup2 ( stdout_sender . as_raw_fd ( ) , stdout_fd ) . unwrap ( ) ; unistd :: close ( stderr_fd ) . expect ( " " ) ; unistd :: dup2 ( stderr_sender . as_raw_fd ( ) , stderr_fd ) . unwrap ( ) ; } // If the manager configuration provides us with a handle, // then we execute composable tests against that handle rather // than starting up a manager. let res = if let Some ( h ) = cfg . man_config . request_handle ( ) { println ! ( " " ) ; p . run_against_handle ( & cfg . pot_config , h ) } else { println ! ( " " ) ; p . run_with ( & cfg . pot_config , cfg . man_config . clone ( ) ) } ; serialize_and_write ( & mut result_sender , & res ) . unwrap ( ) ; std :: process :: exit ( if res . is_success ( ) { 0 } else { 1 } ) } ForkResult :: Parent { child , .. } => child , } ; // If we're running more than one test at a time, we'll manage // each process stdout/stderr, otherwise it will just be inherited. if cfg . jobs > 1 { self . poll . registry ( ) . register ( & mut stdout_receiver , make_token ( pid , InputSource :: Stdout ) , Interest :: READABLE , ) . expect ( " " ) ; self . poll . registry ( ) . register ( & mut stderr_receiver , make_token ( pid , InputSource :: Stderr ) , Interest :: READABLE , ) . expect ( " " ) ; } let obs = RunningPot { pid , pot_name : p . derived_name , test_names : p . test . test_names ( ) , result_pipe : result_receiver , stdout_pipe : BufferedReader :: new ( stdout_receiver ) , stderr_pipe : BufferedReader :: new ( stderr_receiver ) , started_at : Instant :: now ( ) , } ; self . running . insert ( pid , obs ) ; pid }
Launches a process responsible for running the given pot and registers
the necessary pipes in the internal poll and the [RunningPot] within
`self.observing`.
Launches a process responsible for running the given pot and registers the necessary pipes in the internal poll and the [ RunningPot ] within ` self . observing ` .
&mut self
p
pot::Pot
cfg
&Config