diff --git a/ROADMAP.md b/ROADMAP.md index 97cf19a..d3029c5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,6 +4,28 @@ Create a simple disk exploration and analysis tool. --- +## v0.0.3 - Polish, Stability & User Experience +### Features +- [ ] Add a settings page for user preferences +- [ ] Add a light mode theme +- [ ] Add a file type filter for the Tree Map +- [ ] Add duplication detection and visualization +### Core +- [ ] Refactor the UI to use a more modular component structure +- [ ] Improve error handling and user feedback for failed scans +- [ ] Add support for scanning network drives and external storage +- [ ] Dictated module test file structure and naming conventions. +### Performance +- [ ] Optimize Tree Map rendering for large datasets +- [ ] Implement lazy loading for directory contents in the Tree Map +- [ ] Add caching of scan results to speed up subsequent scans +### Quality +- [ ] Add end-to-end tests for the entire scanning and visualization workflow +- [ ] Add performance benchmarks for scanning and rendering +- [ ] Get user feedback and iterate on the UI/UX design +### CI / Project setup +- [ ] Investigate for failure CI runs and fix them. +- [ ] Add script helper for checking crate versions. ## v0.0.2 — Usability, Analysis & Infrastructure diff --git a/src/app/update.rs b/src/app/update.rs index 5d38ded..7f3357c 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -286,12 +286,22 @@ impl FrostScanApp { #[cfg(test)] mod tests { use std::path::PathBuf; + use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::app::notice::{AppNotice, NoticeLevel, NoticeSource}; use crate::app::state::{CachedDirectory, FrostScanApp}; use crate::i18n::TextKey; use crate::model::scan_node::ScanNode; + fn unique_missing_path(label: &str) -> PathBuf { + let suffix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_else(|_| Duration::from_secs(0)) + .as_nanos(); + + std::env::temp_dir().join(format!("frostscan-missing-{label}-{suffix}")) + } + #[test] fn clears_sticky_navigation_notice_after_successful_navigation() { let (mut app, _) = FrostScanApp::new(); @@ -346,9 +356,10 @@ mod tests { fn failed_open_clears_previous_scan_tree() { let (mut app, _) = FrostScanApp::new(); app.scan_tree = Some(ScanNode::new_directory(PathBuf::from("old"))); + let missing_path = unique_missing_path("open-directory"); app.open_directory( - PathBuf::from("Z:\\definitely-missing-frostscan-path"), + missing_path, crate::app::state::Tab::Explorer, true, ); diff --git a/tests/scanner_integration.rs b/tests/scanner_integration.rs index 4636b08..dad7963 100644 --- a/tests/scanner_integration.rs +++ b/tests/scanner_integration.rs @@ -104,16 +104,19 @@ fn cancelled_scan_stops_without_emitting_results() { #[cfg(unix)] #[test] fn symlinked_entries_produce_partial_scan_warning() { - use std::os::unix::fs::symlink; use frostscan::services::scanner::ScanFailureKind; + use std::os::unix::fs::symlink; let root = unique_temp_dir("symlink-scan"); let target = root.join("target"); - let linked = root.join("linked-dir"); + let real_dir = root.join("real-dir"); + let linked = target.join("linked-dir"); fs::create_dir(&target).unwrap(); + fs::create_dir(&real_dir).unwrap(); fs::write(target.join("payload.txt"), vec![0_u8; 7]).unwrap(); - symlink(&target, &linked).unwrap(); + fs::write(real_dir.join("nested.txt"), vec![0_u8; 3]).unwrap(); + symlink(&real_dir, &linked).unwrap(); let entries = list_directory(root.clone()).unwrap(); let (tx, rx) = unbounded(); @@ -121,13 +124,20 @@ fn symlinked_entries_produce_partial_scan_warning() { compute_directory_sizes_async(entries, tx, cancel); - let event = rx.recv_timeout(Duration::from_secs(2)).unwrap(); - let DirSizeEvent::Done(result) = event else { - panic!("expected successful scan event"); + let result = loop { + let event = rx.recv_timeout(Duration::from_secs(2)).unwrap(); + let DirSizeEvent::Done(result) = event else { + panic!("expected successful scan event"); + }; + + if result.path == target { + break result; + } }; assert!(result.partial); assert_eq!(result.issue, Some(ScanFailureKind::SymlinkSkipped)); + assert_eq!(result.size, 7); fs::remove_dir_all(root).unwrap(); }