Skip to content

Commit

Permalink
Added smoke tests for new methods.
Browse files Browse the repository at this point in the history
Fixed bug in existing StrSearcher impl
  • Loading branch information
Kimundi committed Apr 5, 2015
1 parent c29559d commit fbba28e
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 22 deletions.
149 changes: 137 additions & 12 deletions src/libcollectionstest/str.rs
Expand Up @@ -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);
}

Expand All @@ -1731,25 +1756,35 @@ 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),
Reject(3, 6),
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),
Expand Down Expand Up @@ -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};

Expand Down
33 changes: 23 additions & 10 deletions src/libcore/str/pattern.rs
Expand Up @@ -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.
Expand All @@ -368,7 +375,7 @@ impl<'a, 'b> Pattern<'a> for &'b str {
needle: self,
start: 0,
end: haystack.len(),
done: false,
state: State::NotDone,
}
}
}
Expand All @@ -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)
},
Expand Down Expand Up @@ -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)
},
Expand Down Expand Up @@ -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
}
}
Expand Down

0 comments on commit fbba28e

Please sign in to comment.