use std::{
cell::Cell,
io,
rc::Rc,
sync::{
Arc,
atomic::{
AtomicUsize,
Ordering,
},
},
};
use qubit_function::{
ArcRunnable,
BoxRunnable,
Callable,
RcRunnable,
Runnable,
SupplierOnce,
};
#[derive(Clone)]
struct ClonedRunnable {
flag: Rc<Cell<bool>>,
}
impl Runnable<io::Error> for ClonedRunnable {
fn run(&mut self) -> Result<(), io::Error> {
self.flag.set(true);
Ok(())
}
}
#[test]
fn test_runnable_closure_run_returns_success() {
let flag = Rc::new(Cell::new(false));
let captured = Rc::clone(&flag);
let mut task = move || {
captured.set(true);
Ok::<(), io::Error>(())
};
task.run().expect("runnable closure should succeed");
assert!(flag.get());
}
#[test]
fn test_runnable_closure_run_returns_error() {
let mut task = || Err::<(), _>(io::Error::other("failed"));
let error = task.run().expect_err("runnable closure should fail");
assert_eq!(error.kind(), io::ErrorKind::Other);
assert_eq!(error.to_string(), "failed");
}
#[test]
fn test_runnable_closure_into_box_executes_once() {
let flag = Rc::new(Cell::new(false));
let captured = Rc::clone(&flag);
let task = move || {
captured.set(true);
Ok::<(), io::Error>(())
};
let mut boxed = Runnable::into_box(task);
boxed.run().expect("boxed runnable should succeed");
assert!(flag.get());
}
#[test]
fn test_runnable_closure_into_fn_returns_fn_once() {
let task = || Ok::<(), io::Error>(());
let mut function = Runnable::into_fn(task);
function().expect("runnable function should succeed");
}
#[test]
fn test_runnable_to_box_clones_runnable() {
let flag = Rc::new(Cell::new(false));
let mut task = ClonedRunnable {
flag: Rc::clone(&flag),
};
let mut boxed = task.to_box();
boxed.run().expect("boxed clone should succeed");
assert!(flag.get());
flag.set(false);
task.run().expect("original runnable should remain usable");
assert!(flag.get());
}
#[test]
fn test_runnable_to_fn_clones_runnable() {
let flag = Rc::new(Cell::new(false));
let mut task = ClonedRunnable {
flag: Rc::clone(&flag),
};
let mut function = task.to_fn();
function().expect("cloned runnable should succeed");
assert!(flag.get());
flag.set(false);
drop(function);
task.run().expect("original runnable should remain usable");
assert!(flag.get());
}
#[derive(Clone)]
struct SharedCounterRunnable {
count: Rc<Cell<u32>>,
}
impl Runnable<io::Error> for SharedCounterRunnable {
fn run(&mut self) -> Result<(), io::Error> {
self.count.set(self.count.get() + 1);
Ok(())
}
}
#[derive(Clone)]
struct SharedAtomicRunnable {
count: Arc<AtomicUsize>,
}
impl Runnable<io::Error> for SharedAtomicRunnable {
fn run(&mut self) -> Result<(), io::Error> {
self.count.fetch_add(1, Ordering::SeqCst);
Ok(())
}
}
#[test]
fn test_runnable_default_into_callable_returns_unit() {
let flag = Rc::new(Cell::new(false));
let task = ClonedRunnable {
flag: Rc::clone(&flag),
};
let mut callable = Runnable::into_callable(task);
callable.call().expect("default callable should succeed");
assert!(flag.get());
}
#[test]
fn test_runnable_into_rc_runnable_preserves_name_and_state() {
let count = Rc::new(Cell::new(0));
let captured = Rc::clone(&count);
let task = BoxRunnable::new_with_name("shared", move || {
captured.set(captured.get() + 1);
Ok::<(), io::Error>(())
});
let mut shared = Runnable::into_rc(task);
let mut clone = shared.clone();
assert_eq!(shared.name(), Some("shared"));
shared.run().expect("shared runnable should succeed");
clone.run().expect("shared clone should succeed");
assert_eq!(count.get(), 2);
}
#[test]
fn test_runnable_to_rc_clones_source_after_boxed_runnable() {
let count = Rc::new(Cell::new(0));
let mut source = SharedCounterRunnable {
count: Rc::clone(&count),
};
let mut shared = source.to_rc();
let mut shared_clone = shared.clone();
shared
.run()
.expect("to_rc should build shared runnable from cloneable source");
shared_clone
.run()
.expect("shared clone should also execute");
source
.run()
.expect("original cloneable runnable should remain usable");
assert_eq!(count.get(), 3);
}
#[test]
fn test_runnable_into_arc_runnable_preserves_name_and_state() {
let count = Arc::new(AtomicUsize::new(0));
let captured = Arc::clone(&count);
let task = move || {
captured.fetch_add(1, Ordering::SeqCst);
Ok::<(), io::Error>(())
};
let mut shared = Runnable::into_arc(task);
assert_eq!(count.load(Ordering::SeqCst), 0);
shared.run().expect("shared runnable should succeed");
shared
.run()
.expect("shared runnable should execute repeatedly");
assert_eq!(count.load(Ordering::SeqCst), 2);
}
#[test]
fn test_runnable_to_arc_clones_source_after_boxed_runnable() {
let count = Arc::new(AtomicUsize::new(0));
let mut source = SharedAtomicRunnable {
count: Arc::clone(&count),
};
let mut shared = source.to_arc();
let mut shared_clone = shared.clone();
shared
.run()
.expect("to_arc should build shared runnable from cloneable source");
shared_clone
.run()
.expect("shared arc clone should also execute");
source
.run()
.expect("original cloneable runnable should remain usable");
assert_eq!(count.load(Ordering::SeqCst), 3);
}
#[test]
fn test_rc_runnable_from_supplier() {
let count = Rc::new(Cell::new(0));
let captured = Rc::clone(&count);
let mut task = RcRunnable::from_supplier(move || {
captured.set(captured.get() + 1);
Ok::<(), io::Error>(())
});
task.run().expect("supplier runnable should succeed");
task.run().expect("supplier runnable should succeed again");
assert_eq!(count.get(), 2);
}
#[test]
fn test_arc_runnable_from_supplier() {
let count = Arc::new(AtomicUsize::new(0));
let captured = Arc::clone(&count);
let mut task = ArcRunnable::from_supplier(move || {
captured.fetch_add(1, Ordering::SeqCst);
Ok::<(), io::Error>(())
});
task.run().expect("supplier runnable should succeed");
task.run().expect("supplier runnable should succeed again");
assert_eq!(count.load(Ordering::SeqCst), 2);
}
#[test]
fn test_box_runnable_new_and_run() {
let flag = Rc::new(Cell::new(false));
let captured = Rc::clone(&flag);
let mut task = BoxRunnable::new(move || {
captured.set(true);
Ok::<(), io::Error>(())
});
task.run().expect("box runnable should succeed");
assert!(flag.get());
}
#[test]
fn test_box_runnable_name_management() {
let mut task = BoxRunnable::<io::Error>::new_with_name("cleanup", || Ok(()));
assert_eq!(task.name(), Some("cleanup"));
assert_eq!(task.to_string(), "BoxRunnable(cleanup)");
assert!(format!("{task:?}").contains("cleanup"));
task.set_name("renamed");
assert_eq!(task.name(), Some("renamed"));
task.clear_name();
assert_eq!(task.name(), None);
assert_eq!(task.to_string(), "BoxRunnable");
}
#[test]
fn test_box_runnable_into_box_returns_self() {
let task = BoxRunnable::new(|| Ok::<(), io::Error>(()));
let mut boxed = Runnable::into_box(task);
boxed.run().expect("boxed runnable should succeed");
}
#[test]
fn test_box_runnable_into_fn_extracts_function() {
let task = BoxRunnable::new(|| Ok::<(), io::Error>(()));
let mut function = Runnable::into_fn(task);
function().expect("runnable function should succeed");
}
#[test]
fn test_box_runnable_from_supplier() {
let supplier = || Ok::<(), io::Error>(());
let mut task = BoxRunnable::from_supplier(supplier);
task.run().expect("supplier-backed runnable should succeed");
}
#[test]
fn test_box_runnable_implements_supplier_once() {
let task = BoxRunnable::new(|| Ok::<(), io::Error>(()));
let result = SupplierOnce::get(task);
result.expect("supplier runnable should succeed");
}
#[test]
fn test_box_runnable_and_then_runs_next_on_success() {
let events = Rc::new(Cell::new(0));
let first_events = Rc::clone(&events);
let second_events = Rc::clone(&events);
let first = BoxRunnable::new(move || {
first_events.set(1);
Ok::<(), io::Error>(())
});
let second = move || {
second_events.set(2);
Ok::<(), io::Error>(())
};
let mut chained = first.and_then(second);
chained.run().expect("chained runnable should succeed");
assert_eq!(events.get(), 2);
}
#[test]
fn test_box_runnable_and_then_skips_next_on_error() {
let events = Rc::new(Cell::new(0));
let second_events = Rc::clone(&events);
let first = BoxRunnable::new(|| Err::<(), _>(io::Error::other("stop")));
let second = move || {
second_events.set(2);
Ok::<(), io::Error>(())
};
let mut chained = first.and_then(second);
assert_eq!(
chained
.run()
.expect_err("chained runnable should preserve error")
.to_string(),
"stop",
);
assert_eq!(events.get(), 0);
}
#[test]
fn test_box_runnable_then_callable_runs_callable_on_success() {
let task = BoxRunnable::new_with_name("prepare", || Ok::<(), io::Error>(()));
let callable = || Ok::<i32, io::Error>(42);
let mut chained = task.then_callable(callable);
assert_eq!(chained.name(), Some("prepare"));
assert_eq!(chained.call().expect("callable should succeed"), 42);
}
#[test]
fn test_runnable_into_callable_returns_unit_callable() {
let task = BoxRunnable::new_with_name("cleanup", || Ok::<(), io::Error>(()));
let mut callable = task.into_callable();
assert_eq!(callable.name(), Some("cleanup"));
callable.call().expect("unit callable should succeed");
}