implement locate and updatedb (#536)

---------

Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
Jeremy Smart
2026-06-06 04:10:44 -04:00
committed by GitHub
parent e7c34ab12f
commit c5ff1bbb6b
14 changed files with 1551 additions and 1 deletions
Generated
+229
View File
@@ -87,6 +87,21 @@ dependencies = [
"once_cell",
]
[[package]]
name = "assert_cmd"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aa3a22042e45de04255c7bf3626e239f450200fd0493c1e382263544b20aea6"
dependencies = [
"anstyle",
"bstr",
"libc",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "autocfg"
version = "1.5.0"
@@ -242,6 +257,12 @@ version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "displaydoc"
version = "0.2.5"
@@ -271,6 +292,12 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "either"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -325,15 +352,19 @@ name = "findutils"
version = "0.8.0"
dependencies = [
"argmax",
"assert_cmd",
"chrono",
"clap",
"ctor",
"faccess",
"filetime",
"itertools",
"nix 0.31.3",
"onig",
"regex",
"rstest",
"tempfile",
"thiserror",
"uucore",
"uutests",
"walkdir",
@@ -390,6 +421,48 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-macro"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-timer"
version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "getrandom"
version = "0.4.2"
@@ -498,6 +571,15 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.18"
@@ -688,6 +770,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "pkg-config"
version = "0.3.32"
@@ -715,6 +803,33 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "predicates"
version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe"
dependencies = [
"anstyle",
"difflib",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144"
[[package]]
name = "predicates-tree"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "pretty_assertions"
version = "1.4.1"
@@ -735,6 +850,15 @@ dependencies = [
"syn",
]
[[package]]
name = "proc-macro-crate"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -805,6 +929,12 @@ version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "relative-path"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "rlimit"
version = "0.11.0"
@@ -814,12 +944,51 @@ dependencies = [
"libc",
]
[[package]]
name = "rstest"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d"
dependencies = [
"futures-timer",
"futures-util",
"rstest_macros",
"rustc_version",
]
[[package]]
name = "rstest_macros"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746"
dependencies = [
"cfg-if",
"glob",
"proc-macro-crate",
"proc-macro2",
"quote",
"regex",
"relative-path",
"rustc_version",
"syn",
"unicode-ident",
]
[[package]]
name = "rustc-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "1.1.4"
@@ -909,6 +1078,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.1"
@@ -965,6 +1140,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "termtree"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
[[package]]
name = "thiserror"
version = "2.0.18"
@@ -1029,6 +1210,36 @@ dependencies = [
"zerovec",
]
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.25.12+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7"
dependencies = [
"indexmap",
"toml_datetime",
"toml_parser",
"winnow",
]
[[package]]
name = "toml_parser"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow",
]
[[package]]
name = "type-map"
version = "0.5.1"
@@ -1135,6 +1346,15 @@ dependencies = [
"xattr",
]
[[package]]
name = "wait-timeout"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
dependencies = [
"libc",
]
[[package]]
name = "walkdir"
version = "2.5.0"
@@ -1424,6 +1644,15 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
+13 -1
View File
@@ -12,18 +12,22 @@ authors = ["uutils developers"]
[dependencies]
argmax = "0.4.0"
chrono = "0.4.45"
clap = "4.6"
clap = { version = "4.6", features = ["env"] }
faccess = "0.2.4"
nix = { version = "0.31", features = ["fs", "user"] }
onig = { version = "6.5", default-features = false }
regex = "1.12"
uucore = { version = "0.9.0", features = ["entries", "fs", "fsext", "mode"] }
walkdir = "2.5"
itertools = "0.14.0"
thiserror = "2.0.12"
[dev-dependencies]
assert_cmd = "2"
ctor = "1.0"
filetime = "0.2"
nix = { version = "0.31", features = ["fs"] }
rstest = "0.25.0"
tempfile = "3"
uutests = "0.9.0"
@@ -31,6 +35,14 @@ uutests = "0.9.0"
name = "find"
path = "src/find/main.rs"
[[bin]]
name = "locate"
path = "src/locate/main.rs"
[[bin]]
name = "updatedb"
path = "src/updatedb/main.rs"
[[bin]]
name = "xargs"
path = "src/xargs/main.rs"
+1
View File
@@ -74,6 +74,7 @@ use std::{
use super::{Config, Dependencies};
pub use entry::{FileType, WalkEntry, WalkError};
pub use regex::RegexType;
/// Symlink following mode.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+3
View File
@@ -5,4 +5,7 @@
// https://opensource.org/licenses/MIT.
pub mod find;
#[cfg(unix)]
pub mod locate;
pub mod updatedb;
pub mod xargs;
+18
View File
@@ -0,0 +1,18 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#[cfg(not(windows))]
fn main() {
let args = std::env::args().collect::<Vec<String>>();
let strs: Vec<&str> = args.iter().map(std::convert::AsRef::as_ref).collect();
std::process::exit(findutils::locate::locate_main(strs.as_slice()));
}
#[cfg(windows)]
fn main() {
// TODO: locate currently uses UNIX-specific OsString APIs. If those can be worked around, locate
// should function normally on Windows. If and when that happens, make sure to make a separate
// windows test database with \ instead of /.
println!("locate is unsupported on Windows");
}
+713
View File
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
#[cfg(not(windows))]
fn main() {
let args = std::env::args().collect::<Vec<String>>();
let strs: Vec<&str> = args.iter().map(std::convert::AsRef::as_ref).collect();
std::process::exit(findutils::updatedb::updatedb_main(strs.as_slice()));
}
#[cfg(windows)]
fn main() {
println!("updatedb is unsupported on Windows");
}
+346
View File
@@ -0,0 +1,346 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
use std::{
cell::RefCell,
fmt::Display,
fs::OpenOptions,
io::{stderr, BufRead, BufReader, BufWriter, Write},
path::PathBuf,
rc::Rc,
str::FromStr,
time::SystemTime,
};
use clap::{crate_version, value_parser, Arg, ArgAction, ArgMatches, Command};
use uucore::error::UResult;
use crate::find::{find_main, Dependencies};
// TODO: the `localuser` and `netuser` arguments are accepted but not yet honored; handling them
// will likely involve splitting the find logic into two calls.
pub struct Config {
find_options: String,
local_paths: Vec<PathBuf>,
net_paths: Vec<String>,
prune_paths: Vec<PathBuf>,
prune_fs: Vec<String>,
output: PathBuf,
db_format: DbFormat,
}
impl From<ArgMatches> for Config {
fn from(value: ArgMatches) -> Self {
Self {
find_options: value
.get_one::<String>("findoptions")
.cloned()
.unwrap_or_else(String::new),
local_paths: value
.get_one::<String>("localpaths")
.map(|s| {
s.split_whitespace()
.filter_map(|s| PathBuf::from_str(s).ok())
.collect()
})
.unwrap_or_else(|| vec![PathBuf::from("/")]),
net_paths: value
.get_one::<String>("netpaths")
.map(|s| s.split_whitespace().map(|s| s.to_owned()).collect())
.unwrap_or_default(),
prune_paths: value
.get_one::<String>("prunepaths")
.map(|s| s.split_whitespace().map(PathBuf::from).collect())
.unwrap_or_else(|| {
["/tmp", "/usr/tmp", "/var/tmp", "/afs"]
.into_iter()
.map(PathBuf::from)
.collect()
}),
prune_fs: value
.get_one::<String>("prunefs")
.map(|s| s.split_whitespace().map(|s| s.to_owned()).collect())
.unwrap_or_else(|| {
["nfs", "NFS", "proc"]
.into_iter()
.map(str::to_string)
.collect()
}),
db_format: value
.get_one::<DbFormat>("dbformat")
.copied()
.unwrap_or_default(),
output: value
.get_one::<PathBuf>("output")
.cloned()
// FIXME: the default should be platform-dependent
.unwrap_or_else(|| PathBuf::from("/usr/local/var/locatedb")),
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub enum DbFormat {
#[default]
Locate02,
}
// used for locate's --statistics
impl Display for DbFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Locate02 => f.write_str("GNU LOCATE02"),
}
}
}
fn uu_app() -> Command {
Command::new("updatedb")
.version(crate_version!())
.arg(
Arg::new("findoptions")
.long("findoptions")
.require_equals(true)
.env("FINDOPTIONS")
.action(ArgAction::Append),
)
.arg(
Arg::new("localpaths")
.long("localpaths")
.require_equals(true)
.action(ArgAction::Set),
)
.arg(
Arg::new("netpaths")
.long("netpaths")
.require_equals(true)
.env("NETPATHS")
.action(ArgAction::Set),
)
.arg(
Arg::new("prunepaths")
.long("prunepaths")
.require_equals(true)
.env("PRUNEPATHS")
.action(ArgAction::Set),
)
.arg(
Arg::new("prunefs")
.long("prunefs")
.require_equals(true)
.env("PRUNEFS")
.action(ArgAction::Set),
)
.arg(
Arg::new("output")
.long("output")
.require_equals(true)
.value_parser(value_parser!(PathBuf))
.action(ArgAction::Set),
)
.arg(
Arg::new("localuser")
.long("localuser")
.require_equals(true)
.env("LOCALUSER")
.action(ArgAction::Set),
)
.arg(
Arg::new("netuser")
.long("netuser")
.require_equals(true)
.env("NETUSER")
.action(ArgAction::Set),
)
.arg(
Arg::new("dbformat")
.long("dbformat")
.require_equals(true)
.value_parser(["LOCATE02"])
.action(ArgAction::Set),
)
}
// The LOCATE02 format elides bytes from the path until the first byte that differs from the
// previous entry. It keeps a running total of the prefix length, and uses 1 or 3 bytes to write
// the difference from the previous prefix length. Paths are provided in sorted order by find.
struct Frcoder<'a> {
reader: BufReader<&'a [u8]>,
prev: Option<Vec<u8>>,
prefix: usize,
ty: DbFormat,
}
impl<'a> Frcoder<'a> {
fn new(v: &'a [u8], ty: DbFormat) -> Self {
Self {
reader: BufReader::new(v),
prev: None,
prefix: 0,
ty,
}
}
fn generate_header(&self) -> Vec<u8> {
match self.ty {
DbFormat::Locate02 => "\0LOCATE02\0".as_bytes().to_vec(),
}
}
}
impl Iterator for Frcoder<'_> {
type Item = Vec<u8>;
fn next(&mut self) -> Option<Self::Item> {
let mut path = Vec::new();
// find prints nul bytes after each path
if self.reader.read_until(b'\0', &mut path).ok()? == 0 {
return None;
}
let prefix = path
.iter()
.zip(self.prev.as_deref().unwrap_or_default())
.take_while(|(a, b)| a == b)
.count();
let diff = prefix as i32 - self.prefix as i32;
// if the prefix delta exceeds 0x7f, we use 0x80 to signal that the next two bytes comprise
// the delta
let mut out = Vec::new();
if diff.abs() > 0x7f {
out.push(0x80);
out.extend((diff as i16).to_be_bytes());
} else {
out.push(diff as u8);
}
out.extend(path.iter().skip(prefix));
self.prefix = prefix;
self.prev = Some(path);
Some(out)
}
}
// capture find's stdout
struct CapturedDependencies {
output: Rc<RefCell<dyn Write>>,
now: SystemTime,
}
impl CapturedDependencies {
fn new(output: Rc<RefCell<dyn Write>>) -> Self {
Self {
output,
now: SystemTime::now(),
}
}
}
impl Dependencies for CapturedDependencies {
fn get_output(&self) -> &RefCell<dyn Write> {
self.output.as_ref()
}
fn now(&self) -> SystemTime {
self.now
}
fn confirm(&self, _prompt: &str) -> bool {
// updatedb runs non-interactively; there is nothing to confirm.
false
}
}
fn do_updatedb(args: &[&str]) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args)?;
let config = Config::from(matches);
// offload most of the logic to find. Build the argument list as discrete tokens rather than
// a single string so that paths and options aren't re-split on whitespace and so that an empty
// `prunefs`/`prunepaths` can't produce an invalid expression (e.g. an empty `( )` group).
let mut tokens: Vec<String> = Vec::new();
tokens.extend(
config
.local_paths
.iter()
.filter_map(|p| p.to_str())
.map(String::from),
);
tokens.extend(config.net_paths.iter().cloned());
// FINDOPTIONS is a single string of options, split on whitespace as GNU updatedb does
tokens.extend(config.find_options.split_whitespace().map(String::from));
// Each clause prunes a subtree (by filesystem type or by path); the final clause prints the
// entry. Clauses are OR-ed together so a path that matches no prune clause gets printed.
let mut clauses: Vec<Vec<String>> = Vec::new();
if !config.prune_fs.is_empty() {
let mut clause = vec![String::from("(")];
for (i, fs) in config.prune_fs.iter().enumerate() {
if i > 0 {
clause.push(String::from("-or"));
}
clause.push(String::from("-fstype"));
clause.push(fs.clone());
}
clause.push(String::from(")"));
clause.push(String::from("-prune"));
clauses.push(clause);
}
for path in config.prune_paths.iter().filter_map(|p| p.to_str()) {
clauses.push(vec![
String::from("-regex"),
path.to_string(),
String::from("-prune"),
]);
}
clauses.push(vec![String::from("-print0")]);
for (i, clause) in clauses.into_iter().enumerate() {
if i > 0 {
tokens.push(String::from("-or"));
}
tokens.extend(clause);
}
tokens.push(String::from("-sorted"));
let mut find_args = vec!["find"];
find_args.extend(tokens.iter().map(String::as_str));
let output = Rc::new(RefCell::new(Vec::new()));
let deps = CapturedDependencies::new(output.clone());
find_main(find_args.as_slice(), &deps);
let mut writer = BufWriter::new(
OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(config.output)?,
);
let output = output.borrow();
let frcoder = Frcoder::new(output.as_slice(), config.db_format);
writer.write_all(&frcoder.generate_header())?;
for v in frcoder {
writer.write_all(v.as_slice())?;
}
writer.flush()?;
Ok(())
}
pub fn updatedb_main(args: &[&str]) -> i32 {
match do_updatedb(args) {
Ok(()) => 0,
Err(e) => {
let _ = writeln!(&mut stderr(), "Error: {e}");
1
}
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
+213
View File
@@ -0,0 +1,213 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
mod common;
use std::process::Command;
use assert_cmd::{assert::OutputAssertExt, cargo::CommandCargoExt};
use rstest::rstest;
#[cfg(not(windows))]
const DB_FLAG: &str = "--database=test_data/db/test_data_db";
#[cfg(not(windows))]
const INVALID_DB_FLAG: &str = "--database=test_data/db/invalid_db";
#[cfg(not(windows))]
const OLD_DB_FLAG: &str = "--database=test_data/db/old_db";
#[test]
#[cfg(not(windows))]
fn test_locate_no_matches() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["usr", DB_FLAG])
.assert()
.failure();
}
#[test]
#[cfg(not(windows))]
fn test_locate_match() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["test_data", DB_FLAG])
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_locate_no_matches_basename() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["test_data1234567890", "--basename", DB_FLAG])
.assert()
.failure();
}
#[test]
#[cfg(not(windows))]
fn test_locate_match_basename() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["abbbc", "--basename", DB_FLAG])
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_locate_existing() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["abbbc", "--existing", DB_FLAG])
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_locate_non_existing() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["abbbc", "--non-existing", DB_FLAG])
.assert()
.failure();
}
#[test]
#[cfg(not(windows))]
fn test_locate_statistics() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["", "--statistics", DB_FLAG])
.assert()
.success();
}
#[rstest]
#[case("emacs")]
#[case("grep")]
#[case("posix-basic")]
#[case("posix-extended")]
#[cfg(not(windows))]
fn test_locate_regex(#[case] input: &str) {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args([
"abbbc",
"--regex",
format!("--regextype={input}").as_str(),
DB_FLAG,
])
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_locate_all() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["abb", "bbc", "--all", DB_FLAG])
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_locate_all_regex() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["abb", "b*c", "--all", "--regex", DB_FLAG])
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_locate_invalid_db() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["test_data", INVALID_DB_FLAG])
.assert()
.failure();
}
#[test]
#[cfg(not(windows))]
fn test_locate_outdated_db() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["test_data", OLD_DB_FLAG])
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_locate_print_help() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.arg("--help")
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_locate_invalid_flag() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.arg("--unknown")
.assert()
.failure();
}
// an un-compilable regex should be reported as an error rather than silently matching nothing
#[test]
#[cfg(not(windows))]
fn test_locate_invalid_regex() {
Command::cargo_bin("locate")
.expect("couldn't find locate binary")
.args(["[", "--regex", DB_FLAG])
.assert()
.failure();
}
#[test]
#[cfg(not(windows))]
fn test_updatedb() {
Command::cargo_bin("updatedb")
.expect("couldn't find updatedb binary")
.args(["--localpaths=./test_data", "--output=/dev/null"])
.assert()
.success();
}
#[test]
#[cfg(not(windows))]
fn test_updatedb_invalid_flag() {
Command::cargo_bin("updatedb")
.expect("couldn't find updatedb binary")
.args(["--unknown"])
.assert()
.failure();
}
// empty prunefs/prunepaths must not produce an invalid find expression (e.g. an empty `( )` group)
#[test]
#[cfg(not(windows))]
fn test_updatedb_empty_prune() {
Command::cargo_bin("updatedb")
.expect("couldn't find updatedb binary")
.args([
"--localpaths=./test_data",
"--output=/dev/null",
"--prunefs=",
"--prunepaths=",
])
.assert()
.success();
}