mod support;
use std::fs;
use predicates::prelude::*;
use support::lx_no_colour;
use tempfile::tempdir;
fn filtering_fixture() -> tempfile::TempDir {
let dir = tempdir().expect("failed to create tempdir");
let root = dir.path();
fs::create_dir_all(root.join("src")).unwrap();
fs::create_dir_all(root.join("target/debug")).unwrap();
fs::create_dir_all(root.join("node_modules/foo")).unwrap();
fs::create_dir_all(root.join("docs")).unwrap();
fs::write(root.join("src/main.rs"), "fn main() {}").unwrap();
fs::write(root.join("target/debug/binary"), "ELF").unwrap();
fs::write(root.join("node_modules/foo/index.js"), "module").unwrap();
fs::write(root.join("docs/readme.md"), "# Docs").unwrap();
fs::write(root.join("Cargo.toml"), "[package]").unwrap();
fs::write(root.join("notes.tmp"), "scratch").unwrap();
dir
}
#[test]
fn ignore_hides_matching_files() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-1", "-I", "*.tmp"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("notes.tmp").not())
.stdout(predicate::str::contains("Cargo.toml"));
}
#[test]
fn ignore_hides_matching_directories() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-1", "-I", "target"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("target").not())
.stdout(predicate::str::contains("src"));
}
#[test]
fn ignore_glob_alias_still_works() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-1", "--ignore-glob", "*.tmp"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("notes.tmp").not());
}
#[test]
fn prune_shows_directory_but_hides_children_in_tree() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-T", "-P", "target"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("target"))
.stdout(predicate::str::contains("debug").not())
.stdout(predicate::str::contains("binary").not())
.stdout(predicate::str::contains("main.rs"));
}
#[test]
fn prune_shows_directory_but_hides_children_in_recurse() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-R", "-P", "target"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("target"))
.stdout(predicate::str::contains("debug").not());
}
#[test]
fn prune_multiple_patterns() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-T", "-P", "target|node_modules"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("target"))
.stdout(predicate::str::contains("node_modules"))
.stdout(predicate::str::contains("debug").not())
.stdout(predicate::str::contains("foo").not())
.stdout(predicate::str::contains("main.rs"));
}
#[test]
fn prune_with_total_size() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-lTZ", "-P", "target"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("target"))
.stdout(predicate::str::contains("debug").not());
}
#[test]
fn prune_does_not_affect_non_recursive_listing() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-1", "-P", "target"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("target"));
}
#[test]
fn prune_short_flag() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-T", "-P", "target"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("target"))
.stdout(predicate::str::contains("debug").not());
}
#[test]
fn ignore_and_prune_compose() {
let dir = filtering_fixture();
lx_no_colour()
.args(["-T", "-I", "*.tmp", "-P", "target"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("notes.tmp").not())
.stdout(predicate::str::contains("target"))
.stdout(predicate::str::contains("debug").not())
.stdout(predicate::str::contains("main.rs"));
}
fn symlink_fixture() -> tempfile::TempDir {
use std::os::unix::fs as unix_fs;
let dir = tempdir().expect("failed to create tempdir");
let root = dir.path();
fs::write(root.join("real.txt"), "content").unwrap();
fs::write(root.join("target.rs"), "fn main() {}").unwrap();
unix_fs::symlink("target.rs", root.join("link.rs")).unwrap();
unix_fs::symlink("nonexistent", root.join("broken.rs")).unwrap();
dir
}
#[test]
fn symlinks_show_is_default() {
let dir = symlink_fixture();
lx_no_colour()
.args(["-1"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("real.txt"))
.stdout(predicate::str::contains("link.rs"))
.stdout(predicate::str::contains("broken.rs"));
}
#[test]
fn symlinks_hide() {
let dir = symlink_fixture();
lx_no_colour()
.args(["-1", "--symlinks=hide"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("real.txt"))
.stdout(predicate::str::contains("target.rs"))
.stdout(predicate::str::contains("link.rs").not())
.stdout(predicate::str::contains("broken.rs").not());
}
#[test]
fn symlinks_follow_shows_target_metadata() {
let dir = symlink_fixture();
lx_no_colour()
.args(["-l", "--symlinks=follow"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("link.rs"))
.stdout(predicate::str::contains("broken.rs"));
}
#[test]
fn symlinks_follow_recurses_into_linked_dirs() {
use std::os::unix::fs as unix_fs;
let dir = tempdir().expect("failed to create tempdir");
let root = dir.path();
fs::create_dir_all(root.join("real_dir")).unwrap();
fs::write(root.join("real_dir/inner.txt"), "inside").unwrap();
unix_fs::symlink("real_dir", root.join("linked_dir")).unwrap();
lx_no_colour()
.args(["-T", "--symlinks=show"])
.arg(dir.path())
.assert()
.success()
.stdout(predicate::str::contains("linked_dir"))
.stdout(predicate::str::contains("inner.txt"));
let output = lx_no_colour()
.args(["-T", "--symlinks=follow"])
.arg(dir.path())
.output()
.expect("failed to run lx");
let stdout = String::from_utf8_lossy(&output.stdout);
assert_eq!(stdout.matches("inner.txt").count(), 2,
"Expected inner.txt twice (under real_dir and linked_dir), got:\n{stdout}");
}