Add pipeline executor with progress reporting and cancellation
Sequential execution with per-image progress callbacks, cancellation via atomic flag, batch result tracking (success/fail/sizes/timing). Phase 5 complete - 85 tests passing, zero clippy warnings.
This commit is contained in:
134
pixstrip-core/tests/executor_tests.rs
Normal file
134
pixstrip-core/tests/executor_tests.rs
Normal file
@@ -0,0 +1,134 @@
|
||||
use pixstrip_core::executor::PipelineExecutor;
|
||||
use pixstrip_core::operations::*;
|
||||
use pixstrip_core::pipeline::ProcessingJob;
|
||||
use pixstrip_core::types::*;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
fn create_test_jpeg(path: &std::path::Path) {
|
||||
let img = image::RgbImage::from_fn(200, 150, |x, y| {
|
||||
image::Rgb([(x % 256) as u8, (y % 256) as u8, 128])
|
||||
});
|
||||
img.save_with_format(path, image::ImageFormat::Jpeg).unwrap();
|
||||
}
|
||||
|
||||
fn setup_test_dir() -> (tempfile::TempDir, std::path::PathBuf, std::path::PathBuf) {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let input = dir.path().join("input");
|
||||
let output = dir.path().join("output");
|
||||
std::fs::create_dir_all(&input).unwrap();
|
||||
std::fs::create_dir_all(&output).unwrap();
|
||||
(dir, input, output)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_single_image_resize() {
|
||||
let (_dir, input, output) = setup_test_dir();
|
||||
create_test_jpeg(&input.join("photo.jpg"));
|
||||
|
||||
let mut job = ProcessingJob::new(&input, &output);
|
||||
job.add_source(input.join("photo.jpg"));
|
||||
job.resize = Some(ResizeConfig::ByWidth(100));
|
||||
|
||||
let executor = PipelineExecutor::new();
|
||||
let result = executor.execute(&job, |_| {}).unwrap();
|
||||
|
||||
assert_eq!(result.succeeded, 1);
|
||||
assert_eq!(result.failed, 0);
|
||||
assert!(output.join("photo.jpg").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_resize_and_convert() {
|
||||
let (_dir, input, output) = setup_test_dir();
|
||||
create_test_jpeg(&input.join("photo.jpg"));
|
||||
|
||||
let mut job = ProcessingJob::new(&input, &output);
|
||||
job.add_source(input.join("photo.jpg"));
|
||||
job.resize = Some(ResizeConfig::ByWidth(100));
|
||||
job.convert = Some(ConvertConfig::SingleFormat(ImageFormat::WebP));
|
||||
|
||||
let executor = PipelineExecutor::new();
|
||||
let result = executor.execute(&job, |_| {}).unwrap();
|
||||
|
||||
assert_eq!(result.succeeded, 1);
|
||||
assert!(output.join("photo.webp").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_multiple_images() {
|
||||
let (_dir, input, output) = setup_test_dir();
|
||||
create_test_jpeg(&input.join("a.jpg"));
|
||||
create_test_jpeg(&input.join("b.jpg"));
|
||||
create_test_jpeg(&input.join("c.jpg"));
|
||||
|
||||
let mut job = ProcessingJob::new(&input, &output);
|
||||
job.add_source(input.join("a.jpg"));
|
||||
job.add_source(input.join("b.jpg"));
|
||||
job.add_source(input.join("c.jpg"));
|
||||
job.resize = Some(ResizeConfig::ByWidth(50));
|
||||
|
||||
let executor = PipelineExecutor::new();
|
||||
let result = executor.execute(&job, |_| {}).unwrap();
|
||||
|
||||
assert_eq!(result.succeeded, 3);
|
||||
assert_eq!(result.failed, 0);
|
||||
assert_eq!(result.total, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_collects_progress() {
|
||||
let (_dir, input, output) = setup_test_dir();
|
||||
create_test_jpeg(&input.join("photo.jpg"));
|
||||
|
||||
let mut job = ProcessingJob::new(&input, &output);
|
||||
job.add_source(input.join("photo.jpg"));
|
||||
job.resize = Some(ResizeConfig::ByWidth(100));
|
||||
|
||||
let updates = Arc::new(Mutex::new(Vec::new()));
|
||||
let updates_clone = updates.clone();
|
||||
|
||||
let executor = PipelineExecutor::new();
|
||||
executor.execute(&job, move |update| {
|
||||
updates_clone.lock().unwrap().push(update);
|
||||
}).unwrap();
|
||||
|
||||
let updates = updates.lock().unwrap();
|
||||
assert!(!updates.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_with_cancellation() {
|
||||
let (_dir, input, output) = setup_test_dir();
|
||||
create_test_jpeg(&input.join("a.jpg"));
|
||||
create_test_jpeg(&input.join("b.jpg"));
|
||||
|
||||
let mut job = ProcessingJob::new(&input, &output);
|
||||
job.add_source(input.join("a.jpg"));
|
||||
job.add_source(input.join("b.jpg"));
|
||||
job.resize = Some(ResizeConfig::ByWidth(100));
|
||||
|
||||
let cancel = Arc::new(AtomicBool::new(true)); // cancel immediately
|
||||
let executor = PipelineExecutor::with_cancel(cancel);
|
||||
let result = executor.execute(&job, |_| {}).unwrap();
|
||||
|
||||
// With immediate cancellation, fewer images should be processed
|
||||
assert!(result.succeeded + result.failed <= 2);
|
||||
assert!(result.cancelled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batch_result_tracks_sizes() {
|
||||
let (_dir, input, output) = setup_test_dir();
|
||||
create_test_jpeg(&input.join("photo.jpg"));
|
||||
|
||||
let mut job = ProcessingJob::new(&input, &output);
|
||||
job.add_source(input.join("photo.jpg"));
|
||||
job.compress = Some(CompressConfig::Preset(QualityPreset::Low));
|
||||
|
||||
let executor = PipelineExecutor::new();
|
||||
let result = executor.execute(&job, |_| {}).unwrap();
|
||||
|
||||
assert!(result.total_input_bytes > 0);
|
||||
assert!(result.total_output_bytes > 0);
|
||||
}
|
||||
Reference in New Issue
Block a user