blob: af159cb21d922afef7c41d6282da3a260d06d21c [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Tests all functionality in the system package
//!
//! Test failure is defined as the function returning via panicking
//! and the result being caught in the test! macro. If a test function
//! returns without panicking, it is assumed to pass.
#[macro_use]
extern crate mojo;
#[macro_use]
mod util;
use mojo::bindings::run_loop;
use mojo::bindings::run_loop::{RunLoop, Token, Handler, WaitError};
use mojo::system::MOJO_INDEFINITE;
use mojo::system::message_pipe;
use std::cell::Cell;
use std::rc::Rc;
struct HandlerExpectReady {}
impl Handler for HandlerExpectReady {
fn on_ready(&mut self, runloop: &mut RunLoop, token: Token) {
runloop.deregister(token);
}
fn on_timeout(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Timed-out when expected ready");
}
fn on_error(&mut self, _runloop: &mut RunLoop, _token: Token, _error: WaitError) {
panic!("Error when expected ready");
}
}
struct HandlerExpectTimeout {}
impl Handler for HandlerExpectTimeout {
fn on_ready(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Ready when expected timeout");
}
fn on_timeout(&mut self, runloop: &mut RunLoop, token: Token) {
runloop.deregister(token);
}
fn on_error(&mut self, _runloop: &mut RunLoop, _token: Token, _error: WaitError) {
panic!("Error when expected timeout");
}
}
struct HandlerExpectError {}
impl Handler for HandlerExpectError {
fn on_ready(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Ready when expected error");
}
fn on_timeout(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Timed-out when expected error");
}
fn on_error(&mut self, runloop: &mut RunLoop, token: Token, error: WaitError) {
assert_eq!(error, WaitError::Unsatisfiable);
runloop.deregister(token);
}
}
struct HandlerQuit {}
impl Handler for HandlerQuit {
fn on_ready(&mut self, runloop: &mut RunLoop, _token: Token) {
runloop.quit();
}
fn on_timeout(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Timed-out when expected error");
}
fn on_error(&mut self, _runloop: &mut RunLoop, _token: Token, _error: WaitError) {
panic!("Error when expected ready");
}
}
struct HandlerRegister {}
impl Handler for HandlerRegister {
fn on_ready(&mut self, runloop: &mut RunLoop, token: Token) {
let (_, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
let _ = runloop.register(&endpt1, signals!(Signals::Writable), MOJO_INDEFINITE, HandlerDeregisterOther { other: token });
}
fn on_timeout(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Timed-out when expected error");
}
fn on_error(&mut self, _runloop: &mut RunLoop, _token: Token, _error: WaitError) {
panic!("Error when expected ready");
}
}
struct HandlerDeregisterOther {
other: Token,
}
impl Handler for HandlerDeregisterOther {
fn on_ready(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Ready when expected error");
}
fn on_timeout(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Timed-out when expected error");
}
fn on_error(&mut self, runloop: &mut RunLoop, token: Token, error: WaitError) {
assert_eq!(error, WaitError::HandleClosed);
runloop.deregister(token);
runloop.deregister(self.other.clone());
}
}
struct HandlerReregister {
count: u64,
}
impl Handler for HandlerReregister {
fn on_ready(&mut self, runloop: &mut RunLoop, token: Token) {
runloop.deregister(token);
}
fn on_timeout(&mut self, runloop: &mut RunLoop, token: Token) {
if self.count < 10 {
runloop.reregister(&token, signals!(Signals::Readable), 0);
self.count += 1;
} else {
runloop.reregister(&token, signals!(Signals::Writable), MOJO_INDEFINITE);
}
}
fn on_error(&mut self, _runloop: &mut RunLoop, _token: Token, _error: WaitError) {
panic!("Error when expected ready");
}
}
struct HandlerNesting {
count: u64,
}
impl Handler for HandlerNesting {
fn on_ready(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Ready when expected timeout");
}
fn on_timeout(&mut self, runloop: &mut RunLoop, token: Token) {
let mut nested_runloop = run_loop::RunLoop::new();
if self.count < 10 {
let handler = HandlerNesting { count: self.count + 1 };
let (endpt0, _endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
let _ = nested_runloop.register(&endpt0, signals!(Signals::Readable), 0, handler);
nested_runloop.run();
} else {
let handler = HandlerNesting { count: self.count + 1 };
let (endpt0, _) = message_pipe::create(mpflags!(Create::None)).unwrap();
let _ = nested_runloop.register(&endpt0, signals!(Signals::Readable), 0, handler);
nested_runloop.run();
}
runloop.deregister(token);
}
fn on_error(&mut self, runloop: &mut RunLoop, token: Token, error: WaitError) {
assert_eq!(error, WaitError::Unsatisfiable);
assert_eq!(self.count, 11);
runloop.deregister(token);
}
}
struct HandlerBadNesting {}
impl Handler for HandlerBadNesting {
fn on_ready(&mut self, runloop: &mut RunLoop, _token: Token) {
runloop.quit();
}
fn on_timeout(&mut self, runloop: &mut RunLoop, _token: Token) {
runloop.run();
}
fn on_error(&mut self, runloop: &mut RunLoop, _token: Token, _error: WaitError) {
runloop.quit();
}
}
struct HandlerTasks {
count: Rc<Cell<u64>>,
}
impl Handler for HandlerTasks {
fn on_ready(&mut self, runloop: &mut RunLoop, token: Token) {
let r = self.count.clone();
let _ = runloop.post_task(move |_runloop| {
let val = (*r).get();
(*r).set(val+1);
}, 10);
if (*self.count).get() > 10 {
runloop.deregister(token);
}
}
fn on_timeout(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Timed-out when expected error");
}
fn on_error(&mut self, _runloop: &mut RunLoop, _token: Token, _error: WaitError) {
panic!("Error when expected ready");
}
}
struct NestedTasks {
count: Rc<Cell<u64>>,
quitter: bool,
}
impl Handler for NestedTasks {
fn on_ready(&mut self, runloop: &mut RunLoop, token: Token) {
let r = self.count.clone();
let quit = self.quitter;
let _ = runloop.post_task(move |runloop| {
let r2 = r.clone();
let tk = token.clone();
if (*r).get() < 10 {
let _ = runloop.post_task(move |_runloop| {
let val = (*r2).get();
(*r2).set(val+1);
}, 0);
} else {
if quit {
runloop.quit();
} else {
runloop.deregister(tk);
}
}
}, 0);
}
fn on_timeout(&mut self, _runloop: &mut RunLoop, _token: Token) {
panic!("Timed-out when expected error");
}
fn on_error(&mut self, _runloop: &mut RunLoop, _token: Token, _error: WaitError) {
panic!("Error when expected ready");
}
}
tests! {
// Verifies that after adding and removing, we can run, exit and be
// left in a consistent state.
fn add_remove() {
run_loop::with_current(|runloop| {
let (endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
let token0 = runloop.register(&endpt0, signals!(Signals::Writable), 0, HandlerExpectReady {});
let token1 = runloop.register(&endpt1, signals!(Signals::Writable), 0, HandlerExpectReady {});
runloop.deregister(token1);
runloop.deregister(token0);
runloop.run();
})
}
// Verifies that generated tokens are unique.
fn tokens() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
let mut vec = Vec::new();
run_loop::with_current(|runloop| {
for _ in 0..10 {
vec.push(runloop.register(&endpt1, signals!(Signals::None), 0, HandlerExpectReady {}));
}
for i in 0..10 {
for j in 0..10 {
if i != j {
assert!(vec[i] != vec[j]);
}
}
}
});
}
// Verifies that the handler's "on_ready" function is called.
fn notify_results() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Writable), MOJO_INDEFINITE, HandlerExpectReady {});
runloop.run();
});
}
// Verifies that the handler's "on_timeout" function is called.
fn notify_timeout() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Readable), 0, HandlerExpectTimeout {});
runloop.run();
});
}
// Verifies that the handler's "on_error" function is called.
fn notify_error() {
// Drop the first endpoint immediately
let (_, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Readable), 0, HandlerExpectError {});
runloop.run();
});
}
// Verifies that the handler's "on_ready" function is called which only quits.
fn notify_ready_quit() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Writable), MOJO_INDEFINITE, HandlerQuit {});
runloop.run();
});
}
// Tests more complex behavior, i.e. the interaction between two handlers.
fn register_deregister() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Writable), MOJO_INDEFINITE, HandlerRegister {});
runloop.run();
});
}
// Tests reregistering.
fn reregister() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Readable), 0, HandlerReregister { count: 0 });
runloop.run();
});
}
// Tests nesting run loops by having a handler create a new one.
fn nesting() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Readable), 0, HandlerNesting { count: 0 });
runloop.run();
});
}
// Tests to make sure nesting with the SAME runloop fails.
#[should_panic]
fn bad_nesting() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Readable), 0, HandlerBadNesting {});
runloop.run();
});
}
// Tests adding a simple task that adds a handler.
fn simple_task() {
run_loop::with_current(|runloop| {
let _ = runloop.post_task(|runloop| {
let (_, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
let _ = runloop.register(&endpt1, signals!(Signals::Readable), 0, HandlerExpectError {});
}, 0);
runloop.run();
});
}
// Tests using a handler that adds a bunch of tasks.
fn handler_tasks() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
let r = Rc::new(Cell::new(0));
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Writable), 0, HandlerTasks { count: r.clone() });
runloop.run();
assert!((*r).get() >= 11);
});
}
// Tests using a handler that adds a bunch of tasks.
fn nested_tasks() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
let r = Rc::new(Cell::new(0));
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Writable), 0, NestedTasks { count: r.clone(), quitter: false });
runloop.run();
assert!((*r).get() >= 10);
});
}
// Tests using a handler that adds a bunch of tasks.
fn nested_tasks_quit() {
let (_endpt0, endpt1) = message_pipe::create(mpflags!(Create::None)).unwrap();
let r = Rc::new(Cell::new(0));
run_loop::with_current(|runloop| {
let _ = runloop.register(&endpt1, signals!(Signals::Writable), 0, NestedTasks { count: r.clone(), quitter: true });
runloop.run();
assert!((*r).get() >= 10);
});
}
}