Added smoke tests for new methods.

Fixed bug in existing StrSearcher impl
This commit is contained in:
Marvin Löbel 2015-04-02 01:12:58 +02:00
parent c29559d28a
commit fbba28e246
2 changed files with 160 additions and 22 deletions

View File

@ -1720,6 +1720,31 @@ mod pattern {
if rev {
v.reverse();
}
let mut first_index = 0;
let mut err = None;
for (i, e) in right.iter().enumerate() {
match *e {
Match(a, b) | Reject(a, b)
if a <= b && a == first_index => {
first_index = b;
}
_ => {
err = Some(i);
break;
}
}
}
if let Some(err) = err {
panic!("Input skipped range at {}", err);
}
if first_index != haystack.len() {
panic!("Did not cover whole input");
}
assert_eq!(v, right);
}
@ -1731,14 +1756,21 @@ mod pattern {
Reject(6, 7),
]);
make_test!(str_searcher_empty_needle_ascii_haystack, "", "abbcbbd", [
Match(0, 0),
Match(1, 1),
Match(2, 2),
Match(3, 3),
Match(4, 4),
Match(5, 5),
Match(6, 6),
Match(7, 7),
Match (0, 0),
Reject(0, 1),
Match (1, 1),
Reject(1, 2),
Match (2, 2),
Reject(2, 3),
Match (3, 3),
Reject(3, 4),
Match (4, 4),
Reject(4, 5),
Match (5, 5),
Reject(5, 6),
Match (6, 6),
Reject(6, 7),
Match (7, 7),
]);
make_test!(str_searcher_mulibyte_haystack, " ", "├──", [
Reject(0, 3),
@ -1746,10 +1778,13 @@ mod pattern {
Reject(6, 9),
]);
make_test!(str_searcher_empty_needle_mulibyte_haystack, "", "├──", [
Match(0, 0),
Match(3, 3),
Match(6, 6),
Match(9, 9),
Match (0, 0),
Reject(0, 3),
Match (3, 3),
Reject(3, 6),
Match (6, 6),
Reject(6, 9),
Match (9, 9),
]);
make_test!(str_searcher_empty_needle_empty_haystack, "", "", [
Match(0, 0),
@ -1778,6 +1813,96 @@ mod pattern {
}
macro_rules! generate_iterator_test {
{
$name:ident {
$(
($($arg:expr),*) -> [$($t:tt)*];
)*
}
with $fwd:expr, $bwd:expr;
} => {
#[test]
fn $name() {
$(
{
let res = vec![$($t)*];
let fwd_vec: Vec<_> = ($fwd)($($arg),*).collect();
assert_eq!(fwd_vec, res);
let mut bwd_vec: Vec<_> = ($bwd)($($arg),*).collect();
bwd_vec.reverse();
assert_eq!(bwd_vec, res);
}
)*
}
};
{
$name:ident {
$(
($($arg:expr),*) -> [$($t:tt)*];
)*
}
with $fwd:expr;
} => {
#[test]
fn $name() {
$(
{
let res = vec![$($t)*];
let fwd_vec: Vec<_> = ($fwd)($($arg),*).collect();
assert_eq!(fwd_vec, res);
}
)*
}
}
}
generate_iterator_test! {
double_ended_split {
("foo.bar.baz", '.') -> ["foo", "bar", "baz"];
("foo::bar::baz", "::") -> ["foo", "bar", "baz"];
}
with str::split, str::rsplit;
}
generate_iterator_test! {
double_ended_split_terminator {
("foo;bar;baz;", ';') -> ["foo", "bar", "baz"];
}
with str::split_terminator, str::rsplit_terminator;
}
generate_iterator_test! {
double_ended_matches {
("a1b2c3", char::is_numeric) -> ["1", "2", "3"];
}
with str::matches, str::rmatches;
}
generate_iterator_test! {
double_ended_match_indices {
("a1b2c3", char::is_numeric) -> [(1, 2), (3, 4), (5, 6)];
}
with str::match_indices, str::rmatch_indices;
}
generate_iterator_test! {
not_double_ended_splitn {
("foo::bar::baz", 2, "::") -> ["foo", "bar::baz"];
}
with str::splitn;
}
generate_iterator_test! {
not_double_ended_rsplitn {
("foo::bar::baz", 2, "::") -> ["baz", "foo::bar"];
}
with str::rsplitn;
}
mod bench {
use test::{Bencher, black_box};

View File

@ -351,7 +351,14 @@ pub struct StrSearcher<'a, 'b> {
needle: &'b str,
start: usize,
end: usize,
done: bool,
state: State,
}
#[derive(Clone, PartialEq)]
enum State { Done, NotDone, Reject(usize, usize) }
impl State {
#[inline] fn done(&self) -> bool { *self == State::Done }
#[inline] fn take(&mut self) -> State { ::mem::replace(self, State::NotDone) }
}
/// Non-allocating substring search.
@ -368,7 +375,7 @@ impl<'a, 'b> Pattern<'a> for &'b str {
needle: self,
start: 0,
end: haystack.len(),
done: false,
state: State::NotDone,
}
}
}
@ -385,8 +392,9 @@ unsafe impl<'a, 'b> Searcher<'a> for StrSearcher<'a, 'b> {
|m: &mut StrSearcher| {
// Forward step for empty needle
let current_start = m.start;
if !m.done {
if !m.state.done() {
m.start = m.haystack.char_range_at(current_start).next;
m.state = State::Reject(current_start, m.start);
}
SearchStep::Match(current_start, current_start)
},
@ -415,8 +423,9 @@ unsafe impl<'a, 'b> ReverseSearcher<'a> for StrSearcher<'a, 'b> {
|m: &mut StrSearcher| {
// Backward step for empty needle
let current_end = m.end;
if !m.done {
if !m.state.done() {
m.end = m.haystack.char_range_at_reverse(current_end).next;
m.state = State::Reject(m.end, current_end);
}
SearchStep::Match(current_end, current_end)
},
@ -446,23 +455,27 @@ fn str_search_step<F, G>(mut m: &mut StrSearcher,
where F: FnOnce(&mut StrSearcher) -> SearchStep,
G: FnOnce(&mut StrSearcher) -> SearchStep
{
if m.done {
if m.state.done() {
SearchStep::Done
} else if m.needle.len() == 0 && m.start <= m.end {
// Case for needle == ""
if m.start == m.end {
m.done = true;
if let State::Reject(a, b) = m.state.take() {
SearchStep::Reject(a, b)
} else {
if m.start == m.end {
m.state = State::Done;
}
empty_needle_step(&mut m)
}
empty_needle_step(&mut m)
} else if m.start + m.needle.len() <= m.end {
// Case for needle != ""
nonempty_needle_step(&mut m)
} else if m.start < m.end {
// Remaining slice shorter than needle, reject it
m.done = true;
m.state = State::Done;
SearchStep::Reject(m.start, m.end)
} else {
m.done = true;
m.state = State::Done;
SearchStep::Done
}
}