<commit_before>//! Unix-specific types for signal handling.
//!
//! This module is only defined on Unix platforms and contains the primary
//! `Signal` type for receiving notifications of signals.

#![cfg(unix)]

pub extern crate libc;
extern crate mio;
extern crate tokio_uds;

use std::cell::RefCell;
use std::io::{self, Write, Read};
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Once, ONCE_INIT, Mutex};

use futures::stream::{Stream, Fuse};
use futures::{self, Future, IntoFuture, Complete, Oneshot, Poll, Async};
use self::libc::c_int;
use self::tokio_uds::UnixStream;
use tokio_core::io::IoFuture;
use tokio_core::reactor::{PollEvented, Handle};
use tokio_core::channel::{channel, Sender, Receiver};

static INIT: Once = ONCE_INIT;
static mut GLOBAL_STATE: *mut GlobalState = 0 as *mut _;

/// An implementation of `Stream` for receiving a particular type of signal.
///
/// This structure implements the `Stream` trait and represents notifications
/// of the current process receiving a particular signal. The signal being
/// listened for is passed to `Signal::new`, and the same signal number is then
/// yielded as each element for the stream.
///
/// In general signal handling on Unix is a pretty tricky topic, and this
/// structure is no exception! There are some important limitations to keep in
/// mind when using `Signal` streams:
///
/// * While multiple event loops are supported, the *first* event loop to
///   register a signal handler is required to be active to ensure that signals
///   for other event loops are delivered. In other words, once an event loop
///   registers a signal, it's best to keep it around and running. This is
///   normally just a problem for tests, and the "workaround" is to spawn a
///   thread in the background at the beginning of the test suite which is
///   running an event loop (and listening for a signal).
///
/// * Signals handling in Unix already necessitates coalescing signals
///   together sometimes. This `Signal` stream is also no exception here in
///   that it will also coalesce signals. That is, even if the signal handler
///   for this process runs multiple times, the `Signal` stream may only return
///   one signal notification. Specifically, before `poll` is called, all
///   signal notifications are coalesced into one item returned from `poll`.
///   Once `poll` has been called, however, a further signal is guaranteed to
///   be yielded as an item.
///
/// * Signal handling in general is relatively inefficient. Although some
///   improvements are possible in this crate, it's recommended to not plan on
///   having millions of signal channels open.
///
/// * Currently the "driver task" to process incoming signals never exits.
///
/// If you've got any questions about this feel free to open an issue on the
/// repo, though, as I'd love to chat about this! In other words, I'd love to
/// alleviate some of these limitations if possible!
pub struct Signal {
    signum: c_int,
    reg: PollEvented<MyRegistration>,
    _finished: Complete<()>,
}

struct GlobalState {
    write: UnixStream,
    tx: Mutex<Sender<Message>>,
    signals: [GlobalSignalState; 32],
}

struct GlobalSignalState {
    ready: AtomicBool,
    prev: libc::sigaction,
}

enum Message {
    NewSignal(c_int, Complete<io::Result<Signal>>),
}

struct DriverTask {
    handle: Handle,
    read: UnixStream,
    rx: Fuse<Receiver<Message>>,
    signals: [SignalState; 32],
}

struct SignalState {
    registered: bool,
    tasks: Vec<(RefCell<Oneshot<()>>, mio::SetReadiness)>,
}

pub use self::libc::{SIGINT, SIGKILL, SIGTERM, SIGUSR1, SIGUSR2};
pub use self::libc::{SIGHUP, SIGQUIT, SIGPIPE, SIGALRM, SIGTRAP};

impl Signal {
    /// Creates a new stream which will receive notifications when the current
    /// process receives the signal `signum`.
    ///
    /// This function will create a new stream which may be based on the
    /// event loop handle provided. This function returns a future which will
    /// then resolve to the signal stream, if successful.
    ///
    /// The `Signal` stream is an infinite stream which will receive
    /// notifications whenever a signal is received. More documentation can be
    /// found on `Signal` itself, but to reiterate:
    ///
    /// * Signals may be coalesced beyond what the kernel already does.
    /// * While multiple event loops are supported, the first event loop to
    ///   register a signal handler must be active to deliver signal
    ///   notifications
    /// * Once a signal handle is registered with the process the underlying
    ///   libc signal handler is never unregistered.
    ///
    /// A `Signal` stream can be created for a particular signal number
    /// multiple times. When a signal is received then all the associated
    /// channels will receive the signal notification.
    pub fn new(signum: c_int, handle: &Handle) -> IoFuture<Signal> {
        let mut init = None;
        INIT.call_once(|| {
            init = Some(global_init(handle));
        });
        let new_signal = futures::lazy(move || {
            let (tx, rx) = futures::oneshot();
            let msg = Message::NewSignal(signum, tx);
            let res = unsafe {
                (*GLOBAL_STATE).tx.lock().unwrap().send(msg)
            };
            res.expect("failed to request a new signal stream, did the \
                        first event loop go away?");
            rx.then(|r| r.unwrap())
        });
        match init {
            Some(init) => init.into_future().and_then(|()| new_signal).boxed(),
            None => new_signal.boxed(),
        }
    }
}

impl Stream for Signal {
    type Item = c_int;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<c_int>, io::Error> {
        if !self.reg.poll_read().is_ready() {
            return Ok(Async::NotReady)
        }
        self.reg.need_read();
        self.reg.get_ref()
                .inner.borrow()
                .as_ref().unwrap().1
                .set_readiness(mio::Ready::none())
                .expect("failed to set readiness");
        Ok(Async::Ready(Some(self.signum)))
    }
}

fn global_init(handle: &Handle) -> io::Result<()> {
    let (tx, rx) = try!(channel(handle));
    let (read, write) = try!(UnixStream::pair(handle));
    unsafe {
        let state = Box::new(GlobalState {
            write: write,
            signals: {
                fn new() -> GlobalSignalState {
                    GlobalSignalState {
                        ready: AtomicBool::new(false),
                        prev: unsafe { mem::zeroed() },
                    }
                }
                [
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                ]
            },
            tx: Mutex::new(tx.clone()),
        });
        GLOBAL_STATE = Box::into_raw(state);

        handle.spawn(DriverTask {
            handle: handle.clone(),
            rx: rx.fuse(),
            read: read,
            signals: {
                fn new() -> SignalState {
                    SignalState { registered: false, tasks: Vec::new() }
                }
                [
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                ]
            },
        });

        Ok(())
    }
}

impl Future for DriverTask {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        self.check_signal_drops();
        self.check_messages();
        self.check_signals();

        // TODO: when to finish this task?
        Ok(Async::NotReady)
    }
}

impl DriverTask {
    fn check_signal_drops(&mut self) {
        for signal in self.signals.iter_mut() {
            signal.tasks.retain(|task| {
                !task.0.borrow_mut().poll().is_err()
            });
        }
    }

    fn check_messages(&mut self) {
        loop {
            // Acquire the next message
            let message = match self.rx.poll() {
                Ok(Async::Ready(Some(e))) => e,
                Ok(Async::Ready(None)) |
                Ok(Async::NotReady) => break,
                Err(e) => panic!("error on rx: {}", e),
            };
            let (sig, complete) = match message {
                Message::NewSignal(sig, complete) => (sig, complete),
            };

            // If the signal's too large, then we return an error, otherwise we
            // use this index to look at the signal slot.
            //
            // If the signal wasn't previously registered then we do so now.
            let signal = match self.signals.get_mut(sig as usize) {
                Some(signal) => signal,
                None => {
                    complete.complete(Err(io::Error::new(io::ErrorKind::Other,
                                                         "signum too large")));
                    continue
                }
            };
            if !signal.registered {
                unsafe {
                    let mut new: libc::sigaction = mem::zeroed();
                    new.sa_sigaction = handler as usize;
                    new.sa_flags = libc::SA_RESTART | libc::SA_SIGINFO;
                    let mut prev = mem::zeroed();
                    if libc::sigaction(sig, &new, &mut prev) != 0 {
                        complete.complete(Err(io::Error::last_os_error()));
                        continue
                    }
                    signal.registered = true;
                }
            }

            // Acquire the (registration, set_readiness) pair by... assuming
            // we're on the event loop (true because of the spawn above).
            let reg = MyRegistration { inner: RefCell::new(None) };
            let reg = match PollEvented::new(reg, &self.handle) {
                Ok(reg) => reg,
                Err(e) => {
                    complete.complete(Err(e));
                    continue
                }
            };

            // Create the `Signal` to pass back and then also keep a handle to
            // the `SetReadiness` for ourselves internally.
            let (tx, rx) = futures::oneshot();
            let ready = reg.get_ref().inner.borrow_mut().as_mut().unwrap().1.clone();
            complete.complete(Ok(Signal {
                signum: sig,
                reg: reg,
                _finished: tx,
            }));
            signal.tasks.push((RefCell::new(rx), ready));
        }
    }

    fn check_signals(&mut self) {
        // Drain all data from the pipe
        let mut buf = [0; 32];
        let mut any = false;
        loop {
            match self.read.read(&mut buf) {
                Ok(0) => {  // EOF == something happened
                    any = true;
                    break
                }
                Ok(..) => any = true,   // data read, but keep draining
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break,
                Err(e) => panic!("bad read: {}", e),
            }
        }

        // If nothing happened, no need to check the signals
        if !any {
            return
        }

        for (i, slot) in self.signals.iter().enumerate() {
            // No need to go farther if we haven't even registered a signal
            if !slot.registered {
                continue
            }

            // See if this signal actually happened since we last checked
            unsafe {
                if !(*GLOBAL_STATE).signals[i].ready.swap(false, Ordering::SeqCst) {
                    continue
                }
            }

            // Wake up all the tasks waiting on this signal
            for task in slot.tasks.iter() {
                task.1.set_readiness(mio::Ready::readable())
                      .expect("failed to set readiness");
            }
        }
    }
}

extern fn handler(signum: c_int,
                  info: *mut libc::siginfo_t,
                  ptr: *mut libc::c_void) {
    type FnSigaction = extern fn(c_int, *mut libc::siginfo_t, *mut libc::c_void);
    type FnHandler = extern fn(c_int);

    unsafe {
        let state = match (*GLOBAL_STATE).signals.get(signum as usize) {
            Some(state) => state,
            None => return,
        };

        if !state.ready.swap(true, Ordering::SeqCst) {
            match (&(*GLOBAL_STATE).write).write(&[1]) {
                Ok(..) => {}
                Err(e) => {
                    if e.kind() != io::ErrorKind::WouldBlock {
                        panic!("bad error on write fd: {}", e)
                    }
                }
            }
        }

        let fnptr = state.prev.sa_sigaction;
        if fnptr == 0 || fnptr == libc::SIG_DFL || fnptr == libc::SIG_IGN {
            return
        }
        if state.prev.sa_flags & libc::SA_SIGINFO == 0 {
            let action = mem::transmute::<usize, FnHandler>(fnptr);
            action(signum)
        } else {
            let action = mem::transmute::<usize, FnSigaction>(fnptr);
            action(signum, info, ptr)
        }
    }
}

struct MyRegistration {
    inner: RefCell<Option<(mio::Registration, mio::SetReadiness)>>,
}

impl mio::Evented for MyRegistration {
    fn register(&self,
                poll: &mio::Poll,
                token: mio::Token,
                events: mio::Ready,
                opts: mio::PollOpt) -> io::Result<()> {
        let reg = mio::Registration::new(poll, token, events, opts);
        *self.inner.borrow_mut() = Some(reg);
        Ok(())
    }

    fn reregister(&self,
                  _poll: &mio::Poll,
                  _token: mio::Token,
                  _events: mio::Ready,
                  _opts: mio::PollOpt) -> io::Result<()> {
        Ok(())
    }

    fn deregister(&self, _poll: &mio::Poll) -> io::Result<()> {
        Ok(())
    }
}
<commit_msg>signal: Remove SIGKILL reexport<commit_after>//! Unix-specific types for signal handling.
//!
//! This module is only defined on Unix platforms and contains the primary
//! `Signal` type for receiving notifications of signals.

#![cfg(unix)]

pub extern crate libc;
extern crate mio;
extern crate tokio_uds;

use std::cell::RefCell;
use std::io::{self, Write, Read};
use std::mem;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Once, ONCE_INIT, Mutex};

use futures::stream::{Stream, Fuse};
use futures::{self, Future, IntoFuture, Complete, Oneshot, Poll, Async};
use self::libc::c_int;
use self::tokio_uds::UnixStream;
use tokio_core::io::IoFuture;
use tokio_core::reactor::{PollEvented, Handle};
use tokio_core::channel::{channel, Sender, Receiver};

static INIT: Once = ONCE_INIT;
static mut GLOBAL_STATE: *mut GlobalState = 0 as *mut _;

/// An implementation of `Stream` for receiving a particular type of signal.
///
/// This structure implements the `Stream` trait and represents notifications
/// of the current process receiving a particular signal. The signal being
/// listened for is passed to `Signal::new`, and the same signal number is then
/// yielded as each element for the stream.
///
/// In general signal handling on Unix is a pretty tricky topic, and this
/// structure is no exception! There are some important limitations to keep in
/// mind when using `Signal` streams:
///
/// * While multiple event loops are supported, the *first* event loop to
///   register a signal handler is required to be active to ensure that signals
///   for other event loops are delivered. In other words, once an event loop
///   registers a signal, it's best to keep it around and running. This is
///   normally just a problem for tests, and the "workaround" is to spawn a
///   thread in the background at the beginning of the test suite which is
///   running an event loop (and listening for a signal).
///
/// * Signals handling in Unix already necessitates coalescing signals
///   together sometimes. This `Signal` stream is also no exception here in
///   that it will also coalesce signals. That is, even if the signal handler
///   for this process runs multiple times, the `Signal` stream may only return
///   one signal notification. Specifically, before `poll` is called, all
///   signal notifications are coalesced into one item returned from `poll`.
///   Once `poll` has been called, however, a further signal is guaranteed to
///   be yielded as an item.
///
/// * Signal handling in general is relatively inefficient. Although some
///   improvements are possible in this crate, it's recommended to not plan on
///   having millions of signal channels open.
///
/// * Currently the "driver task" to process incoming signals never exits.
///
/// If you've got any questions about this feel free to open an issue on the
/// repo, though, as I'd love to chat about this! In other words, I'd love to
/// alleviate some of these limitations if possible!
pub struct Signal {
    signum: c_int,
    reg: PollEvented<MyRegistration>,
    _finished: Complete<()>,
}

struct GlobalState {
    write: UnixStream,
    tx: Mutex<Sender<Message>>,
    signals: [GlobalSignalState; 32],
}

struct GlobalSignalState {
    ready: AtomicBool,
    prev: libc::sigaction,
}

enum Message {
    NewSignal(c_int, Complete<io::Result<Signal>>),
}

struct DriverTask {
    handle: Handle,
    read: UnixStream,
    rx: Fuse<Receiver<Message>>,
    signals: [SignalState; 32],
}

struct SignalState {
    registered: bool,
    tasks: Vec<(RefCell<Oneshot<()>>, mio::SetReadiness)>,
}

pub use self::libc::{SIGINT, SIGTERM, SIGUSR1, SIGUSR2};
pub use self::libc::{SIGHUP, SIGQUIT, SIGPIPE, SIGALRM, SIGTRAP};

impl Signal {
    /// Creates a new stream which will receive notifications when the current
    /// process receives the signal `signum`.
    ///
    /// This function will create a new stream which may be based on the
    /// event loop handle provided. This function returns a future which will
    /// then resolve to the signal stream, if successful.
    ///
    /// The `Signal` stream is an infinite stream which will receive
    /// notifications whenever a signal is received. More documentation can be
    /// found on `Signal` itself, but to reiterate:
    ///
    /// * Signals may be coalesced beyond what the kernel already does.
    /// * While multiple event loops are supported, the first event loop to
    ///   register a signal handler must be active to deliver signal
    ///   notifications
    /// * Once a signal handle is registered with the process the underlying
    ///   libc signal handler is never unregistered.
    ///
    /// A `Signal` stream can be created for a particular signal number
    /// multiple times. When a signal is received then all the associated
    /// channels will receive the signal notification.
    pub fn new(signum: c_int, handle: &Handle) -> IoFuture<Signal> {
        let mut init = None;
        INIT.call_once(|| {
            init = Some(global_init(handle));
        });
        let new_signal = futures::lazy(move || {
            let (tx, rx) = futures::oneshot();
            let msg = Message::NewSignal(signum, tx);
            let res = unsafe {
                (*GLOBAL_STATE).tx.lock().unwrap().send(msg)
            };
            res.expect("failed to request a new signal stream, did the \
                        first event loop go away?");
            rx.then(|r| r.unwrap())
        });
        match init {
            Some(init) => init.into_future().and_then(|()| new_signal).boxed(),
            None => new_signal.boxed(),
        }
    }
}

impl Stream for Signal {
    type Item = c_int;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<c_int>, io::Error> {
        if !self.reg.poll_read().is_ready() {
            return Ok(Async::NotReady)
        }
        self.reg.need_read();
        self.reg.get_ref()
                .inner.borrow()
                .as_ref().unwrap().1
                .set_readiness(mio::Ready::none())
                .expect("failed to set readiness");
        Ok(Async::Ready(Some(self.signum)))
    }
}

fn global_init(handle: &Handle) -> io::Result<()> {
    let (tx, rx) = try!(channel(handle));
    let (read, write) = try!(UnixStream::pair(handle));
    unsafe {
        let state = Box::new(GlobalState {
            write: write,
            signals: {
                fn new() -> GlobalSignalState {
                    GlobalSignalState {
                        ready: AtomicBool::new(false),
                        prev: unsafe { mem::zeroed() },
                    }
                }
                [
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                ]
            },
            tx: Mutex::new(tx.clone()),
        });
        GLOBAL_STATE = Box::into_raw(state);

        handle.spawn(DriverTask {
            handle: handle.clone(),
            rx: rx.fuse(),
            read: read,
            signals: {
                fn new() -> SignalState {
                    SignalState { registered: false, tasks: Vec::new() }
                }
                [
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                    new(), new(), new(), new(), new(), new(), new(), new(),
                ]
            },
        });

        Ok(())
    }
}

impl Future for DriverTask {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        self.check_signal_drops();
        self.check_messages();
        self.check_signals();

        // TODO: when to finish this task?
        Ok(Async::NotReady)
    }
}

impl DriverTask {
    fn check_signal_drops(&mut self) {
        for signal in self.signals.iter_mut() {
            signal.tasks.retain(|task| {
                !task.0.borrow_mut().poll().is_err()
            });
        }
    }

    fn check_messages(&mut self) {
        loop {
            // Acquire the next message
            let message = match self.rx.poll() {
                Ok(Async::Ready(Some(e))) => e,
                Ok(Async::Ready(None)) |
                Ok(Async::NotReady) => break,
                Err(e) => panic!("error on rx: {}", e),
            };
            let (sig, complete) = match message {
                Message::NewSignal(sig, complete) => (sig, complete),
            };

            // If the signal's too large, then we return an error, otherwise we
            // use this index to look at the signal slot.
            //
            // If the signal wasn't previously registered then we do so now.
            let signal = match self.signals.get_mut(sig as usize) {
                Some(signal) => signal,
                None => {
                    complete.complete(Err(io::Error::new(io::ErrorKind::Other,
                                                         "signum too large")));
                    continue
                }
            };
            if !signal.registered {
                unsafe {
                    let mut new: libc::sigaction = mem::zeroed();
                    new.sa_sigaction = handler as usize;
                    new.sa_flags = libc::SA_RESTART | libc::SA_SIGINFO;
                    let mut prev = mem::zeroed();
                    if libc::sigaction(sig, &new, &mut prev) != 0 {
                        complete.complete(Err(io::Error::last_os_error()));
                        continue
                    }
                    signal.registered = true;
                }
            }

            // Acquire the (registration, set_readiness) pair by... assuming
            // we're on the event loop (true because of the spawn above).
            let reg = MyRegistration { inner: RefCell::new(None) };
            let reg = match PollEvented::new(reg, &self.handle) {
                Ok(reg) => reg,
                Err(e) => {
                    complete.complete(Err(e));
                    continue
                }
            };

            // Create the `Signal` to pass back and then also keep a handle to
            // the `SetReadiness` for ourselves internally.
            let (tx, rx) = futures::oneshot();
            let ready = reg.get_ref().inner.borrow_mut().as_mut().unwrap().1.clone();
            complete.complete(Ok(Signal {
                signum: sig,
                reg: reg,
                _finished: tx,
            }));
            signal.tasks.push((RefCell::new(rx), ready));
        }
    }

    fn check_signals(&mut self) {
        // Drain all data from the pipe
        let mut buf = [0; 32];
        let mut any = false;
        loop {
            match self.read.read(&mut buf) {
                Ok(0) => {  // EOF == something happened
                    any = true;
                    break
                }
                Ok(..) => any = true,   // data read, but keep draining
                Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => break,
                Err(e) => panic!("bad read: {}", e),
            }
        }

        // If nothing happened, no need to check the signals
        if !any {
            return
        }

        for (i, slot) in self.signals.iter().enumerate() {
            // No need to go farther if we haven't even registered a signal
            if !slot.registered {
                continue
            }

            // See if this signal actually happened since we last checked
            unsafe {
                if !(*GLOBAL_STATE).signals[i].ready.swap(false, Ordering::SeqCst) {
                    continue
                }
            }

            // Wake up all the tasks waiting on this signal
            for task in slot.tasks.iter() {
                task.1.set_readiness(mio::Ready::readable())
                      .expect("failed to set readiness");
            }
        }
    }
}

extern fn handler(signum: c_int,
                  info: *mut libc::siginfo_t,
                  ptr: *mut libc::c_void) {
    type FnSigaction = extern fn(c_int, *mut libc::siginfo_t, *mut libc::c_void);
    type FnHandler = extern fn(c_int);

    unsafe {
        let state = match (*GLOBAL_STATE).signals.get(signum as usize) {
            Some(state) => state,
            None => return,
        };

        if !state.ready.swap(true, Ordering::SeqCst) {
            match (&(*GLOBAL_STATE).write).write(&[1]) {
                Ok(..) => {}
                Err(e) => {
                    if e.kind() != io::ErrorKind::WouldBlock {
                        panic!("bad error on write fd: {}", e)
                    }
                }
            }
        }

        let fnptr = state.prev.sa_sigaction;
        if fnptr == 0 || fnptr == libc::SIG_DFL || fnptr == libc::SIG_IGN {
            return
        }
        if state.prev.sa_flags & libc::SA_SIGINFO == 0 {
            let action = mem::transmute::<usize, FnHandler>(fnptr);
            action(signum)
        } else {
            let action = mem::transmute::<usize, FnSigaction>(fnptr);
            action(signum, info, ptr)
        }
    }
}

struct MyRegistration {
    inner: RefCell<Option<(mio::Registration, mio::SetReadiness)>>,
}

impl mio::Evented for MyRegistration {
    fn register(&self,
                poll: &mio::Poll,
                token: mio::Token,
                events: mio::Ready,
                opts: mio::PollOpt) -> io::Result<()> {
        let reg = mio::Registration::new(poll, token, events, opts);
        *self.inner.borrow_mut() = Some(reg);
        Ok(())
    }

    fn reregister(&self,
                  _poll: &mio::Poll,
                  _token: mio::Token,
                  _events: mio::Ready,
                  _opts: mio::PollOpt) -> io::Result<()> {
        Ok(())
    }

    fn deregister(&self, _poll: &mio::Poll) -> io::Result<()> {
        Ok(())
    }
}
<|endoftext|>


<commit_before>use error::FerrumResult;

pub fn copy_recursively<F>(source: &Path, dest: &Path, mut criteria: F) -> FerrumResult<()>
    where F : Fn(&Path) -> bool
{
    use std::old_io;
    use std::old_io::fs;
    use std::old_io::fs::PathExtensions;

    if !source.is_dir() {
        try!(Err(old_io::standard_error(old_io::InvalidInput)))
    }

    let mut contents = try!(fs::walk_dir(source));
    for entry in contents {
        debug!("ENTRY: {}", entry.display());
        if !criteria(&entry) { continue; }

        // TODO: remove this unwrap.
        let new_dest = &dest.join(entry.path_relative_from(source).unwrap());

        if entry.is_dir() {
            try!(fs::mkdir(new_dest, old_io::USER_RWX));
        } else {
            try!(fs::copy(&entry, new_dest));
        }
    }

    Ok(())
}
<commit_msg>Remove unnecessary mut<commit_after>use error::FerrumResult;

pub fn copy_recursively<F>(source: &Path, dest: &Path, criteria: F) -> FerrumResult<()>
    where F : Fn(&Path) -> bool
{
    use std::old_io;
    use std::old_io::fs;
    use std::old_io::fs::PathExtensions;

    if !source.is_dir() {
        try!(Err(old_io::standard_error(old_io::InvalidInput)))
    }

    let mut contents = try!(fs::walk_dir(source));
    for entry in contents {
        debug!("ENTRY: {}", entry.display());
        if !criteria(&entry) { continue; }

        // TODO: remove this unwrap.
        let new_dest = &dest.join(entry.path_relative_from(source).unwrap());

        if entry.is_dir() {
            try!(fs::mkdir(new_dest, old_io::USER_RWX));
        } else {
            try!(fs::copy(&entry, new_dest));
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>// util

use crate::parse::*;
use std::cell::RefCell;
use std::rc::Rc;

thread_local! {
    static NLABEL: RefCell<usize> = RefCell::new(1);
}

pub fn bump_nlabel() -> usize {
    NLABEL.with(|n| {
        let mut nlabel = n.borrow_mut();
        let ret = *nlabel;
        *nlabel += 1;
        return ret;
    })
}

pub fn first_char(s: &str) -> char {
    if s.len() == 0 {
        return '\0';
    }
    return char::from((&s[0..1].as_bytes())[0]);
}

pub fn roundup(x: i32, align: i32) -> i32 {
    return (x + align - 1) & (!(align - 1));
}

pub fn ptr_to(base: Rc<RefCell<Type>>) -> Type {
    let mut ty = alloc_type();
    ty.ty = CType::PTR;
    ty.size = 8;
    ty.align = 8;
    ty.ptr_to = Some(base);
    return ty;
}

pub fn ary_of(base: Type, len: i32) -> Type {
    let mut ty = alloc_type();
    ty.ty = CType::ARY;
    ty.size = base.size * len;
    ty.align = base.align;
    ty.ary_of = Some(Box::new(base));
    ty.len = len;
    return ty;
}

fn new_prim_ty(ty: CType, size: i32) -> Type {
    let mut ret = alloc_type();
    ret.ty = ty;
    ret.size = size;
    ret.align = size;
    return ret;
}

pub fn void_ty() -> Type {
    return new_prim_ty(CType::VOID, 0);
}

pub fn bool_ty() -> Type {
    return new_prim_ty(CType::BOOL, 1);
}

pub fn char_ty() -> Type {
    return new_prim_ty(CType::CHAR, 1);
}

pub fn int_ty() -> Type {
    return new_prim_ty(CType::INT, 4);
}

pub fn func_ty(base: Type) -> Type {
    let mut ty = alloc_type();
    ty.returning = Some(Box::new(base));
    return ty;
}

pub fn same_type(x: Rc<RefCell<Type>>, y: Rc<RefCell<Type>>) -> bool {
    if x.borrow().ty != y.borrow().ty {
        return false;
    }

    let xx = x.borrow();
    let yy = y.borrow();
    match xx.ty {
        CType::PTR => {
            let xptr = xx.ptr_to.clone().unwrap();
            let yptr = yy.ptr_to.clone().unwrap();
            same_type(xptr, yptr)
        }
        CType::ARY => {
            let xary = xx.ary_of.clone().unwrap();
            let yary = yy.ary_of.clone().unwrap();
            xx.size == yy.size && same_type(Rc::new(RefCell::new(*xary)), Rc::new(RefCell::new(*yary)))
        }
        CType::STRUCT | CType::FUNC => *xx == *yy,
        _ => true,
    }
}

<commit_msg>Simplify.<commit_after>// util

use crate::parse::*;
use std::cell::RefCell;
use std::rc::Rc;

thread_local! {
    static NLABEL: RefCell<usize> = RefCell::new(1);
}

pub fn bump_nlabel() -> usize {
    NLABEL.with(|n| {
        let mut nlabel = n.borrow_mut();
        let ret = *nlabel;
        *nlabel += 1;
        return ret;
    })
}

pub fn first_char(s: &str) -> char {
    if s.len() == 0 {
        return '\0';
    }
    return char::from((&s[0..1].as_bytes())[0]);
}

pub fn roundup(x: i32, align: i32) -> i32 {
    return (x + align - 1) & (!(align - 1));
}

pub fn ptr_to(base: Rc<RefCell<Type>>) -> Type {
    let mut ty = alloc_type();
    ty.ty = CType::PTR;
    ty.size = 8;
    ty.align = 8;
    ty.ptr_to = Some(base);
    return ty;
}

pub fn ary_of(base: Type, len: i32) -> Type {
    let mut ty = alloc_type();
    ty.ty = CType::ARY;
    ty.size = base.size * len;
    ty.align = base.align;
    ty.ary_of = Some(Box::new(base));
    ty.len = len;
    return ty;
}

fn new_ty(ty: CType, size: i32) -> Type {
    let mut ret = alloc_type();
    ret.ty = ty;
    ret.size = size;
    ret.align = size;
    return ret;
}

pub fn void_ty() -> Type {
    return new_ty(CType::VOID, 0);
}

pub fn bool_ty() -> Type {
    return new_ty(CType::BOOL, 1);
}

pub fn char_ty() -> Type {
    return new_ty(CType::CHAR, 1);
}

pub fn int_ty() -> Type {
    return new_ty(CType::INT, 4);
}

pub fn func_ty(base: Type) -> Type {
    let mut ty = alloc_type();
    ty.returning = Some(Box::new(base));
    return ty;
}

pub fn same_type(x: Rc<RefCell<Type>>, y: Rc<RefCell<Type>>) -> bool {
    if x.borrow().ty != y.borrow().ty {
        return false;
    }

    let xx = x.borrow();
    let yy = y.borrow();
    match xx.ty {
        CType::PTR => {
            let xptr = xx.ptr_to.clone().unwrap();
            let yptr = yy.ptr_to.clone().unwrap();
            same_type(xptr, yptr)
        }
        CType::ARY => {
            let xary = xx.ary_of.clone().unwrap();
            let yary = yy.ary_of.clone().unwrap();
            xx.size == yy.size && same_type(Rc::new(RefCell::new(*xary)), Rc::new(RefCell::new(*yary)))
        }
        CType::STRUCT | CType::FUNC => *xx == *yy,
        _ => true,
    }
}

<|endoftext|>


<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]
use std::{cmp::min, env};

use crate::{
    cluster::Cluster,
    experiments::{
        CompatiblityTestParams, CpuFlamegraphParams, Experiment, ExperimentParam,
        PerformanceBenchmarkParams, PerformanceBenchmarkThreeRegionSimulationParams,
        RebootRandomValidatorsParams, ReconfigurationParams, RecoveryTimeParams,
        StateSyncPerformanceParams, TwinValidatorsParams, ValidatorVersioningParams,
    },
};
use anyhow::{format_err, Result};

pub struct ExperimentSuite {
    pub experiments: Vec<Box<dyn Experiment>>,
}

impl ExperimentSuite {
    fn new_pre_release(cluster: &Cluster) -> Self {
        let mut experiments: Vec<Box<dyn Experiment>> = vec![];
        if env::var("RECOVERY_EXP").is_ok() {
            experiments.push(Box::new(
                RecoveryTimeParams {
                    num_accounts_to_mint: 100_000,
                }
                .build(cluster),
            ));
        }
        let count = min(3, cluster.validator_instances().len() / 3);
        // Reboot different sets of 3 validators *100 times
        for _ in 0..10 {
            let b = Box::new(RebootRandomValidatorsParams::new(count, 0).build(cluster));
            experiments.push(b);
        }
        experiments.push(Box::new(
            PerformanceBenchmarkParams::non_zero_gas_price(0, 1)
                .enable_db_backup()
                .build(cluster),
        ));
        experiments.push(Box::new(
            PerformanceBenchmarkParams::new_nodes_down(0)
                .enable_db_backup()
                .build(cluster),
        ));
        experiments.push(Box::new(
            PerformanceBenchmarkParams::new_nodes_down(10)
                .enable_db_backup()
                .build(cluster),
        ));
        experiments.push(Box::new(
            PerformanceBenchmarkThreeRegionSimulationParams {}.build(cluster),
        ));
        experiments.push(Box::new(
            PerformanceBenchmarkParams::new_fixed_tps(0, 10)
                .enable_db_backup()
                .build(cluster),
        ));
        experiments.push(Box::new(StateSyncPerformanceParams::new(60).build(cluster)));
        experiments.push(Box::new(TwinValidatorsParams { pair: 1 }.build(cluster)));
        // This can't be run before any experiment that requires clean_data.
        experiments.push(Box::new(
            ReconfigurationParams {
                count: 101,
                emit_txn: false,
            }
            .build(cluster),
        ));
        experiments.push(Box::new(
            CpuFlamegraphParams { duration_secs: 60 }.build(cluster),
        ));
        Self { experiments }
    }

    fn new_twin_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> = vec![
            Box::new(TwinValidatorsParams { pair: 1 }.build(cluster)),
            Box::new(CpuFlamegraphParams { duration_secs: 60 }.build(cluster)),
        ];
        Self { experiments }
    }

    fn new_perf_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> = vec![
            Box::new(PerformanceBenchmarkParams::new_nodes_down(0).build(cluster)),
            Box::new(PerformanceBenchmarkParams::new_nodes_down(10).build(cluster)),
            Box::new(PerformanceBenchmarkThreeRegionSimulationParams {}.build(cluster)),
            Box::new(PerformanceBenchmarkParams::new_fixed_tps(0, 10).build(cluster)),
        ];
        Self { experiments }
    }

    fn new_land_blocking_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> = vec![Box::new(
            PerformanceBenchmarkParams::new_nodes_down(0).build(cluster),
        )];
        Self { experiments }
    }

    fn new_land_blocking_compat_suite(cluster: &Cluster) -> Result<Self> {
        let count: usize = match env::var("BATCH_SIZE") {
            Ok(val) => val
                .parse()
                .map_err(|e| format_err!("Failed to parse BATCH_SIZE {}: {}", val, e))?,
            Err(_) => cluster.validator_instances().len() / 2,
        };
        let updated_image_tag = env::var("UPDATE_TO_TAG")
            .map_err(|_| format_err!("Expected environment variable UPDATE_TO_TAG"))?;
        let mut experiments: Vec<Box<dyn Experiment>> = vec![Box::new(
            CompatiblityTestParams {
                count,
                updated_image_tag,
            }
            .build(cluster),
        )];
        experiments.extend(Self::new_land_blocking_suite(cluster).experiments);
        Ok(Self { experiments })
    }

    fn new_versioning_suite(cluster: &Cluster) -> Result<Self> {
        let count: usize = match env::var("BATCH_SIZE") {
            Ok(val) => val
                .parse()
                .map_err(|e| format_err!("Failed to parse BATCH_SIZE {}: {}", val, e))?,
            Err(_) => cluster.validator_instances().len() / 2,
        };
        let updated_image_tag = env::var("UPDATE_TO_TAG")
            .map_err(|_| format_err!("Expected environment variable UPDATE_TO_TAG"))?;
        let experiments: Vec<Box<dyn Experiment>> = vec![Box::new(
            ValidatorVersioningParams {
                count,
                updated_image_tag,
            }
            .build(cluster),
        )];
        Ok(Self { experiments })
    }

    fn new_invalid_tx_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> = vec![
            Box::new(PerformanceBenchmarkParams::new_nodes_down(0).build(cluster)),
            Box::new(PerformanceBenchmarkParams::mix_invalid_tx(0, 10).build(cluster)),
        ];
        Self { experiments }
    }

    fn new_state_sync_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> =
            vec![Box::new(StateSyncPerformanceParams::new(60).build(cluster))];
        Self { experiments }
    }

    pub fn new_by_name(cluster: &Cluster, name: &str) -> Result<Self> {
        match name {
            "perf" => Ok(Self::new_perf_suite(cluster)),
            "pre_release" => Ok(Self::new_pre_release(cluster)),
            "twin" => Ok(Self::new_twin_suite(cluster)),
            "land_blocking" => Ok(Self::new_land_blocking_suite(cluster)),
            "land_blocking_compat" => Self::new_land_blocking_compat_suite(cluster),
            "versioning" => Self::new_versioning_suite(cluster),
            "invalid" => Ok(Self::new_invalid_tx_suite(cluster)),
            "state_sync" => Ok(Self::new_state_sync_suite(cluster)),
            other => Err(format_err!("Unknown suite: {}", other)),
        }
    }
}
<commit_msg>[State Sync] Reduce the txn emitter time in the performance test.<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]
use std::{cmp::min, env};

use crate::{
    cluster::Cluster,
    experiments::{
        CompatiblityTestParams, CpuFlamegraphParams, Experiment, ExperimentParam,
        PerformanceBenchmarkParams, PerformanceBenchmarkThreeRegionSimulationParams,
        RebootRandomValidatorsParams, ReconfigurationParams, RecoveryTimeParams,
        StateSyncPerformanceParams, TwinValidatorsParams, ValidatorVersioningParams,
    },
};
use anyhow::{format_err, Result};

pub struct ExperimentSuite {
    pub experiments: Vec<Box<dyn Experiment>>,
}

impl ExperimentSuite {
    fn new_pre_release(cluster: &Cluster) -> Self {
        let mut experiments: Vec<Box<dyn Experiment>> = vec![];
        if env::var("RECOVERY_EXP").is_ok() {
            experiments.push(Box::new(
                RecoveryTimeParams {
                    num_accounts_to_mint: 100_000,
                }
                .build(cluster),
            ));
        }
        let count = min(3, cluster.validator_instances().len() / 3);
        // Reboot different sets of 3 validators *100 times
        for _ in 0..10 {
            let b = Box::new(RebootRandomValidatorsParams::new(count, 0).build(cluster));
            experiments.push(b);
        }
        experiments.push(Box::new(
            PerformanceBenchmarkParams::non_zero_gas_price(0, 1)
                .enable_db_backup()
                .build(cluster),
        ));
        experiments.push(Box::new(
            PerformanceBenchmarkParams::new_nodes_down(0)
                .enable_db_backup()
                .build(cluster),
        ));
        experiments.push(Box::new(
            PerformanceBenchmarkParams::new_nodes_down(10)
                .enable_db_backup()
                .build(cluster),
        ));
        experiments.push(Box::new(
            PerformanceBenchmarkThreeRegionSimulationParams {}.build(cluster),
        ));
        experiments.push(Box::new(
            PerformanceBenchmarkParams::new_fixed_tps(0, 10)
                .enable_db_backup()
                .build(cluster),
        ));
        experiments.push(Box::new(StateSyncPerformanceParams::new(30).build(cluster)));
        experiments.push(Box::new(TwinValidatorsParams { pair: 1 }.build(cluster)));
        // This can't be run before any experiment that requires clean_data.
        experiments.push(Box::new(
            ReconfigurationParams {
                count: 101,
                emit_txn: false,
            }
            .build(cluster),
        ));
        experiments.push(Box::new(
            CpuFlamegraphParams { duration_secs: 60 }.build(cluster),
        ));
        Self { experiments }
    }

    fn new_twin_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> = vec![
            Box::new(TwinValidatorsParams { pair: 1 }.build(cluster)),
            Box::new(CpuFlamegraphParams { duration_secs: 60 }.build(cluster)),
        ];
        Self { experiments }
    }

    fn new_perf_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> = vec![
            Box::new(PerformanceBenchmarkParams::new_nodes_down(0).build(cluster)),
            Box::new(PerformanceBenchmarkParams::new_nodes_down(10).build(cluster)),
            Box::new(PerformanceBenchmarkThreeRegionSimulationParams {}.build(cluster)),
            Box::new(PerformanceBenchmarkParams::new_fixed_tps(0, 10).build(cluster)),
        ];
        Self { experiments }
    }

    fn new_land_blocking_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> = vec![Box::new(
            PerformanceBenchmarkParams::new_nodes_down(0).build(cluster),
        )];
        Self { experiments }
    }

    fn new_land_blocking_compat_suite(cluster: &Cluster) -> Result<Self> {
        let count: usize = match env::var("BATCH_SIZE") {
            Ok(val) => val
                .parse()
                .map_err(|e| format_err!("Failed to parse BATCH_SIZE {}: {}", val, e))?,
            Err(_) => cluster.validator_instances().len() / 2,
        };
        let updated_image_tag = env::var("UPDATE_TO_TAG")
            .map_err(|_| format_err!("Expected environment variable UPDATE_TO_TAG"))?;
        let mut experiments: Vec<Box<dyn Experiment>> = vec![Box::new(
            CompatiblityTestParams {
                count,
                updated_image_tag,
            }
            .build(cluster),
        )];
        experiments.extend(Self::new_land_blocking_suite(cluster).experiments);
        Ok(Self { experiments })
    }

    fn new_versioning_suite(cluster: &Cluster) -> Result<Self> {
        let count: usize = match env::var("BATCH_SIZE") {
            Ok(val) => val
                .parse()
                .map_err(|e| format_err!("Failed to parse BATCH_SIZE {}: {}", val, e))?,
            Err(_) => cluster.validator_instances().len() / 2,
        };
        let updated_image_tag = env::var("UPDATE_TO_TAG")
            .map_err(|_| format_err!("Expected environment variable UPDATE_TO_TAG"))?;
        let experiments: Vec<Box<dyn Experiment>> = vec![Box::new(
            ValidatorVersioningParams {
                count,
                updated_image_tag,
            }
            .build(cluster),
        )];
        Ok(Self { experiments })
    }

    fn new_invalid_tx_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> = vec![
            Box::new(PerformanceBenchmarkParams::new_nodes_down(0).build(cluster)),
            Box::new(PerformanceBenchmarkParams::mix_invalid_tx(0, 10).build(cluster)),
        ];
        Self { experiments }
    }

    fn new_state_sync_suite(cluster: &Cluster) -> Self {
        let experiments: Vec<Box<dyn Experiment>> =
            vec![Box::new(StateSyncPerformanceParams::new(30).build(cluster))];
        Self { experiments }
    }

    pub fn new_by_name(cluster: &Cluster, name: &str) -> Result<Self> {
        match name {
            "perf" => Ok(Self::new_perf_suite(cluster)),
            "pre_release" => Ok(Self::new_pre_release(cluster)),
            "twin" => Ok(Self::new_twin_suite(cluster)),
            "land_blocking" => Ok(Self::new_land_blocking_suite(cluster)),
            "land_blocking_compat" => Self::new_land_blocking_compat_suite(cluster),
            "versioning" => Self::new_versioning_suite(cluster),
            "invalid" => Ok(Self::new_invalid_tx_suite(cluster)),
            "state_sync" => Ok(Self::new_state_sync_suite(cluster)),
            other => Err(format_err!("Unknown suite: {}", other)),
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::remove_file;
use std::process::{Command, Output};

const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];

pub fn compile_test_cmd(filename: &str) -> Output {
    Command::new("rustc")
        .args(&["--test", filename, "-o", "temp"])
        .args(RUSTC_COLOR_ARGS)
        .output()
        .expect("failed to compile exercise")
}

pub fn compile_cmd(filename: &str) -> Output {
    Command::new("rustc")
        .args(&[filename, "-o", "temp"])
        .args(RUSTC_COLOR_ARGS)
        .output()
        .expect("failed to compile exercise")
}

pub fn run_cmd() -> Output {
    Command::new("./temp")
        .output()
        .expect("failed to run exercise")
}

pub fn clean() {
    let _ignored = remove_file("temp");
}

#[test]
fn test_clean() {
    std::fs::File::create("temp").unwrap();
    clean();
    assert!(!std::path::Path::new("temp").exists());
}
<commit_msg>Add process id to temp file name<commit_after>use std::fs::remove_file;
use std::process::{self, Command, Output};

const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];

fn temp_file() -> String {
    format!("./temp_{}", process::id())
}

pub fn compile_test_cmd(filename: &str) -> Output {
    Command::new("rustc")
        .args(&["--test", filename, "-o", &temp_file()])
        .args(RUSTC_COLOR_ARGS)
        .output()
        .expect("failed to compile exercise")
}

pub fn compile_cmd(filename: &str) -> Output {
    Command::new("rustc")
        .args(&[filename, "-o", &temp_file()])
        .args(RUSTC_COLOR_ARGS)
        .output()
        .expect("failed to compile exercise")
}

pub fn run_cmd() -> Output {
    Command::new(&temp_file())
        .output()
        .expect("failed to run exercise")
}

pub fn clean() {
    let _ignored = remove_file(&temp_file());
}

#[test]
fn test_clean() {
    std::fs::File::create(&temp_file()).unwrap();
    clean();
    assert!(!std::path::Path::new(&temp_file()).exists());
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::aptos_cli::setup_cli_test;
use aptos::{account::create::DEFAULT_FUNDED_COINS, test::CliTestFramework};
use aptos_config::{config::ApiConfig, utils::get_available_port};
use aptos_crypto::HashValue;
use aptos_rosetta::common::{BLOCKCHAIN, Y2K_SECS};
use aptos_rosetta::types::{
    AccountBalanceResponse, Block, BlockIdentifier, NetworkIdentifier, NetworkRequest,
    PartialBlockIdentifier,
};
use aptos_rosetta::{
    client::RosettaClient,
    common::native_coin,
    types::{AccountBalanceRequest, BlockRequest},
    ROSETTA_VERSION,
};
use aptos_types::account_address::AccountAddress;
use aptos_types::chain_id::ChainId;
use forge::{LocalSwarm, Node};
use std::{future::Future, time::Duration};
use tokio::task::JoinHandle;

pub async fn setup_test(
    num_nodes: usize,
    num_accounts: usize,
) -> (LocalSwarm, CliTestFramework, JoinHandle<()>, RosettaClient) {
    let (swarm, cli, faucet) = setup_cli_test(num_nodes).await;
    let validator = swarm.validators().next().unwrap();

    // And the client
    let rosetta_port = get_available_port();
    let rosetta_socket_addr = format!("127.0.0.1:{}", rosetta_port);
    let rosetta_url = format!("http://{}", rosetta_socket_addr.clone())
        .parse()
        .unwrap();
    let rosetta_client = RosettaClient::new(rosetta_url);
    let api_config = ApiConfig {
        enabled: true,
        address: rosetta_socket_addr.parse().unwrap(),
        tls_cert_path: None,
        tls_key_path: None,
        content_length_limit: None,
    };

    // Start the server
    let _rosetta = aptos_rosetta::bootstrap_async(
        swarm.chain_id(),
        api_config,
        Some(aptos_rest_client::Client::new(
            validator.rest_api_endpoint(),
        )),
    )
    .await
    .unwrap();

    // Create accounts
    for i in 0..num_accounts {
        cli.create_account_with_faucet(i).await.unwrap();
    }
    (swarm, cli, faucet, rosetta_client)
}

#[tokio::test]
async fn test_network() {
    let (swarm, _, _, rosetta_client) = setup_test(1, 1).await;
    let chain_id = swarm.chain_id();

    // We only support one network, this network
    let networks = rosetta_client.network_list().await.unwrap();
    assert_eq!(1, networks.network_identifiers.len());
    let network_id = networks.network_identifiers.first().unwrap();
    assert_eq!(BLOCKCHAIN, network_id.blockchain);
    assert_eq!(chain_id.to_string(), network_id.network);

    let request = NetworkRequest {
        network_identifier: NetworkIdentifier::from(chain_id),
    };
    let options = rosetta_client.network_options(&request).await.unwrap();
    assert_eq!(ROSETTA_VERSION, options.version.rosetta_version);

    // TODO: Check other options

    let request = NetworkRequest {
        network_identifier: NetworkIdentifier::from(chain_id),
    };
    let status = rosetta_client.network_status(&request).await.unwrap();
    assert!(status.current_block_identifier.index > 0);
    assert!(status.current_block_timestamp > Y2K_SECS);
    assert_eq!(
        BlockIdentifier {
            index: 0,
            hash: HashValue::zero().to_hex()
        },
        status.genesis_block_identifier
    );
    assert_eq!(
        Some(status.genesis_block_identifier),
        status.oldest_block_identifier,
    );
}

#[tokio::test]
async fn test_account_balance() {
    let (swarm, cli, _faucet, rosetta_client) = setup_test(1, 1).await;

    cli.create_account_with_faucet(0).await.unwrap();
    let account = CliTestFramework::account_id(0);
    let chain_id = swarm.chain_id();

    // At time 0, there should be 0 balance
    let response = get_balance(&rosetta_client, chain_id, account, 0)
        .await
        .unwrap();
    assert_eq!(
        response.block_identifier,
        BlockIdentifier {
            index: 0,
            hash: HashValue::zero().to_hex(),
        }
    );

    // At some time before version 100, the account should exist
    let mut successful_version = None;
    for i in 1..100 {
        let response = get_balance(&rosetta_client, chain_id, account, i)
            .await
            .unwrap();
        let amount = response.balances.first().unwrap();
        if amount.value == DEFAULT_FUNDED_COINS.to_string() {
            successful_version = Some(i);
            break;
        }
    }

    if successful_version.is_none() {
        panic!("Failed to find account balance increase")
    }

    // TODO: Send money
    // TODO: Fail request due to bad transaction
    // TODO: Receive money
    // TODO: Recieve money by faucet
}

async fn get_balance(
    rosetta_client: &RosettaClient,
    chain_id: ChainId,
    account: AccountAddress,
    index: u64,
) -> anyhow::Result<AccountBalanceResponse> {
    let request = AccountBalanceRequest {
        network_identifier: chain_id.into(),
        account_identifier: account.into(),
        block_identifier: Some(PartialBlockIdentifier {
            index: Some(index),
            hash: None,
        }),
        currencies: Some(vec![native_coin()]),
    };
    try_until_ok(|| rosetta_client.account_balance(&request)).await
}

#[tokio::test]
async fn test_block() {
    let (swarm, _cli, _faucet, rosetta_client) = setup_test(1, 0).await;
    let chain_id = swarm.chain_id();

    let request_genesis = BlockRequest::by_index(chain_id, 0);
    let by_version_response = try_until_ok(|| rosetta_client.block(&request_genesis))
        .await
        .unwrap();
    let genesis_block = by_version_response.block.unwrap();

    // Genesis txn should always have parent be same as block
    assert_eq!(
        genesis_block.block_identifier,
        genesis_block.parent_block_identifier
    );
    assert_eq!(
        HashValue::zero().to_hex(),
        genesis_block.block_identifier.hash,
    );
    assert_eq!(0, genesis_block.block_identifier.index);

    // Genesis timestamp is always Y2K
    assert_eq!(Y2K_SECS, genesis_block.timestamp);

    // There should only be the genesis transaction
    assert_eq!(1, genesis_block.transactions.len());
    let genesis_txn = genesis_block.transactions.first().unwrap();

    // Version should match as 0
    assert_eq!(
        0,
        genesis_txn.metadata.unwrap().version.0,
        "Genesis version"
    );

    // There should be at least one transfer in genesis
    assert!(!genesis_txn.operations.is_empty());

    // Get genesis txn by hash
    let request_genesis_by_hash =
        BlockRequest::by_hash(chain_id, genesis_block.block_identifier.hash.clone());
    let by_hash_response = rosetta_client
        .block(&request_genesis_by_hash)
        .await
        .unwrap();
    let genesis_block_by_hash = by_hash_response.block.unwrap();

    // Both blocks should be the same
    assert_eq!(genesis_block, genesis_block_by_hash);

    // Responses should be idempotent
    let response = rosetta_client.block(&request_genesis).await.unwrap();
    assert_eq!(response.block.unwrap(), genesis_block_by_hash);
    let response = rosetta_client
        .block(&request_genesis_by_hash)
        .await
        .unwrap();
    assert_eq!(response.block.unwrap(), genesis_block_by_hash);

    // Block 1 is always a reconfig with exactly 1 txn
    let block_1 = get_block(&rosetta_client, chain_id, 1).await;
    assert_eq!(1, block_1.transactions.len());
    // Block metadata won't have operations
    assert!(block_1.transactions.first().unwrap().operations.is_empty());
    assert!(block_1.timestamp > genesis_block.timestamp);

    // Block 2 is always a standard block with 2 or more txns
    let block_2 = get_block(&rosetta_client, chain_id, 2).await;
    assert!(block_2.transactions.len() >= 2);
    // Block metadata won't have operations
    assert!(block_2.transactions.first().unwrap().operations.is_empty());
    // StateCheckpoint won't have operations
    assert!(block_2.transactions.last().unwrap().operations.is_empty());
    assert!(block_2.timestamp >= block_1.timestamp);

    // No input should give the latest version, not the genesis txn
    let request_latest = BlockRequest::latest(chain_id);
    let response = rosetta_client.block(&request_latest).await.unwrap();
    let latest_block = response.block.unwrap();

    // The latest block should always come after genesis
    assert!(latest_block.block_identifier.index >= block_2.block_identifier.index);
    assert!(latest_block.timestamp >= block_2.timestamp);

    // The parent should always be exactly one version before
    assert_eq!(
        latest_block.parent_block_identifier.index,
        latest_block.block_identifier.index - 1
    );

    // There should be at least 1 txn
    assert!(latest_block.transactions.len() > 1);

    // We should be able to query it again by hash or by version and it is the same
    let request_latest_by_version =
        BlockRequest::by_index(chain_id, latest_block.block_identifier.index);
    let latest_block_by_version = rosetta_client
        .block(&request_latest_by_version)
        .await
        .unwrap()
        .block
        .unwrap();
    let request_latest_by_hash =
        BlockRequest::by_hash(chain_id, latest_block.block_identifier.hash.clone());
    let latest_block_by_hash = rosetta_client
        .block(&request_latest_by_hash)
        .await
        .unwrap()
        .block
        .unwrap();

    assert_eq!(latest_block, latest_block_by_version);
    assert_eq!(latest_block_by_hash, latest_block_by_version);

    // Wait until we get a new block processed
    let network_request = NetworkRequest {
        network_identifier: NetworkIdentifier::from(chain_id),
    };
    while rosetta_client
        .network_status(&network_request)
        .await
        .unwrap()
        .current_block_identifier
        .index
        == latest_block.block_identifier.index
    {
        tokio::time::sleep(Duration::from_micros(10)).await
    }

    // And querying latest again should get yet another transaction in the future
    let newer_block = rosetta_client
        .block(&request_latest)
        .await
        .unwrap()
        .block
        .unwrap();
    assert!(newer_block.block_identifier.index >= latest_block.block_identifier.index);
    assert!(newer_block.timestamp >= latest_block.timestamp);
}

async fn get_block(rosetta_client: &RosettaClient, chain_id: ChainId, index: u64) -> Block {
    let rosetta_client = (*rosetta_client).clone();
    let request = BlockRequest::by_index(chain_id, index);
    try_until_ok(|| rosetta_client.block(&request))
        .await
        .unwrap()
        .block
        .unwrap()
}

/// Try for 2 seconds to get a response.  This handles the fact that it's starting async
async fn try_until_ok<F, Fut, T>(function: F) -> anyhow::Result<T>
where
    F: Fn() -> Fut,
    Fut: Future<Output = anyhow::Result<T>>,
{
    let mut result = Err(anyhow::Error::msg("Failed to get response"));
    for _ in 1..10 {
        result = function().await;
        if result.is_ok() {
            break;
        }
        tokio::time::sleep(Duration::from_millis(200)).await;
    }

    result
}
<commit_msg>[aptos-rosetta] Refactor e2e tests<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::aptos_cli::setup_cli_test;
use aptos::{account::create::DEFAULT_FUNDED_COINS, test::CliTestFramework};
use aptos_config::{config::ApiConfig, utils::get_available_port};
use aptos_crypto::HashValue;
use aptos_rosetta::common::{BLOCKCHAIN, Y2K_SECS};
use aptos_rosetta::types::{
    AccountBalanceResponse, Block, BlockIdentifier, NetworkIdentifier, NetworkRequest,
    PartialBlockIdentifier,
};
use aptos_rosetta::{
    client::RosettaClient,
    common::native_coin,
    types::{AccountBalanceRequest, BlockRequest},
    ROSETTA_VERSION,
};
use aptos_types::account_address::AccountAddress;
use aptos_types::chain_id::ChainId;
use forge::{LocalSwarm, Node};
use std::{future::Future, time::Duration};
use tokio::task::JoinHandle;

pub async fn setup_test(
    num_nodes: usize,
    num_accounts: usize,
) -> (LocalSwarm, CliTestFramework, JoinHandle<()>, RosettaClient) {
    let (swarm, cli, faucet) = setup_cli_test(num_nodes).await;
    let validator = swarm.validators().next().unwrap();

    // And the client
    let rosetta_port = get_available_port();
    let rosetta_socket_addr = format!("127.0.0.1:{}", rosetta_port);
    let rosetta_url = format!("http://{}", rosetta_socket_addr.clone())
        .parse()
        .unwrap();
    let rosetta_client = RosettaClient::new(rosetta_url);
    let api_config = ApiConfig {
        enabled: true,
        address: rosetta_socket_addr.parse().unwrap(),
        tls_cert_path: None,
        tls_key_path: None,
        content_length_limit: None,
    };

    // Start the server
    let _rosetta = aptos_rosetta::bootstrap_async(
        swarm.chain_id(),
        api_config,
        Some(aptos_rest_client::Client::new(
            validator.rest_api_endpoint(),
        )),
    )
    .await
    .unwrap();

    // Create accounts
    for i in 0..num_accounts {
        cli.create_account_with_faucet(i).await.unwrap();
    }
    (swarm, cli, faucet, rosetta_client)
}

#[tokio::test]
async fn test_network() {
    let (swarm, _, _, rosetta_client) = setup_test(1, 1).await;
    let chain_id = swarm.chain_id();

    // We only support one network, this network
    let networks = rosetta_client.network_list().await.unwrap();
    assert_eq!(1, networks.network_identifiers.len());
    let network_id = networks.network_identifiers.first().unwrap();
    assert_eq!(BLOCKCHAIN, network_id.blockchain);
    assert_eq!(chain_id.to_string(), network_id.network);

    let request = NetworkRequest {
        network_identifier: NetworkIdentifier::from(chain_id),
    };
    let options = rosetta_client.network_options(&request).await.unwrap();
    assert_eq!(ROSETTA_VERSION, options.version.rosetta_version);

    // TODO: Check other options

    let request = NetworkRequest {
        network_identifier: NetworkIdentifier::from(chain_id),
    };
    let status = rosetta_client.network_status(&request).await.unwrap();
    assert!(status.current_block_identifier.index > 0);
    assert!(status.current_block_timestamp > Y2K_SECS);
    assert_eq!(
        BlockIdentifier {
            index: 0,
            hash: HashValue::zero().to_hex()
        },
        status.genesis_block_identifier
    );
    assert_eq!(
        Some(status.genesis_block_identifier),
        status.oldest_block_identifier,
    );
}

#[tokio::test]
async fn test_account_balance() {
    let (swarm, cli, _faucet, rosetta_client) = setup_test(1, 1).await;

    cli.create_account_with_faucet(0).await.unwrap();
    let account = CliTestFramework::account_id(0);
    let chain_id = swarm.chain_id();

    // At time 0, there should be 0 balance
    let response = get_balance(&rosetta_client, chain_id, account, 0)
        .await
        .unwrap();
    assert_eq!(
        response.block_identifier,
        BlockIdentifier {
            index: 0,
            hash: HashValue::zero().to_hex(),
        }
    );

    // At some time before version 100, the account should exist
    let mut successful_version = None;
    for i in 1..100 {
        let response = get_balance(&rosetta_client, chain_id, account, i)
            .await
            .unwrap();
        let amount = response.balances.first().unwrap();
        if amount.value == DEFAULT_FUNDED_COINS.to_string() {
            successful_version = Some(i);
            break;
        }
    }

    if successful_version.is_none() {
        panic!("Failed to find account balance increase")
    }

    // TODO: Send money
    // TODO: Fail request due to bad transaction
    // TODO: Receive money
    // TODO: Recieve money by faucet
}

async fn get_balance(
    rosetta_client: &RosettaClient,
    chain_id: ChainId,
    account: AccountAddress,
    index: u64,
) -> anyhow::Result<AccountBalanceResponse> {
    let request = AccountBalanceRequest {
        network_identifier: chain_id.into(),
        account_identifier: account.into(),
        block_identifier: Some(PartialBlockIdentifier {
            index: Some(index),
            hash: None,
        }),
        currencies: Some(vec![native_coin()]),
    };
    try_until_ok(|| rosetta_client.account_balance(&request)).await
}

#[tokio::test]
async fn test_block() {
    let (swarm, _cli, _faucet, rosetta_client) = setup_test(1, 0).await;
    let chain_id = swarm.chain_id();

    // Genesis by version
    let request_genesis = BlockRequest::by_index(chain_id, 0);
    let genesis_block = try_until_ok(|| rosetta_client.block(&request_genesis))
        .await
        .unwrap().block.unwrap();
    assert_genesis_block(&genesis_block);

    // Get genesis txn by hash
    let request_genesis_by_hash =
        BlockRequest::by_hash(chain_id, genesis_block.block_identifier.hash.clone());
    let genesis_block_by_hash = rosetta_client
        .block(&request_genesis_by_hash)
        .await
        .unwrap().block.unwrap();

    // Both blocks should be the same
    assert_eq!(genesis_block, genesis_block_by_hash, "Genesis by hash or by index should be the same");

    // Responses should be idempotent
    let idempotent_block = rosetta_client.block(&request_genesis).await.unwrap().block.unwrap();
    assert_eq!(idempotent_block, genesis_block_by_hash, "Blocks should be idempotent");

    // Block 1 is always a reconfig with exactly 1 txn
    let block_1 = get_block(&rosetta_client, chain_id, 1).await;
    assert_eq!(1, block_1.transactions.len());
    // Block metadata won't have operations
    assert!(block_1.transactions.first().unwrap().operations.is_empty());
    assert!(block_1.timestamp > genesis_block.timestamp);

    // Block 2 is always a standard block with 2 or more txns
    let block_2 = get_block(&rosetta_client, chain_id, 2).await;
    assert!(block_2.transactions.len() >= 2);
    // Block metadata won't have operations
    assert!(block_2.transactions.first().unwrap().operations.is_empty());
    // StateCheckpoint won't have operations
    assert!(block_2.transactions.last().unwrap().operations.is_empty());
    assert!(block_2.timestamp >= block_1.timestamp);

    // No input should give the latest version, not the genesis txn
    let request_latest = BlockRequest::latest(chain_id);
    let response = rosetta_client.block(&request_latest).await.unwrap();
    let latest_block = response.block.unwrap();

    // The latest block should always come after genesis
    assert!(latest_block.block_identifier.index >= block_2.block_identifier.index);
    assert!(latest_block.timestamp >= block_2.timestamp);

    // The parent should always be exactly one version before
    assert_eq!(
        latest_block.parent_block_identifier.index,
        latest_block.block_identifier.index - 1
    );

    // There should be at least 1 txn
    assert!(latest_block.transactions.len() > 1);

    // We should be able to query it again by hash or by version and it is the same
    let request_latest_by_version =
        BlockRequest::by_index(chain_id, latest_block.block_identifier.index);
    let latest_block_by_version = rosetta_client
        .block(&request_latest_by_version)
        .await
        .unwrap()
        .block
        .unwrap();
    let request_latest_by_hash =
        BlockRequest::by_hash(chain_id, latest_block.block_identifier.hash.clone());
    let latest_block_by_hash = rosetta_client
        .block(&request_latest_by_hash)
        .await
        .unwrap()
        .block
        .unwrap();

    assert_eq!(latest_block, latest_block_by_version);
    assert_eq!(latest_block_by_hash, latest_block_by_version);

    // Wait until we get a new block processed
    let network_request = NetworkRequest {
        network_identifier: NetworkIdentifier::from(chain_id),
    };
    while rosetta_client
        .network_status(&network_request)
        .await
        .unwrap()
        .current_block_identifier
        .index
        == latest_block.block_identifier.index
    {
        tokio::time::sleep(Duration::from_micros(10)).await
    }

    // And querying latest again should get yet another transaction in the future
    let newer_block = rosetta_client
        .block(&request_latest)
        .await
        .unwrap()
        .block
        .unwrap();
    assert!(newer_block.block_identifier.index >= latest_block.block_identifier.index);
    assert!(newer_block.timestamp >= latest_block.timestamp);
}

fn assert_genesis_block(block: &Block) {
    assert_eq!(
        genesis_block.block_identifier, genesis_block.parent_block_identifier,
        "The genesis block is also it's own parent"
    );
    assert_eq!(
        HashValue::zero().to_hex(),
        block.block_identifier.hash,
        "The genesis block hash is always 0s"
    );
    assert_eq!(
        0, block.block_identifier.index,
        "The genesis block index is always 0"
    );

    assert_eq!(
        Y2K_SECS, block.timestamp,
        "The genesis timestamp should be Y2K seconds"
    );
    assert_eq!(
        1,
        block.transactions.len(),
        "The genesis block should be exactly 1 transaction"
    );

    let genesis_txn = block.transactions.first().unwrap();
    assert_eq!(
        0,
        genesis_txn.metadata.unwrap().version.0,
        "Genesis version should be 0"
    );
    assert_ne!(
        HashValue::zero().to_hex(),
        genesis_txn.transaction_identifier.hash,
        "Genesis should have a txn hash"
    );

    assert!(
        !genesis_txn.operations.is_empty(),
        "There should be at least one operation in genesis"
    );
}

async fn get_block(rosetta_client: &RosettaClient, chain_id: ChainId, index: u64) -> Block {
    let rosetta_client = (*rosetta_client).clone();
    let request = BlockRequest::by_index(chain_id, index);
    try_until_ok(|| rosetta_client.block(&request))
        .await
        .unwrap()
        .block
        .unwrap()
}

/// Try for 2 seconds to get a response.  This handles the fact that it's starting async
async fn try_until_ok<F, Fut, T>(function: F) -> anyhow::Result<T>
where
    F: Fn() -> Fut,
    Fut: Future<Output = anyhow::Result<T>>,
{
    let mut result = Err(anyhow::Error::msg("Failed to get response"));
    for _ in 1..10 {
        result = function().await;
        if result.is_ok() {
            break;
        }
        tokio::time::sleep(Duration::from_millis(200)).await;
    }

    result
}
<|endoftext|>


<commit_before>use crate::text::{Attributes, Text};
use crate::widgets::{Widget, WidgetStream};
use alsa::mixer::{SelemChannelId, SelemId};
use alsa::{self, Mixer, PollDescriptors};
use anyhow::{anyhow, Context, Result};
use std::os::unix::io::AsRawFd;
use std::os::unix::io::RawFd;
use std::pin::Pin;
use std::task::Poll;
use tokio::io::unix::AsyncFd;
use tokio_stream::{Stream, StreamExt};

/// Shows the current volume of the default ALSA output.
///
/// This widget shows the current volume of the default ALSA output, or '`M`' if
/// the output is muted.
///
/// The widget uses `alsa-lib` to receive events when the volume changes,
/// avoiding expensive polling. If you do not have `alsa-lib` installed, you
/// can disable the `volume-widget` feature on the `cnx` crate to avoid
/// compiling this widget.
pub struct Volume {
    attr: Attributes,
}

impl Volume {
    /// Creates a new Volume widget.
    ///
    /// Creates a new `Volume` widget, whose text will be displayed
    /// with the given [`Attributes`].
    ///
    /// The [`Cnx`] instance is borrowed during construction in order to get
    /// access to handles of its event loop. However, it is not borrowed for the
    /// lifetime of the widget. See the [`cnx_add_widget!()`] for more discussion
    /// about the lifetime of the borrow.
    ///
    /// [`Attributes`]: ../text/struct.Attributes.html
    /// [`Cnx`]: ../struct.Cnx.html
    /// [`cnx_add_widget!()`]: ../macro.cnx_add_widget.html
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use]
    /// # extern crate cnx;
    /// #
    /// # use cnx::*;
    /// # use cnx::text::*;
    /// # use cnx::widgets::*;
    /// # use anyhow::Result;
    /// #
    /// # fn run() -> Result<()> {
    /// let attr = Attributes {
    ///     font: Font::new("SourceCodePro 21"),
    ///     fg_color: Color::white(),
    ///     bg_color: None,
    ///     padding: Padding::new(8.0, 8.0, 0.0, 0.0),
    /// };
    ///
    /// let mut cnx = Cnx::new(Position::Top);
    /// cnx.add_widget(volume::Volume::new(attr.clone()));
    /// # Ok(())
    /// # }
    /// # fn main() { run().unwrap(); }
    /// ```
    pub fn new(attr: Attributes) -> Volume {
        Volume { attr }
    }
}

// https://github.com/mjkillough/cnx/blob/92c24238be541c75d88181208862505739be33fd/src/widgets/volume.rs

impl Widget for Volume {
    fn into_stream(self: Box<Self>) -> Result<WidgetStream> {
        let mixer_name = "default";
        // We don't attempt to use the same mixer to listen for events and to
        // recompute the mixer state (in the callback below) as the Mixer seems
        // to cache the state from when it was created. It's relatively cheap
        // create a new mixer each time we get an event though.
        let mixer = Mixer::new(mixer_name, true)
            .with_context(|| format!("Failed to open ALSA mixer: {}", mixer_name))?;
        let stream = AlsaEventStream::new(mixer)?.map(move |()| {
            // FrontLeft has special meaning in ALSA and is the channel
            // that's used when the mixer is mono.
            let channel = SelemChannelId::FrontLeft;

            let mixer = Mixer::new(mixer_name, true)?;
            let master = mixer.find_selem(&SelemId::new("Master", 0))
             .ok_or_else(|| anyhow!("Couldn't open Master channel"))?;

            let mute = master.get_playback_switch(channel)? == 0;

            let text = if !mute {
                let volume = master.get_playback_volume(channel)?;
                let (min, max) = master.get_playback_volume_range();
                let percentage = (volume as f64 / (max as f64 - min as f64)) * 100.0;
                format!("[🔈 {:.0}%]", percentage)
            } else {
                "🔇".to_owned()
            };

            Ok(vec![Text {
                attr: self.attr.clone(),
                text,
                stretch: false,
                markup: true,
            }])
        });

        Ok(Box::pin(stream))
    }
}

struct AlsaEvented(Mixer);

impl AlsaEvented {
    fn mixer(&self) -> &Mixer {
        &self.0
    }

    fn fds(&self) -> Vec<RawFd> {
        self.0.get().map_or(vec![], |vec_poll| {
            vec_poll.iter().map(|pollfd| pollfd.fd).collect()
        })
    }
}

struct AlsaEventStream {
    poll: AsyncFd<AlsaEvented>,
    initial: bool,
}

impl AsRawFd for AlsaEvented {
    fn as_raw_fd(&self) -> RawFd {
        self.fds()
            .into_iter()
            .next()
            .expect("volume: as_raw_fd empty")
    }
}

impl AlsaEventStream {
    fn new(mixer: Mixer) -> Result<AlsaEventStream> {
        Ok(AlsaEventStream {
            poll: AsyncFd::new(AlsaEvented(mixer))?,
            // The first few calls to poll() need to process any existing events.
            // We don't know what state the fds are in when we give them to tokio
            // and it's edge-triggered.
            initial: true,
        })
    }
}

impl Stream for AlsaEventStream {
    // We don't bother yielding the events and just yield unit each time we get
    // an event. This stream is used only to get woken up when the ALSA state
    // changes - the caller is expected to requery all necessary state when
    // it receives a new item from the stream.
    type Item = ();

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut std::task::Context,
    ) -> Poll<Option<Self::Item>> {
        // Always assume we're ready initially, so that we can clear the
        // state of the fds.

        // Do a poll with a timeout of 0 to figure out exactly which fds were
        // woken up, followed by a call to revents() which clears the pending
        // events. We don't actually care what the events are - we're just
        // using it as a wake-up so we can check the volume again.
        if self.initial {
            let mixer = self.poll.get_ref().mixer();
            let _poll_result = alsa::poll::poll_all(&[mixer], 0);
            self.initial = false;
            return Poll::Ready(Some(()));
        }
        // All events have been consumed - tell Tokio we're interested in waiting
        // for more again.
        match self.poll.poll_read_ready(cx) {
            Poll::Ready(Ok(mut r)) => {
                let mixer = self.poll.get_ref().mixer();
                let _poll_result = alsa::poll::poll_all(&[mixer], 0);
                let _result = mixer.handle_events();
                r.clear_ready();
                Poll::Ready(Some(()))
            }
            Poll::Ready(Err(_)) => Poll::Ready(Some(())),
            Poll::Pending => Poll::Pending,
        }
    }
}
<commit_msg>Send None on error<commit_after>use crate::text::{Attributes, Text};
use crate::widgets::{Widget, WidgetStream};
use alsa::mixer::{SelemChannelId, SelemId};
use alsa::{self, Mixer, PollDescriptors};
use anyhow::{anyhow, Context, Result};
use std::os::unix::io::AsRawFd;
use std::os::unix::io::RawFd;
use std::pin::Pin;
use std::task::Poll;
use tokio::io::unix::AsyncFd;
use tokio_stream::{Stream, StreamExt};

/// Shows the current volume of the default ALSA output.
///
/// This widget shows the current volume of the default ALSA output, or '`M`' if
/// the output is muted.
///
/// The widget uses `alsa-lib` to receive events when the volume changes,
/// avoiding expensive polling. If you do not have `alsa-lib` installed, you
/// can disable the `volume-widget` feature on the `cnx` crate to avoid
/// compiling this widget.
pub struct Volume {
    attr: Attributes,
}

impl Volume {
    /// Creates a new Volume widget.
    ///
    /// Creates a new `Volume` widget, whose text will be displayed
    /// with the given [`Attributes`].
    ///
    /// The [`Cnx`] instance is borrowed during construction in order to get
    /// access to handles of its event loop. However, it is not borrowed for the
    /// lifetime of the widget. See the [`cnx_add_widget!()`] for more discussion
    /// about the lifetime of the borrow.
    ///
    /// [`Attributes`]: ../text/struct.Attributes.html
    /// [`Cnx`]: ../struct.Cnx.html
    /// [`cnx_add_widget!()`]: ../macro.cnx_add_widget.html
    ///
    /// # Examples
    ///
    /// ```
    /// # #[macro_use]
    /// # extern crate cnx;
    /// #
    /// # use cnx::*;
    /// # use cnx::text::*;
    /// # use cnx::widgets::*;
    /// # use anyhow::Result;
    /// #
    /// # fn run() -> Result<()> {
    /// let attr = Attributes {
    ///     font: Font::new("SourceCodePro 21"),
    ///     fg_color: Color::white(),
    ///     bg_color: None,
    ///     padding: Padding::new(8.0, 8.0, 0.0, 0.0),
    /// };
    ///
    /// let mut cnx = Cnx::new(Position::Top);
    /// cnx.add_widget(volume::Volume::new(attr.clone()));
    /// # Ok(())
    /// # }
    /// # fn main() { run().unwrap(); }
    /// ```
    pub fn new(attr: Attributes) -> Volume {
        Volume { attr }
    }
}

// https://github.com/mjkillough/cnx/blob/92c24238be541c75d88181208862505739be33fd/src/widgets/volume.rs

impl Widget for Volume {
    fn into_stream(self: Box<Self>) -> Result<WidgetStream> {
        let mixer_name = "default";
        // We don't attempt to use the same mixer to listen for events and to
        // recompute the mixer state (in the callback below) as the Mixer seems
        // to cache the state from when it was created. It's relatively cheap
        // create a new mixer each time we get an event though.
        let mixer = Mixer::new(mixer_name, true)
            .with_context(|| format!("Failed to open ALSA mixer: {}", mixer_name))?;
        let stream = AlsaEventStream::new(mixer)?.map(move |()| {
            // FrontLeft has special meaning in ALSA and is the channel
            // that's used when the mixer is mono.
            let channel = SelemChannelId::FrontLeft;

            let mixer = Mixer::new(mixer_name, true)?;
            let master = mixer.find_selem(&SelemId::new("Master", 0))
             .ok_or_else(|| anyhow!("Couldn't open Master channel"))?;

            let mute = master.get_playback_switch(channel)? == 0;

            let text = if !mute {
                let volume = master.get_playback_volume(channel)?;
                let (min, max) = master.get_playback_volume_range();
                let percentage = (volume as f64 / (max as f64 - min as f64)) * 100.0;
                format!("[🔈 {:.0}%]", percentage)
            } else {
                "🔇".to_owned()
            };

            Ok(vec![Text {
                attr: self.attr.clone(),
                text,
                stretch: false,
                markup: true,
            }])
        });

        Ok(Box::pin(stream))
    }
}

struct AlsaEvented(Mixer);

impl AlsaEvented {
    fn mixer(&self) -> &Mixer {
        &self.0
    }

    fn fds(&self) -> Vec<RawFd> {
        self.0.get().map_or(vec![], |vec_poll| {
            vec_poll.iter().map(|pollfd| pollfd.fd).collect()
        })
    }
}

struct AlsaEventStream {
    poll: AsyncFd<AlsaEvented>,
    initial: bool,
}

impl AsRawFd for AlsaEvented {
    fn as_raw_fd(&self) -> RawFd {
        self.fds()
            .into_iter()
            .next()
            .expect("volume: as_raw_fd empty")
    }
}

impl AlsaEventStream {
    fn new(mixer: Mixer) -> Result<AlsaEventStream> {
        Ok(AlsaEventStream {
            poll: AsyncFd::new(AlsaEvented(mixer))?,
            // The first few calls to poll() need to process any existing events.
            // We don't know what state the fds are in when we give them to tokio
            // and it's edge-triggered.
            initial: true,
        })
    }
}

impl Stream for AlsaEventStream {
    // We don't bother yielding the events and just yield unit each time we get
    // an event. This stream is used only to get woken up when the ALSA state
    // changes - the caller is expected to requery all necessary state when
    // it receives a new item from the stream.
    type Item = ();

    fn poll_next(
        mut self: Pin<&mut Self>,
        cx: &mut std::task::Context,
    ) -> Poll<Option<Self::Item>> {
        // Always assume we're ready initially, so that we can clear the
        // state of the fds.

        // Do a poll with a timeout of 0 to figure out exactly which fds were
        // woken up, followed by a call to revents() which clears the pending
        // events. We don't actually care what the events are - we're just
        // using it as a wake-up so we can check the volume again.
        if self.initial {
            let mixer = self.poll.get_ref().mixer();
            let _poll_result = alsa::poll::poll_all(&[mixer], 0);
            self.initial = false;
            return Poll::Ready(Some(()));
        }
        // All events have been consumed - tell Tokio we're interested in waiting
        // for more again.
        match self.poll.poll_read_ready(cx) {
            Poll::Ready(Ok(mut r)) => {
                let mixer = self.poll.get_ref().mixer();
                let _poll_result = alsa::poll::poll_all(&[mixer], 0);
                let _result = mixer.handle_events();
                r.clear_ready();
                Poll::Ready(Some(()))
            }
            Poll::Ready(Err(_)) => Poll::Ready(None),
            Poll::Pending => Poll::Pending,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2019 Fabian Schuiki

mod common;
use common::*;

#[test]
fn empty_module() {
    simple_logger::init().unwrap();
    let sess = Session::new();
    let ast = parse("module foo; endmodule");
    let gcx = GlobalContext::new(&sess);
    let cx = Context::new(&gcx);
    cx.add_root_nodes(&ast).unwrap();
    let m = cx.find_global_item("foo".into()).unwrap();
    let code = cx.generate_code(m.into()).unwrap();
    let asm = module_to_string(&code);
    eprintln!("{}", asm.trim());
    assert_eq!(asm.trim(), "entity @foo () () {\n}");
}
<commit_msg>svlog: refactor codegen test<commit_after>// Copyright (c) 2016-2019 Fabian Schuiki

mod common;
use common::*;

/// Compile a module in a piece of verilog code and return the LLHD.
fn compile_module(name: &str, code: &str) -> String {
    simple_logger::init().is_ok();
    let sess = Session::new();
    let ast = parse(code);
    let gcx = GlobalContext::new(&sess);
    let cx = Context::new(&gcx);
    cx.add_root_nodes(&ast).unwrap();
    let m = cx.find_global_item(name.into()).unwrap();
    let code = cx.generate_code(m.into()).unwrap();
    module_to_string(&code)
}

#[test]
fn empty_module() {
    assert_eq!(
        compile_module("foo", "module foo; endmodule").trim(),
        "entity @foo () () {\n}"
    );
}

// #[test]
// fn module_with_trivial_ports() {
//     assert_eq!(
//         compile_module("foo", "module foo (input bit a, output bit z); endmodule").trim(),
//         "entity @foo ($i1 %a) ($i1 %b) {\n}"
//     );
// }
<|endoftext|>