use revue::worker::{WorkerError, WorkerHandle, WorkerState};
use std::thread;
use std::time::{Duration, Instant};
fn poll_until<F>(mut condition: F, timeout_ms: u64) -> bool
where
F: FnMut() -> bool,
{
let start = Instant::now();
let timeout = Duration::from_millis(timeout_ms);
let poll_interval = Duration::from_millis(2);
while start.elapsed() < timeout {
if condition() {
return true;
}
thread::sleep(poll_interval);
}
false
}
#[test]
fn test_spawn_blocking_return_types() {
let handle1 = WorkerHandle::spawn_blocking(|| 42i32);
assert_eq!(handle1.join().unwrap(), 42);
let handle2 = WorkerHandle::spawn_blocking(|| "Hello".to_string());
assert_eq!(handle2.join().unwrap(), "Hello");
let handle3 = WorkerHandle::spawn_blocking(|| vec![1, 2, 3]);
assert_eq!(handle3.join().unwrap(), vec![1, 2, 3]);
let handle4 = WorkerHandle::spawn_blocking(|| ());
assert_eq!(handle4.join().unwrap(), ());
let handle5 = WorkerHandle::spawn_blocking(|| (1, "two", 3.0));
assert_eq!(handle5.join().unwrap(), (1, "two", 3.0));
}
#[test]
fn test_spawn_blocking_panic_recovery() {
let handle = WorkerHandle::spawn_blocking(|| {
panic!("intentional panic");
});
let result = handle.join();
assert!(matches!(result, Err(WorkerError::Panicked(_))));
}
#[test]
fn test_spawn_blocking_large_result() {
let large_data: Vec<u8> = (0..10000).map(|i| i as u8).collect();
let handle = WorkerHandle::spawn_blocking(move || large_data.len());
assert_eq!(handle.join().unwrap(), 10000);
}
#[test]
fn test_spawn_blocking_immediate() {
let handle = WorkerHandle::spawn_blocking(|| 42);
poll_until(|| handle.is_finished(), 500);
assert!(handle.is_finished());
assert_eq!(handle.join().unwrap(), 42);
}
#[test]
fn test_state_transitions() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(50));
42
});
let state = handle.state();
assert!(matches!(
state,
WorkerState::Pending | WorkerState::Running | WorkerState::Completed
));
poll_until(|| matches!(handle.state(), WorkerState::Completed), 500);
assert!(matches!(handle.state(), WorkerState::Completed));
}
#[test]
fn test_is_finished_success() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(10));
"done"
});
assert!(!handle.is_finished());
poll_until(|| handle.is_finished(), 500);
assert!(handle.is_finished());
assert!(handle.is_success());
}
#[test]
fn test_is_finished_failure() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(10));
panic!("failed");
});
assert!(!handle.is_finished());
poll_until(|| handle.is_finished(), 500);
assert!(handle.is_finished());
assert!(!handle.is_success());
}
#[test]
fn test_is_running() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(100));
42
});
let _initial_state = handle.state();
poll_until(|| handle.is_running(), 200);
assert!(handle.is_running());
poll_until(|| !handle.is_running(), 500);
assert!(!handle.is_running());
assert!(handle.is_finished());
}
#[test]
fn test_cancel_running_task() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(100));
"never"
});
handle.cancel();
let result = handle.join();
let _ = result;
}
#[test]
fn test_cancel_before_start() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(100));
42
});
handle.cancel();
let state = handle.state();
let _ = state;
}
#[test]
fn test_try_join_not_ready() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(100));
42
});
let mut handle = handle;
let result = handle.try_join();
assert!(result.is_none());
}
#[test]
fn test_try_join_ready() {
let mut handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(50)); 42
});
poll_until(|| handle.is_finished(), 500);
let result = handle.try_join();
assert!(result.is_some(), "Task should be ready by now");
assert!(result.as_ref().unwrap().is_ok());
assert_eq!(result.unwrap().unwrap(), 42);
}
#[test]
fn test_join_timeout_success() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(10));
42
});
let result = handle.join_timeout(Duration::from_millis(100));
assert_eq!(result.unwrap(), 42);
}
#[test]
fn test_join_timeout_failure() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_secs(10));
42
});
let result = handle.join_timeout(Duration::from_millis(50));
assert!(matches!(result, Err(WorkerError::Timeout)));
}
#[test]
fn test_join_consume() {
let handle = WorkerHandle::spawn_blocking(|| 42);
let result = handle.join();
assert_eq!(result.unwrap(), 42);
}
#[test]
fn test_join_panic_result() {
let handle = WorkerHandle::spawn_blocking(|| {
panic!("test panic");
#[allow(unreachable_code)]
42
});
let result = handle.join();
assert!(matches!(result, Err(WorkerError::Panicked(_))));
if let Err(WorkerError::Panicked(msg)) = result {
assert!(msg.contains("test panic"));
}
}
#[cfg(feature = "async")]
#[test]
fn test_spawn_async_basic() {
let handle = WorkerHandle::spawn(async {
thread::sleep(Duration::from_millis(10));
42
});
let result = handle.join();
assert_eq!(result.unwrap(), 42);
}
#[cfg(feature = "async")]
#[test]
fn test_spawn_async_multiple_await() {
let handle = WorkerHandle::spawn(async {
let a = async { 1 }.await;
let b = async { 2 }.await;
a + b
});
let result = handle.join();
assert_eq!(result.unwrap(), 3);
}
#[test]
fn test_poll_returns_state() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(10));
42
});
let state = handle.poll();
assert!(state.is_some());
assert!(matches!(
state.unwrap(),
WorkerState::Pending | WorkerState::Running
));
let completed = poll_until(
|| matches!(handle.poll(), Some(WorkerState::Completed)),
500,
);
assert!(completed, "Worker did not complete in time");
}
#[test]
fn test_worker_error_display() {
let err = WorkerError::Cancelled;
assert_eq!(format!("{}", err), "Worker task was cancelled");
let err = WorkerError::Panicked("test".to_string());
assert!(format!("{}", err).contains("test"));
let err = WorkerError::ChannelClosed;
assert_eq!(format!("{}", err), "Worker channel closed");
let err = WorkerError::Timeout;
assert_eq!(format!("{}", err), "Worker task timed out");
let err = WorkerError::Custom("custom error".to_string());
assert!(format!("{}", err).contains("custom error"));
}
#[test]
fn test_worker_error_clone() {
let err1 = WorkerError::Custom("test".to_string());
let err2 = err1.clone();
assert_eq!(format!("{}", err1), format!("{}", err2));
}
#[test]
fn test_multiple_handles_concurrent() {
let handles: Vec<_> = (0..10)
.map(|i| {
WorkerHandle::spawn_blocking(move || {
thread::sleep(Duration::from_millis(10));
i * 2
})
})
.collect();
let mut results = Vec::new();
for handle in handles {
results.push(handle.join().unwrap());
}
assert_eq!(results.len(), 10);
assert_eq!(results[0], 0);
assert_eq!(results[9], 18);
}
#[test]
fn test_handle_drop_cancels() {
let handle = WorkerHandle::spawn_blocking(|| {
thread::sleep(Duration::from_millis(10));
42
});
drop(handle);
}
#[test]
fn test_state_after_completion() {
let handle = WorkerHandle::spawn_blocking(|| 42);
thread::sleep(Duration::from_millis(50));
assert_eq!(handle.state(), WorkerState::Completed);
assert!(handle.is_success());
}
#[test]
fn test_state_after_panic() {
let handle = WorkerHandle::spawn_blocking(|| {
panic!("test");
});
thread::sleep(Duration::from_millis(50));
assert_eq!(handle.state(), WorkerState::Failed);
assert!(!handle.is_success());
}
#[test]
fn test_run_blocking_convenience() {
let handle = revue::worker::run_blocking(|| {
thread::sleep(Duration::from_millis(10));
"result"
});
assert_eq!(handle.join().unwrap(), "result");
}
#[cfg(feature = "async")]
#[test]
fn test_spawn_convenience() {
let handle = revue::worker::spawn(async {
thread::sleep(Duration::from_millis(10));
"async result"
});
assert_eq!(handle.join().unwrap(), "async result");
}
#[test]
fn test_try_join_consumes() {
let handle = WorkerHandle::spawn_blocking(|| 42);
thread::sleep(Duration::from_millis(50));
let mut handle_ref = handle;
let result1 = handle_ref.try_join();
assert!(result1.is_some());
let result2 = handle_ref.try_join();
assert!(result2.is_none());
}