/// Executes a list of tasks and produces an [ExecutionResult] with the status
/// of /all/ tasks, regardless of whether they were skipped, ignored, etc.
Executes a list of tasks and produces an [ExecutionResult] with the status
of /all/ tasks, regardless of whether they were skipped, ignored, etc.
Executes a list of tasks and produces an [ ExecutionResult ] with the status of / all / tasks regardless of whether they were skipped ignored etc .
pub fn execute(config: &Config, mut tasks: Vec<pot::Pot>) -> Option<ExecutionResult> {
    if let Some(ref filter) = config.filter {
        for t in tasks.iter_mut() {
            t.apply_filter(filter);
        }
    }
    // Execution proceeds by launching or skipping each task in `tasks`,
    // sequentially. We use mio under the hood.
    let poll_timeout = Duration::from_millis(100);
    let mut events = Events::with_capacity(128);
    let mut executor = Executor {
        running: BTreeMap::new(),
        result: ExecutionResult(vec![]),
        poll: Poll::new().expect("Couldn't create poll"),
        // This variable increases whenever we receive a SIGINT
        // or SIGTERM. We use this to decide whether to launch additional tasks
        // or to just report their tests as 'Ignored'.
        sigint_ctr: 0,
    };
    // Installs our signal handler. From this point onwards, this thread stops
    // responding to SIGINT and SIGTERM. Be very careful moving this line and
    // even more careful spawning threads before this point.
    let mut signals = Signals::new(&[SIGINT, SIGTERM]).expect("Couldn't create signals");
    executor
        .poll
        .registry()
        .register(&mut signals, SIGNAL_TOKEN, Interest::READABLE)
        .expect("failed to register signal handler in a Poll registry");
    tasks.reverse();
    // While there are tasks to be ran or running,
    while !tasks.is_empty() || !executor.running.is_empty() {
        // If we have tasks to be ran and not enough jobs running, we can launch some.
        while !tasks.is_empty() && executor.running.len() < config.jobs {
            let p = tasks.pop().unwrap(); // we just checked tasks is not empty! :)
            executor.launch_or_skip(p, config);
        }
        if let Err(e) = executor.poll.poll(&mut events, Some(poll_timeout)) {
            // In case we receive a signal while blocked at poll, it returns EINTR.
            // All is fine and we just run over the loop
            // one more time and the signal will be delivered through mio.
            // check this for context: https://github.com/vorner/signal-hook/issues/98
            if e.kind() == std::io::ErrorKind::Interrupted {
                continue;
            } else {
                executor.signal_running_pots(Signal::SIGKILL, true);
                return None;
            }
        }
        for event in events.iter() {
            if event.token() == SIGNAL_TOKEN {
                for sig in signals.pending() {
                    eprintln!("[!!] Received signal {:?}", sig);
                    if sig == SIGINT || sig == SIGTERM {
                        executor.handle_sigint();
                    }
                }
            } else {
                let (pid, src) = split_token(event.token());
                if let Some(obs) = executor.running.get_mut(&pid) {
                    if event.is_readable() {
                        obs.process_read_event(src);
                    }
                    if event.is_read_closed() {
                        obs.process_read_closed(src);
                    }
                } else {
                    eprintln!("[!!] Event for unregistered pid: {}", pid);
                }
            }
        }
        // Makes sure to remove the completed pids from the set of running pids
        // I was not able to accomplish this in [Executor::wait] due to Rust complaining
        // about borrowing self in too many different ways
        let completed_pids = executor.wait(config.pot_timeout);
        for (pid, (status, dur)) in completed_pids {
            let task = executor.running.remove(&pid).unwrap();
            executor
                .result
                .0
                .push(task.complete(status, dur, config.jobs > 1));
        }
    }
    Some(executor.result)
}
pub fn execute ( config : & Config , mut tasks : Vec < pot :: Pot > ) -> Option < ExecutionResult > { if let Some ( ref filter ) = config . filter { for t in tasks . iter_mut ( ) { t . apply_filter ( filter ) ; } } // Execution proceeds by launching or skipping each task in `tasks`, // sequentially. We use mio under the hood. let poll_timeout = Duration :: from_millis ( 100 ) ; let mut events = Events :: with_capacity ( 128 ) ; let mut executor = Executor { running : BTreeMap :: new ( ) , result : ExecutionResult ( vec ! [ ] ) , poll : Poll :: new ( ) . expect ( " " ) , // This variable increases whenever we receive a SIGINT // or SIGTERM. We use this to decide whether to launch additional tasks // or to just report their tests as 'Ignored'. sigint_ctr : 0 , } ; // Installs our signal handler. From this point onwards, this thread stops // responding to SIGINT and SIGTERM. Be very careful moving this line and // even more careful spawning threads before this point. let mut signals = Signals :: new ( & [ SIGINT , SIGTERM ] ) . expect ( " " ) ; executor . poll . registry ( ) . register ( & mut signals , SIGNAL_TOKEN , Interest :: READABLE ) . expect ( " " ) ; tasks . reverse ( ) ; // While there are tasks to be ran or running, while ! tasks . is_empty ( ) || ! executor . running . is_empty ( ) { // If we have tasks to be ran and not enough jobs running, we can launch some. while ! tasks . is_empty ( ) && executor . running . len ( ) < config . jobs { let p = tasks . pop ( ) . unwrap ( ) ; // we just checked tasks is not empty! :) executor . launch_or_skip ( p , config ) ; } if let Err ( e ) = executor . poll . poll ( & mut events , Some ( poll_timeout ) ) { // In case we receive a signal while blocked at poll, it returns EINTR. // All is fine and we just run over the loop // one more time and the signal will be delivered through mio. // check this for context: https://github.com/vorner/signal-hook/issues/98 if e . kind ( ) == std :: io :: ErrorKind :: Interrupted { continue ; } else { executor . signal_running_pots ( Signal :: SIGKILL , true ) ; return None ; } } for event in events . iter ( ) { if event . token ( ) == SIGNAL_TOKEN { for sig in signals . pending ( ) { eprintln ! ( " " sig ) ; if sig == SIGINT || sig == SIGTERM { executor . handle_sigint ( ) ; } } } else { let ( pid , src ) = split_token ( event . token ( ) ) ; if let Some ( obs ) = executor . running . get_mut ( & pid ) { if event . is_readable ( ) { obs . process_read_event ( src ) ; } if event . is_read_closed ( ) { obs . process_read_closed ( src ) ; } } else { eprintln ! ( " " pid ) ; } } } // Makes sure to remove the completed pids from the set of running pids // I was not able to accomplish this in [Executor::wait] due to Rust complaining // about borrowing self in too many different ways let completed_pids = executor . wait ( config . pot_timeout ) ; for ( pid , ( status , dur ) ) in completed_pids { let task = executor . running . remove ( & pid ) . unwrap ( ) ; executor . result . 0 . push ( task . complete ( status , dur , config . jobs > 1 ) ) ; } } Some ( executor . result ) }
Executes a list of tasks and produces an [ExecutionResult] with the status
of /all/ tasks, regardless of whether they were skipped, ignored, etc.
Executes a list of tasks and produces an [ ExecutionResult ] with the status of / all / tasks regardless of whether they were skipped ignored etc .
config
&Config
tasks
Vec<pot::Pot>