use background_picker::{Args, BackgroundPickerApp};
use clap::Parser;
use std::fs;
use tempfile::TempDir;
use serial_test::serial;
#[cfg(test)]
mod tests {
use super::*;
fn create_comprehensive_test_structure(base_dir: &std::path::Path) -> std::io::Result<()> {
fs::create_dir_all(base_dir.join("Photos/Vacation"))?;
fs::create_dir_all(base_dir.join("Photos/Family"))?;
fs::create_dir_all(base_dir.join("Wallpapers/Nature"))?;
fs::create_dir_all(base_dir.join("Wallpapers/Abstract"))?;
fs::create_dir_all(base_dir.join("Screenshots"))?;
fs::write(base_dir.join("desktop_bg.jpg"), create_fake_jpeg_data())?;
fs::write(base_dir.join("logo.png"), create_fake_png_data())?;
fs::write(base_dir.join("Photos/Vacation/beach1.jpg"), create_fake_jpeg_data())?;
fs::write(base_dir.join("Photos/Vacation/beach2.jpeg"), create_fake_jpeg_data())?;
fs::write(base_dir.join("Photos/Vacation/sunset.png"), create_fake_png_data())?;
fs::write(base_dir.join("Photos/Family/portrait.jpg"), create_fake_jpeg_data())?;
fs::write(base_dir.join("Photos/Family/group.gif"), b"GIF89a fake gif data")?;
fs::write(base_dir.join("Wallpapers/Nature/forest.jpg"), create_fake_jpeg_data())?;
fs::write(base_dir.join("Wallpapers/Nature/mountains.png"), create_fake_png_data())?;
fs::write(base_dir.join("Wallpapers/Abstract/geometric.webp"), b"RIFF fake webp")?;
fs::write(base_dir.join("Wallpapers/Abstract/colors.bmp"), create_fake_bmp_data())?;
fs::write(base_dir.join("Screenshots/screen1.png"), create_fake_png_data())?;
fs::write(base_dir.join("Screenshots/screen2.jpg"), create_fake_jpeg_data())?;
fs::write(base_dir.join("readme.txt"), b"This is not an image")?;
fs::write(base_dir.join("Photos/video.mp4"), b"fake video data")?;
fs::write(base_dir.join("config.yaml"), b"configuration: data")?;
Ok(())
}
fn create_fake_jpeg_data() -> Vec<u8> {
vec![0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46]
}
fn create_fake_png_data() -> Vec<u8> {
vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
}
fn create_fake_bmp_data() -> Vec<u8> {
vec![0x42, 0x4D] }
#[test]
#[serial]
fn test_end_to_end_image_scanning_and_organization() {
let temp_dir = TempDir::new().unwrap();
create_comprehensive_test_structure(temp_dir.path()).unwrap();
let mut args = Args::try_parse_from(["background-picker"]).unwrap();
args.directory = temp_dir.path().to_path_buf();
args.debug = true;
args.thumbnail_size = 128;
let (sender, _receiver) = std::sync::mpsc::channel();
let thread_pool = rayon::ThreadPoolBuilder::new()
.num_threads(2)
.build()
.unwrap();
let mut app = BackgroundPickerApp {
args: args.clone(),
images: std::sync::Arc::new(std::sync::RwLock::new(Vec::new())),
folder_tree: std::collections::HashMap::new(),
loading: true,
thumbnail_sender: sender,
thumbnail_receiver: _receiver,
thread_pool,
cache_dir: temp_dir.path().join("cache"),
};
let _ = app.scan_images();
let images = app.images.read().unwrap();
assert_eq!(images.len(), 13);
assert_eq!(app.folder_tree.len(), 6);
if let Some(root_images) = app.folder_tree.get(".") {
assert_eq!(root_images.len(), 2); }
let vacation_images = app.folder_tree.get("Photos/Vacation").unwrap();
assert_eq!(vacation_images.len(), 3);
let family_images = app.folder_tree.get("Photos/Family").unwrap();
assert_eq!(family_images.len(), 2);
let nature_images = app.folder_tree.get("Wallpapers/Nature").unwrap();
assert_eq!(nature_images.len(), 2);
let abstract_images = app.folder_tree.get("Wallpapers/Abstract").unwrap();
assert_eq!(abstract_images.len(), 2);
let screenshot_images = app.folder_tree.get("Screenshots").unwrap();
assert_eq!(screenshot_images.len(), 2);
for (i, image) in images.iter().enumerate() {
assert!(!image.relative_path.is_empty());
assert!(image.path.exists());
assert!(!image.loading); assert!(image.thumbnail.is_none());
println!("Image {}: {} -> {}", i, image.relative_path, image.path.display());
}
assert!(!app.loading);
}
#[test]
#[serial]
fn test_selected_image_persistence_workflow() {
let temp_dir = TempDir::new().unwrap();
let selected_file = temp_dir.path().join("selected_image.txt");
let mut args = Args::try_parse_from(["background-picker"]).unwrap();
args.selected_image_file = selected_file.clone();
let (sender, receiver) = std::sync::mpsc::channel();
let thread_pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.unwrap();
let app = BackgroundPickerApp {
args,
images: std::sync::Arc::new(std::sync::RwLock::new(Vec::new())),
folder_tree: std::collections::HashMap::new(),
loading: false,
thumbnail_sender: sender,
thumbnail_receiver: receiver,
thread_pool,
cache_dir: temp_dir.path().join("cache"),
};
let first_image = std::path::PathBuf::from("/path/to/vacation/beach.jpg");
app.save_selected_image(&first_image).unwrap();
assert!(selected_file.exists());
let content = fs::read_to_string(&selected_file).unwrap();
assert_eq!(content.trim(), first_image.to_string_lossy());
let second_image = std::path::PathBuf::from("/path/to/nature/forest.jpg");
app.save_selected_image(&second_image).unwrap();
let updated_content = fs::read_to_string(&selected_file).unwrap();
assert_eq!(updated_content.trim(), second_image.to_string_lossy());
}
#[test]
#[serial]
fn test_thumbnail_caching_workflow() {
let temp_dir = TempDir::new().unwrap();
create_comprehensive_test_structure(temp_dir.path()).unwrap();
let cache_dir = temp_dir.path().join("thumbnail_cache");
let test_image = temp_dir.path().join("desktop_bg.jpg");
let thumbnail_size = 150;
let result1 = BackgroundPickerApp::load_or_generate_thumbnail(
&test_image,
thumbnail_size,
&cache_dir,
false
);
if result1.is_none() {
println!("Thumbnail generation failed for fake image data - this is expected");
return; }
assert!(result1.is_some());
let thumbnail1 = result1.unwrap();
assert_eq!(thumbnail1.size[0], thumbnail_size as usize);
assert_eq!(thumbnail1.size[1], thumbnail_size as usize);
let cache_path = BackgroundPickerApp::get_cached_thumbnail_path_static(&test_image, &cache_dir);
assert!(cache_path.is_some());
let cache_file = cache_path.unwrap();
assert!(cache_file.exists(), "Cache file should exist at {:?}", cache_file);
let result2 = BackgroundPickerApp::load_or_generate_thumbnail(
&test_image,
thumbnail_size,
&cache_dir,
false
);
assert!(result2.is_some());
let thumbnail2 = result2.unwrap();
assert_eq!(thumbnail2.size[0], thumbnail_size as usize);
assert_eq!(thumbnail2.size[1], thumbnail_size as usize);
assert!(BackgroundPickerApp::is_thumbnail_cache_valid_static(&test_image, &cache_file));
let different_size = 64;
let result3 = BackgroundPickerApp::load_or_generate_thumbnail(
&test_image,
different_size,
&cache_dir,
false
);
assert!(result3.is_some());
let thumbnail3 = result3.unwrap();
assert_eq!(thumbnail3.size[0], different_size as usize);
assert_eq!(thumbnail3.size[1], different_size as usize);
}
#[test]
#[serial]
fn test_command_execution_workflow() {
let temp_dir = TempDir::new().unwrap();
let test_image = temp_dir.path().join("test.jpg");
fs::write(&test_image, create_fake_jpeg_data()).unwrap();
let mut args = Args::try_parse_from(["background-picker"]).unwrap();
args.command = "echo test".to_string();
let (sender, _receiver) = std::sync::mpsc::channel();
let thread_pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.unwrap();
let app = BackgroundPickerApp {
args,
images: std::sync::Arc::new(std::sync::RwLock::new(Vec::new())),
folder_tree: std::collections::HashMap::new(),
loading: false,
thumbnail_sender: sender,
thumbnail_receiver: _receiver,
thread_pool,
cache_dir: temp_dir.path().join("cache"),
};
let result = app.set_background(&test_image);
assert!(result.is_ok(), "Command execution should succeed");
let mut args2 = Args::try_parse_from(["background-picker"]).unwrap();
args2.command = "echo --bg-max".to_string();
let app2 = BackgroundPickerApp {
args: args2,
images: std::sync::Arc::new(std::sync::RwLock::new(Vec::new())),
folder_tree: std::collections::HashMap::new(),
loading: false,
thumbnail_sender: app.thumbnail_sender.clone(),
thumbnail_receiver: app.thumbnail_receiver,
thread_pool: rayon::ThreadPoolBuilder::new().num_threads(1).build().unwrap(),
cache_dir: temp_dir.path().join("cache"),
};
let result2 = app2.set_background(&test_image);
assert!(result2.is_ok(), "Feh-like command should succeed");
}
#[test]
#[serial]
fn test_pregeneration_workflow() {
let temp_dir = TempDir::new().unwrap();
create_comprehensive_test_structure(temp_dir.path()).unwrap();
let mut args = Args::try_parse_from(["background-picker"]).unwrap();
args.directory = temp_dir.path().to_path_buf();
args.thumbnail_size = 100;
args.debug = false;
let (sender, _receiver) = std::sync::mpsc::channel();
let thread_pool = rayon::ThreadPoolBuilder::new()
.num_threads(2)
.build()
.unwrap();
let mut app = BackgroundPickerApp {
args: args.clone(),
images: std::sync::Arc::new(std::sync::RwLock::new(Vec::new())),
folder_tree: std::collections::HashMap::new(),
loading: true,
thumbnail_sender: sender,
thumbnail_receiver: _receiver,
thread_pool,
cache_dir: temp_dir.path().join("thumbnails"),
};
let _ = app.scan_images();
let image_count = app.images.read().unwrap().len();
assert_eq!(image_count, 13);
let start_time = std::time::Instant::now();
let _ = app.pregenerate_all_thumbnails();
let elapsed = start_time.elapsed();
assert!(elapsed.as_secs() < 30, "Pregeneration took too long: {:?}", elapsed);
let cache_dir = &app.cache_dir;
let mut cache_files_found = 0;
if cache_dir.exists() {
for entry in fs::read_dir(cache_dir).unwrap() {
let entry = entry.unwrap();
if entry.path().extension().is_some_and(|ext| ext == "png") {
cache_files_found += 1;
}
}
}
println!("Cache files found: {}", cache_files_found);
assert!(cache_files_found <= image_count, "Should not create more cache files than images");
}
#[test]
#[serial]
fn test_mixed_file_types_handling() {
let temp_dir = TempDir::new().unwrap();
fs::write(temp_dir.path().join("valid.jpg"), create_fake_jpeg_data()).unwrap();
fs::write(temp_dir.path().join("valid.png"), create_fake_png_data()).unwrap();
fs::write(temp_dir.path().join("invalid.jpg"), b"not actually jpeg data").unwrap();
fs::write(temp_dir.path().join("document.txt"), b"text file").unwrap();
fs::write(temp_dir.path().join("no_extension"), b"file without extension").unwrap();
fs::write(temp_dir.path().join("empty.jpg"), b"").unwrap();
let mut args = Args::try_parse_from(["background-picker"]).unwrap();
args.directory = temp_dir.path().to_path_buf();
args.debug = false;
let (sender, _receiver) = std::sync::mpsc::channel();
let thread_pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.unwrap();
let mut app = BackgroundPickerApp {
args: args.clone(),
images: std::sync::Arc::new(std::sync::RwLock::new(Vec::new())),
folder_tree: std::collections::HashMap::new(),
loading: true,
thumbnail_sender: sender,
thumbnail_receiver: _receiver,
thread_pool,
cache_dir: temp_dir.path().join("cache"),
};
let _ = app.scan_images();
let images = app.images.read().unwrap();
assert_eq!(images.len(), 4);
for image in images.iter() {
let result = BackgroundPickerApp::fast_thumbnail_generation(&image.path, 100);
println!("Thumbnail generation for {:?}: {}",
image.path.file_name().unwrap(),
if result.is_some() { "SUCCESS" } else { "FAILED" }
);
}
assert!(!app.loading);
}
}