Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 12 additions & 1 deletion src/app/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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,
);
Expand Down
22 changes: 16 additions & 6 deletions tests/scanner_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,30 +104,40 @@ 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();
let cancel = Arc::new(AtomicBool::new(false));

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();
}
Loading