aboutsummaryrefslogtreecommitdiffstats
path: root/crates/atuin-nucleo/matcher/fuzz/fuzz_targets
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@atuin.sh>2026-03-16 16:28:54 -0700
committerGitHub <noreply@github.com>2026-03-16 16:28:54 -0700
commita964c27db2a359233bad200a64696b663eca4be5 (patch)
tree9370c6f7b541b79d7183dd754a9d6a863f51c1e2 /crates/atuin-nucleo/matcher/fuzz/fuzz_targets
parentfeat: Allow headless account ops against Hub server (#3280) (diff)
parentvendor nucleo fork into atuin workspace (diff)
downloadatuin-a964c27db2a359233bad200a64696b663eca4be5.zip
chore: vendor nucleo-ext + fork, so we can depend on our changes properly (#3284)
We cannot publish to crates.io without specifying a version, and we cannot do that without properly forking nucleo. We're shipping atuin-nucleo, but will likely drop this if we can get our changes upstream. This is highlighted in the README + manifest, and the original author is still included. Originally forked here: https://github.com/atuinsh/nucleo-ext cc @BinaryMuse - this should just be a vendor + restructure, but would appreciate the sanity check ## Checks - [ ] I am happy for maintainers to push small adjustments to this PR, to speed up the review cycle - [ ] I have checked that there are no existing pull requests for the same thing
Diffstat (limited to 'crates/atuin-nucleo/matcher/fuzz/fuzz_targets')
-rw-r--r--crates/atuin-nucleo/matcher/fuzz/fuzz_targets/fuzz_target_1.rs78
1 files changed, 78 insertions, 0 deletions
diff --git a/crates/atuin-nucleo/matcher/fuzz/fuzz_targets/fuzz_target_1.rs b/crates/atuin-nucleo/matcher/fuzz/fuzz_targets/fuzz_target_1.rs
new file mode 100644
index 00000000..00940d58
--- /dev/null
+++ b/crates/atuin-nucleo/matcher/fuzz/fuzz_targets/fuzz_target_1.rs
@@ -0,0 +1,78 @@
+#![no_main]
+
+use fzf_oxide::{chars, Matcher, MatcherConfig, Utf32Str};
+use libfuzzer_sys::arbitrary::Arbitrary;
+use libfuzzer_sys::fuzz_target;
+
+#[derive(Arbitrary, Debug)]
+pub struct Input<'a> {
+ haystack: &'a str,
+ needle: &'a str,
+ ignore_case: bool,
+ normalize: bool,
+}
+
+fuzz_target!(|data: Input<'_>| {
+ let mut data = data;
+ let mut config = MatcherConfig::DEFAULT;
+ config.ignore_case = data.ignore_case;
+ config.normalize = data.normalize;
+ let mut matcher = Matcher::new(config);
+ let mut indices_optimal = Vec::new();
+ let mut indices_greedy = Vec::new();
+ let mut needle_buf = Vec::new();
+ let mut haystack_buf = Vec::new();
+ let normalize = |mut c: char| {
+ if config.normalize {
+ c = chars::normalize(c);
+ }
+ if config.ignore_case {
+ c = chars::to_lower_case(c);
+ }
+ c
+ };
+ let needle: String = data.needle.chars().map(normalize).collect();
+ let needle_chars: Vec<_> = needle.chars().collect();
+ let needle = Utf32Str::new(&needle, &mut needle_buf);
+ let haystack = Utf32Str::new(data.haystack, &mut haystack_buf);
+
+ let greedy_score = matcher.fuzzy_indices_greedy(haystack, needle, &mut indices_greedy);
+ if greedy_score.is_some() {
+ let match_chars: Vec<_> = indices_greedy
+ .iter()
+ .map(|&i| normalize(haystack.get(i)))
+ .collect();
+ assert_eq!(
+ match_chars, needle_chars,
+ "failed match, found {indices_greedy:?} {match_chars:?} (greedy)"
+ );
+ }
+ let optimal_score = matcher.fuzzy_indices(haystack, needle, &mut indices_optimal);
+ if optimal_score.is_some() {
+ let match_chars: Vec<_> = indices_optimal
+ .iter()
+ .map(|&i| normalize(haystack.get(i)))
+ .collect();
+ assert_eq!(
+ match_chars, needle_chars,
+ "failed match, found {indices_optimal:?} {match_chars:?}"
+ );
+ }
+ match (greedy_score, optimal_score) {
+ (None, Some(score)) => unreachable!("optimal matched {score} but greedy did not match"),
+ (Some(score), None) => unreachable!("greedy matched {score} but optimal did not match"),
+ (Some(greedy), Some(optimal)) => {
+ assert!(
+ greedy <= optimal,
+ "optimal score must be at least the same as greedy score {greedy} {optimal}"
+ );
+ if indices_greedy == indices_optimal {
+ assert_eq!(
+ greedy, optimal,
+ "if matching same char greedy and optimal score should be identical"
+ )
+ }
+ }
+ (None, None) => (),
+ }
+});