Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/aho-corasick/src/packed/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 18 kB image not shown  

Quelle  tests.rs

  Sprache: Rust
 

use std::collections::HashMap;

use alloc::{
    format,
    string::{String, ToString},
    vec,
    vec::Vec,
};

use crate::{
    packed::{Config, MatchKind},
    util::search::Match,
};

/// A description of a single test against a multi-pattern searcher.
///
/// A single test may not necessarily pass on every configuration of a
/// searcher. The tests are categorized and grouped appropriately below.
#[derive(Clone, Debug, Eq, PartialEq)]
struct SearchTest {
    /// The name of this test, for debugging.
    name: &'static str,
    /// The patterns to search for.
    patterns: &'static [&'static str],
    /// The text to search.
    haystack: &'static str,
    /// Each match is a triple of (pattern_index, start, end), where
    /// pattern_index is an index into `patterns` and `start`/`end` are indices
    /// into `haystack`.
    matches: &'static [(usize, usize, usize)],
}

struct SearchTestOwned {
    offset: usize,
    name: String,
    patterns: Vec<String>,
    haystack: String,
    matches: Vec<(usize, usize, usize)>,
}

impl SearchTest {
    fn variations(&self) -> Vec<SearchTestOwned> {
        let count = if cfg!(miri) { 1 } else { 261 };
        let mut tests = vec![];
        for i in 0..count {
            tests.push(self.offset_prefix(i));
            tests.push(self.offset_suffix(i));
            tests.push(self.offset_both(i));
        }
        tests
    }

    fn offset_both(&self, off: usize) -> SearchTestOwned {
        SearchTestOwned {
            offset: off,
            name: self.name.to_string(),
            patterns: self.patterns.iter().map(|s| s.to_string()).collect(),
            haystack: format!(
                "{}{}{}",
                "Z".repeat(off),
                self.haystack,
                "Z".repeat(off)
            ),
            matches: self
                .matches
                .iter()
                .map(|&(id, s, e)| (id, s + off, e + off))
                .collect(),
        }
    }

    fn offset_prefix(&self, off: usize) -> SearchTestOwned {
        SearchTestOwned {
            offset: off,
            name: self.name.to_string(),
            patterns: self.patterns.iter().map(|s| s.to_string()).collect(),
            haystack: format!("{}{}""Z".repeat(off), self.haystack),
            matches: self
                .matches
                .iter()
                .map(|&(id, s, e)| (id, s + off, e + off))
                .collect(),
        }
    }

    fn offset_suffix(&self, off: usize) -> SearchTestOwned {
        SearchTestOwned {
            offset: off,
            name: self.name.to_string(),
            patterns: self.patterns.iter().map(|s| s.to_string()).collect(),
            haystack: format!("{}{}"self.haystack, "Z".repeat(off)),
            matches: self.matches.to_vec(),
        }
    }
}

/// Short-hand constructor for SearchTest. We use it a lot below.
macro_rules! t {
    ($name:ident, $patterns:expr, $haystack:expr, $matches:expr) => {
        SearchTest {
            name: stringify!($name),
            patterns: $patterns,
            haystack: $haystack,
            matches: $matches,
        }
    };
}

/// A collection of test groups.
type TestCollection = &'static [&'static [SearchTest]];

// Define several collections corresponding to the different type of match
// semantics supported. These collections have some overlap, but each
// collection should have some tests that no other collection has.

/// Tests for leftmost-first match semantics.
const PACKED_LEFTMOST_FIRST: TestCollection =
    &[BASICS, LEFTMOST, LEFTMOST_FIRST, REGRESSION, TEDDY];

/// Tests for leftmost-longest match semantics.
const PACKED_LEFTMOST_LONGEST: TestCollection =
    &[BASICS, LEFTMOST, LEFTMOST_LONGEST, REGRESSION, TEDDY];

// Now define the individual tests that make up the collections above.

/// A collection of tests for the that should always be true regardless of
/// match semantics. That is, all combinations of leftmost-{first, longest}
/// should produce the same answer.
const BASICS: &'static [SearchTest] = &[
    t!(basic001, &["a"], "", &[]),
    t!(basic010, &["a"], "a", &[(001)]),
    t!(basic020, &["a"], "aa", &[(001), (012)]),
    t!(basic030, &["a"], "aaa", &[(001), (012), (023)]),
    t!(basic040, &["a"], "aba", &[(001), (023)]),
    t!(basic050, &["a"], "bba", &[(023)]),
    t!(basic060, &["a"], "bbb", &[]),
    t!(basic070, &["a"], "bababbbba", &[(012), (034), (089)]),
    t!(basic100, &["aa"], "", &[]),
    t!(basic110, &["aa"], "aa", &[(002)]),
    t!(basic120, &["aa"], "aabbaa", &[(002), (046)]),
    t!(basic130, &["aa"], "abbab", &[]),
    t!(basic140, &["aa"], "abbabaa", &[(057)]),
    t!(basic150, &["aaa"], "aaa", &[(003)]),
    t!(basic200, &["abc"], "abc", &[(003)]),
    t!(basic210, &["abc"], "zazabzabcz", &[(069)]),
    t!(basic220, &["abc"], "zazabczabcz", &[(036), (0710)]),
    t!(basic230, &["abcd"], "abcd", &[(004)]),
    t!(basic240, &["abcd"], "zazabzabcdz", &[(0610)]),
    t!(basic250, &["abcd"], "zazabcdzabcdz", &[(037), (0812)]),
    t!(basic300, &["a""b"], "", &[]),
    t!(basic310, &["a""b"], "z", &[]),
    t!(basic320, &["a""b"], "b", &[(101)]),
    t!(basic330, &["a""b"], "a", &[(001)]),
    t!(
        basic340,
        &["a""b"],
        "abba",
        &[(001), (112), (123), (034),]
    ),
    t!(
        basic350,
        &["b""a"],
        "abba",
        &[(101), (012), (023), (134),]
    ),
    t!(basic360, &["abc""bc"], "xbc", &[(113),]),
    t!(basic400, &["foo""bar"], "", &[]),
    t!(basic410, &["foo""bar"], "foobar", &[(003), (136),]),
    t!(basic420, &["foo""bar"], "barfoo", &[(103), (036),]),
    t!(basic430, &["foo""bar"], "foofoo", &[(003), (036),]),
    t!(basic440, &["foo""bar"], "barbar", &[(103), (136),]),
    t!(basic450, &["foo""bar"], "bafofoo", &[(047),]),
    t!(basic460, &["bar""foo"], "bafofoo", &[(147),]),
    t!(basic470, &["foo""bar"], "fobabar", &[(147),]),
    t!(basic480, &["bar""foo"], "fobabar", &[(047),]),
    t!(basic700, &["yabcdef""abcdezghi"], "yabcdefghi", &[(le='color: green'>007),]),
    t!(basic710, &["yabcdef""abcdezghi"], "yabcdezghi", &[(le='color: green'>1110),]),
    t!(
        basic720,
        &["yabcdef""bcdeyabc""abcdezghi"],
        "yabcdezghi",
        &[(2110),]
    ),
    t!(basic810, &["abcd""bcd""cd"], "abcd", &[(004),]),
    t!(basic820, &["bcd""cd""abcd"], "abcd", &[(204),]),
    t!(basic830, &["abc""bc"], "zazabcz", &[(036),]),
    t!(
        basic840,
        &["ab""ba"],
        "abababa",
        &[(002), (024), (046),]
    ),
    t!(basic850, &["foo""foo"], "foobarfoo", &[(003), (069),]),
];

/// Tests for leftmost match semantics. These should pass for both
/// leftmost-first and leftmost-longest match kinds. Stated differently, among
/// ambiguous matches, the longest match and the match that appeared first when
/// constructing the automaton should always be the same.
const LEFTMOST: &'static [SearchTest] = &[
    t!(leftmost000, &["ab""ab"], "abcd", &[(002)]),
    t!(leftmost030, &["a""ab"], "aa", &[(001), (012)]),
    t!(leftmost031, &["ab""a"], "aa", &[(101), (112)]),
    t!(leftmost032, &["ab""a"], "xayabbbz", &[(112), (035)]),
    t!(leftmost300, &["abcd""bce""b"], "abce", &[(114)]),
    t!(leftmost310, &["abcd""ce""bc"], "abce", &[(213)]),
    t!(leftmost320, &["abcd""bce""ce""b"], "abce", &[('color: green'>114)]),
    t!(leftmost330, &["abcd""bce""cz""bc"], "abcz", &[(='color: green'>313)]),
    t!(leftmost340, &["bce""cz""bc"], "bcz", &[(202)]),
    t!(leftmost350, &["abc""bd""ab"], "abd", &[(202)]),
    t!(
        leftmost360,
        &["abcdefghi""hz""abcdefgh"],
        "abcdefghz",
        &[(208),]
    ),
    t!(
        leftmost370,
        &["abcdefghi""cde""hz""abcdefgh"],
        "abcdefghz",
        &[(308),]
    ),
    t!(
        leftmost380,
        &["abcdefghi""hz""abcdefgh""a"],
        "abcdefghz",
        &[(208),]
    ),
    t!(
        leftmost390,
        &["b""abcdefghi""hz""abcdefgh"],
        "abcdefghz",
        &[(308),]
    ),
    t!(
        leftmost400,
        &["h""abcdefghi""hz""abcdefgh"],
        "abcdefghz",
        &[(308),]
    ),
    t!(
        leftmost410,
        &["z""abcdefghi""hz""abcdefgh"],
        "abcdefghz",
        &[(308), (089),]
    ),
];

/// Tests for non-overlapping leftmost-first match semantics. These tests
/// should generally be specific to leftmost-first, which means they should
/// generally fail under leftmost-longest semantics.
const LEFTMOST_FIRST: &'static [SearchTest] = &[
    t!(leftfirst000, &["ab""abcd"], "abcd", &[(002)]),
    t!(leftfirst020, &["abcd""ab"], "abcd", &[(004)]),
    t!(leftfirst030, &["ab""ab"], "abcd", &[(002)]),
    t!(leftfirst040, &["a""ab"], "xayabbbz", &[(012), (034)]),
    t!(leftfirst100, &["abcdefg""bcde""bcdef"], "abcdef", &[(style='color: green'>115)]),
    t!(leftfirst110, &["abcdefg""bcdef""bcde"], "abcdef", &[(style='color: green'>116)]),
    t!(leftfirst300, &["abcd""b""bce"], "abce", &[(112)]),
    t!(
        leftfirst310,
        &["abcd""b""bce""ce"],
        "abce",
        &[(112), (324),]
    ),
    t!(
        leftfirst320,
        &["a""abcdefghi""hz""abcdefgh"],
        "abcdefghz",
        &[(001), (279),]
    ),
    t!(leftfirst330, &["a""abab"], "abab", &[(001), (023)]),
    t!(
        leftfirst340,
        &["abcdef""x""x""x""x""x""x""abcde"],
        "abcdef",
        &[(006)]
    ),
];

/// Tests for non-overlapping leftmost-longest match semantics. These tests
/// should generally be specific to leftmost-longest, which means they should
/// generally fail under leftmost-first semantics.
const LEFTMOST_LONGEST: &'static [SearchTest] = &[
    t!(leftlong000, &["ab""abcd"], "abcd", &[(104)]),
    t!(leftlong010, &["abcd""bcd""cd""b"], "abcd", &[('color: green'>004),]),
    t!(leftlong040, &["a""ab"], "a", &[(001)]),
    t!(leftlong050, &["a""ab"], "ab", &[(102)]),
    t!(leftlong060, &["ab""a"], "a", &[(101)]),
    t!(leftlong070, &["ab""a"], "ab", &[(002)]),
    t!(leftlong100, &["abcdefg""bcde""bcdef"], "abcdef", &[(tyle='color: green'>216)]),
    t!(leftlong110, &["abcdefg""bcdef""bcde"], "abcdef", &[(tyle='color: green'>116)]),
    t!(leftlong300, &["abcd""b""bce"], "abce", &[(214)]),
    t!(
        leftlong310,
        &["a""abcdefghi""hz""abcdefgh"],
        "abcdefghz",
        &[(308),]
    ),
    t!(leftlong320, &["a""abab"], "abab", &[(104)]),
    t!(leftlong330, &["abcd""b""ce"], "abce", &[(112), (224),]),
    t!(leftlong340, &["a""ab"], "xayabbbz", &[(012), (135)]),
];

/// Regression tests that are applied to all combinations.
///
/// If regression tests are needed for specific match semantics, then add them
/// to the appropriate group above.
const REGRESSION: &'static [SearchTest] = &[
    t!(regression010, &["inf""ind"], "infind", &[(003), (136),]),
    t!(regression020, &["ind""inf"], "infind", &[(103), (036),]),
    t!(
        regression030,
        &["libcore/""libstd/"],
        "libcore/char/methods.rs",
        &[(008),]
    ),
    t!(
        regression040,
        &["libstd/""libcore/"],
        "libcore/char/methods.rs",
        &[(108),]
    ),
    t!(
        regression050,
        &["\x00\x00\x01""\x00\x00\x00"],
        "\x00\x00\x00",
        &[(103),]
    ),
    t!(
        regression060,
        &["\x00\x00\x00""\x00\x00\x01"],
        "\x00\x00\x00",
        &[(003),]
    ),
];

const TEDDY: &'static [SearchTest] = &[
    t!(
        teddy010,
        &["a""b""c""d""e""f""g""h""i""j""k"],
        "abcdefghijk",
        &[
            (001),
            (112),
            (223),
            (334),
            (445),
            (556),
            (667),
            (778),
            (889),
            (9910),
            (101011)
        ]
    ),
    t!(
        teddy020,
        &["ab""bc""cd""de""ef""fg""gh""hi""ij""jk""kl"],
        "abcdefghijk",
        &[(002), (224), (446), (668), (8810),]
    ),
    t!(
        teddy030,
        &["abc"],
        "abcdefghijklmnopqrstuvwxyzabcdefghijk",
        &[(003), (02629)]
    ),
];

// Now define a test for each combination of things above that we want to run.
// Since there are a few different combinations for each collection of tests,
// we define a couple of macros to avoid repetition drudgery. The testconfig
// macro constructs the automaton from a given match kind, and runs the search
// tests one-by-one over the given collection. The `with` parameter allows one
// to configure the config with additional parameters. The testcombo macro
// invokes testconfig in precisely this way: it sets up several tests where
// each one turns a different knob on Config.

macro_rules! testconfig {
    ($name:ident, $collection:expr, $with:expr) => {
        #[test]
        fn $name() {
            run_search_tests($collection, |test| {
                let mut config = Config::new();
                $with(&mut config);
                let mut builder = config.builder();
                builder.extend(test.patterns.iter().map(|p| p.as_bytes()));
                let searcher = match builder.build() {
                    Some(searcher) => searcher,
                    None => {
                        // For x86-64 and aarch64, not building a searcher is
                        // probably a bug, so be loud.
                        if cfg!(any(
                            target_arch = "x86_64",
                            target_arch = "aarch64"
                        )) {
                            panic!("failed to build packed searcher")
                        }
                        return None;
                    }
                };
                Some(searcher.find_iter(&test.haystack).collect())
            });
        }
    };
}

testconfig!(
    search_default_leftmost_first,
    PACKED_LEFTMOST_FIRST,
    |_: &mut Config| {}
);

testconfig!(
    search_default_leftmost_longest,
    PACKED_LEFTMOST_LONGEST,
    |c: &mut Config| {
        c.match_kind(MatchKind::LeftmostLongest);
    }
);

testconfig!(
    search_teddy_leftmost_first,
    PACKED_LEFTMOST_FIRST,
    |c: &mut Config| {
        c.only_teddy(true);
    }
);

testconfig!(
    search_teddy_leftmost_longest,
    PACKED_LEFTMOST_LONGEST,
    |c: &mut Config| {
        c.only_teddy(true).match_kind(MatchKind::LeftmostLongest);
    }
);

testconfig!(
    search_teddy_ssse3_leftmost_first,
    PACKED_LEFTMOST_FIRST,
    |c: &mut Config| {
        c.only_teddy(true);
        #[cfg(target_arch = "x86_64")]
        if std::is_x86_feature_detected!("ssse3") {
            c.only_teddy_256bit(Some(false));
        }
    }
);

testconfig!(
    search_teddy_ssse3_leftmost_longest,
    PACKED_LEFTMOST_LONGEST,
    |c: &mut Config| {
        c.only_teddy(true).match_kind(MatchKind::LeftmostLongest);
        #[cfg(target_arch = "x86_64")]
        if std::is_x86_feature_detected!("ssse3") {
            c.only_teddy_256bit(Some(false));
        }
    }
);

testconfig!(
    search_teddy_avx2_leftmost_first,
    PACKED_LEFTMOST_FIRST,
    |c: &mut Config| {
        c.only_teddy(true);
        #[cfg(target_arch = "x86_64")]
        if std::is_x86_feature_detected!("avx2") {
            c.only_teddy_256bit(Some(true));
        }
    }
);

testconfig!(
    search_teddy_avx2_leftmost_longest,
    PACKED_LEFTMOST_LONGEST,
    |c: &mut Config| {
        c.only_teddy(true).match_kind(MatchKind::LeftmostLongest);
        #[cfg(target_arch = "x86_64")]
        if std::is_x86_feature_detected!("avx2") {
            c.only_teddy_256bit(Some(true));
        }
    }
);

testconfig!(
    search_teddy_fat_leftmost_first,
    PACKED_LEFTMOST_FIRST,
    |c: &mut Config| {
        c.only_teddy(true);
        #[cfg(target_arch = "x86_64")]
        if std::is_x86_feature_detected!("avx2") {
            c.only_teddy_fat(Some(true));
        }
    }
);

testconfig!(
    search_teddy_fat_leftmost_longest,
    PACKED_LEFTMOST_LONGEST,
    |c: &mut Config| {
        c.only_teddy(true).match_kind(MatchKind::LeftmostLongest);
        #[cfg(target_arch = "x86_64")]
        if std::is_x86_feature_detected!("avx2") {
            c.only_teddy_fat(Some(true));
        }
    }
);

testconfig!(
    search_rabinkarp_leftmost_first,
    PACKED_LEFTMOST_FIRST,
    |c: &mut Config| {
        c.only_rabin_karp(true);
    }
);

testconfig!(
    search_rabinkarp_leftmost_longest,
    PACKED_LEFTMOST_LONGEST,
    |c: &mut Config| {
        c.only_rabin_karp(true).match_kind(MatchKind::LeftmostLongest);
    }
);

#[test]
fn search_tests_have_unique_names() {
    let assert = |constname, tests: &[SearchTest]| {
        let mut seen = HashMap::new(); // map from test name to position
        for (i, test) in tests.iter().enumerate() {
            if !seen.contains_key(test.name) {
                seen.insert(test.name, i);
            } else {
                let last = seen[test.name];
                panic!(
                    "{} tests have duplicate names at positions {} and {}",
                    constname, last, i
                );
            }
        }
    };
    assert("BASICS", BASICS);
    assert("LEFTMOST", LEFTMOST);
    assert("LEFTMOST_FIRST", LEFTMOST_FIRST);
    assert("LEFTMOST_LONGEST", LEFTMOST_LONGEST);
    assert("REGRESSION", REGRESSION);
    assert("TEDDY", TEDDY);
}

fn run_search_tests<F: FnMut(&SearchTestOwned) -> Option<Vec<Match>>>(
    which: TestCollection,
    mut f: F,
) {
    let get_match_triples =
        |matches: Vec<Match>| -> Vec<(usize, usize, usize)> {
            matches
                .into_iter()
                .map(|m| (m.pattern().as_usize(), m.start(), m.end()))
                .collect()
        };
    for &tests in which {
        for spec in tests {
            for test in spec.variations() {
                let results = match f(&test) {
                    None => continue,
                    Some(results) => results,
                };
                assert_eq!(
                    test.matches,
                    get_match_triples(results).as_slice(),
                    "test: {}, patterns: {:?}, haystack(len={:?}): {:?}, \
                     offset: {:?}",
                    test.name,
                    test.patterns,
                    test.haystack.len(),
                    test.haystack,
                    test.offset,
                );
            }
        }
    }
}

Messung V0.5 in Prozent
C=94 H=99 G=96

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet am  2026-08-26) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.