use std::{panic::catch_unwind, thread, time::Duration};
use futures_timer::Delay;
use threadfin::ThreadPool;
fn single_thread() -> ThreadPool {
ThreadPool::builder().size(0..1).build()
}
#[test]
#[should_panic(expected = "thread pool name must not contain null bytes")]
fn name_with_null_bytes_panics() {
ThreadPool::builder().name("uh\0oh").build();
}
#[test]
#[allow(clippy::reversed_empty_ranges)]
#[should_panic(expected = "thread pool minimum size cannot be larger than maximum size")]
fn invalid_size_panics() {
ThreadPool::builder().size(2..1);
}
#[test]
#[should_panic(expected = "thread pool maximum size must be non-zero")]
fn invalid_size_zero_panics() {
ThreadPool::builder().size(0);
}
#[test]
fn execute() {
let pool = single_thread();
let result = pool.execute(|| 2 + 2).join();
assert_eq!(result, 4);
}
#[test]
fn execute_future() {
let pool = single_thread();
let result = pool.execute_future(async { 2 + 2 }).join();
assert_eq!(result, 4);
}
#[test]
fn task_join_timeout() {
let pool = single_thread();
let result = pool
.execute(|| thread::sleep(Duration::from_secs(5)))
.join_timeout(Duration::from_millis(10));
assert!(result.is_err());
}
#[test]
fn futures_that_yield_are_run_concurrently() {
let pool = single_thread();
assert_eq!(pool.running_tasks(), 0);
let first = pool
.try_execute_future(Delay::new(Duration::from_millis(400)))
.unwrap();
thread::sleep(Duration::from_millis(100));
assert_eq!(pool.running_tasks(), 1);
let second = pool
.try_execute_future(Delay::new(Duration::from_millis(200)))
.unwrap();
thread::sleep(Duration::from_millis(100));
assert_eq!(pool.running_tasks(), 2);
assert_eq!(pool.threads(), 1);
first.join();
second.join();
assert_eq!(pool.completed_tasks(), 2);
}
#[test]
fn try_execute_under_core_count() {
let pool = ThreadPool::builder().size(1).build();
thread::sleep(Duration::from_millis(100));
assert_eq!(pool.threads(), 1);
assert!(pool.try_execute(|| 2 + 2).is_ok());
}
#[test]
fn try_execute_over_core_count() {
let pool = ThreadPool::builder().size(0..1).build();
assert!(pool.try_execute(|| 2 + 2).is_ok());
}
#[test]
fn try_execute_over_limit() {
let pool = ThreadPool::builder().size(0..1).queue_limit(0).build();
assert!(pool.try_execute(|| 2 + 2).is_ok());
assert!(pool.try_execute(|| 2 + 2).is_err());
fn task() -> usize {
2 + 2
}
let error = pool.try_execute(task).unwrap_err();
assert_eq!(error.into_inner() as usize, task as usize);
}
#[test]
fn name() {
let pool = ThreadPool::builder().name("foo").build();
let name = pool
.execute(|| thread::current().name().unwrap().to_owned())
.join();
assert_eq!(name, "foo");
}
#[test]
#[should_panic(expected = "oh no!")]
fn panic_propagates_to_task() {
let pool = single_thread();
pool.execute(|| panic!("oh no!")).join();
}
#[test]
fn panic_count() {
let pool = single_thread();
assert_eq!(pool.panicked_tasks(), 0);
let task = pool.execute(|| panic!("oh no!"));
let _ = catch_unwind(move || {
task.join();
});
assert_eq!(pool.panicked_tasks(), 1);
}
#[test]
fn thread_count() {
let pool = ThreadPool::builder().size(0..1).build();
assert_eq!(pool.threads(), 0);
pool.execute(|| 2 + 2).join();
assert_eq!(pool.threads(), 1);
let pool_with_starting_threads = ThreadPool::builder().size(1).build();
thread::sleep(Duration::from_millis(50));
assert_eq!(pool_with_starting_threads.threads(), 1);
}
#[test]
fn idle_shutdown() {
let pool = ThreadPool::builder()
.size(0..1)
.keep_alive(Duration::from_millis(100))
.build();
assert_eq!(pool.threads(), 0, "pool starts out empty");
pool.execute(|| 2 + 2).join();
assert_eq!(pool.threads(), 1, "one thread was added");
thread::sleep(Duration::from_millis(200));
assert_eq!(
pool.threads(),
0,
"thread became idle and terminated after timeout"
);
}
#[test]
fn join() {
ThreadPool::default().join();
}
#[test]
fn join_timeout_expiring() {
let pool = ThreadPool::builder().size(1).build();
assert_eq!(pool.threads(), 1);
let _task = pool.execute(|| thread::sleep(Duration::from_millis(500)));
assert!(!pool.join_timeout(Duration::from_millis(10)));
}
#[test]
fn configure_common_after_init_returns_error() {
threadfin::common();
assert!(threadfin::configure_common(|b| b).is_err());
}