flesh out tests for SliceIndex

m*n lines of implementation deserves m*n lines of tests
This commit is contained in:
Michael Lamparski 2018-04-30 07:37:08 -04:00
parent 0842dc6723
commit ce66f5d918
2 changed files with 624 additions and 139 deletions

View File

@ -292,19 +292,194 @@ fn test_replace_pattern() {
}
mod slice_index {
// Test a slicing operation **that should succeed,**
// testing it on all of the indexing methods.
//
// DO NOT use this in `should_panic` tests, unless you are testing the macro itself.
macro_rules! assert_range_eq {
($s:expr, $range:expr, $expected:expr)
=> {
let mut s: String = $s.to_owned();
let mut expected: String = $expected.to_owned();
{
let s: &str = &s;
let expected: &str = &expected;
assert_eq!(&s[$range], expected, "(in assertion for: index)");
assert_eq!(s.get($range), Some(expected), "(in assertion for: get)");
unsafe {
assert_eq!(
s.get_unchecked($range), expected,
"(in assertion for: get_unchecked)",
);
}
}
{
let s: &mut str = &mut s;
let expected: &mut str = &mut expected;
assert_eq!(
&mut s[$range], expected,
"(in assertion for: index_mut)",
);
assert_eq!(
s.get_mut($range), Some(&mut expected[..]),
"(in assertion for: get_mut)",
);
unsafe {
assert_eq!(
s.get_unchecked_mut($range), expected,
"(in assertion for: get_unchecked_mut)",
);
}
}
}
}
// Make sure the macro can actually detect bugs,
// because if it can't, then what are we even doing here?
//
// (Be aware this only demonstrates the ability to detect bugs
// in the FIRST method it calls, as the macro is not designed
// to be used in `should_panic`)
#[test]
fn test_slice() {
assert_eq!("ab", &"abc"[0..2]);
assert_eq!("bc", &"abc"[1..3]);
assert_eq!("", &"abc"[1..1]);
assert_eq!("\u{65e5}", &"\u{65e5}\u{672c}"[0..3]);
#[should_panic(expected = "out of bounds")]
fn assert_range_eq_can_fail_by_panic() {
assert_range_eq!("abc", 0..5, "abc");
}
// (Be aware this only demonstrates the ability to detect bugs
// in the FIRST method it calls, as the macro is not designed
// to be used in `should_panic`)
#[test]
#[should_panic(expected = "==")]
fn assert_range_eq_can_fail_by_inequality() {
assert_range_eq!("abc", 0..2, "abc");
}
// Generates test cases for bad index operations.
//
// This generates `should_panic` test cases for Index/IndexMut
// and `None` test cases for get/get_mut.
macro_rules! panic_cases {
($(
mod $case_name:ident {
let DATA = $data:expr;
// optional:
//
// a similar input for which DATA[input] succeeds, and the corresponding
// output str. This helps validate "critical points" where an input range
// straddles the boundary between valid and invalid.
// (such as the input `len..len`, which is just barely valid)
$(
let GOOD_INPUT = $good:expr;
let GOOD_OUTPUT = $output:expr;
)*
let BAD_INPUT = $bad:expr;
const EXPECT_MSG = $expect_msg:expr; // must be a literal
!!generate_tests!!
}
)*) => {$(
mod $case_name {
#[test]
fn pass() {
let mut v: String = $data.into();
$( assert_range_eq!(v, $good, $output); )*
{
let v: &str = &v;
assert_eq!(v.get($bad), None, "(in None assertion for get)");
}
{
let v: &mut str = &mut v;
assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)");
}
}
#[test]
#[should_panic(expected = $expect_msg)]
fn index_fail() {
let v: String = $data.into();
let v: &str = &v;
let _v = &v[$bad];
}
#[test]
#[should_panic(expected = $expect_msg)]
fn index_mut_fail() {
let mut v: String = $data.into();
let v: &mut str = &mut v;
let _v = &mut v[$bad];
}
}
)*};
}
#[test]
fn simple_ascii() {
assert_range_eq!("abc", .., "abc");
assert_range_eq!("abc", 0..2, "ab");
assert_range_eq!("abc", 0..=1, "ab");
assert_range_eq!("abc", ..2, "ab");
assert_range_eq!("abc", ..=1, "ab");
assert_range_eq!("abc", 1..3, "bc");
assert_range_eq!("abc", 1..=2, "bc");
assert_range_eq!("abc", 1..1, "");
assert_range_eq!("abc", 1..=0, "");
}
#[test]
fn simple_unicode() {
// 日本
assert_range_eq!("\u{65e5}\u{672c}", .., "\u{65e5}\u{672c}");
assert_range_eq!("\u{65e5}\u{672c}", 0..3, "\u{65e5}");
assert_range_eq!("\u{65e5}\u{672c}", 0..=2, "\u{65e5}");
assert_range_eq!("\u{65e5}\u{672c}", ..3, "\u{65e5}");
assert_range_eq!("\u{65e5}\u{672c}", ..=2, "\u{65e5}");
assert_range_eq!("\u{65e5}\u{672c}", 3..6, "\u{672c}");
assert_range_eq!("\u{65e5}\u{672c}", 3..=5, "\u{672c}");
assert_range_eq!("\u{65e5}\u{672c}", 3.., "\u{672c}");
let data = "ประเทศไทย中华";
assert_eq!("", &data[0..3]);
assert_eq!("", &data[3..6]);
assert_eq!("", &data[3..3]);
assert_eq!("", &data[30..33]);
assert_range_eq!(data, 0..3, "");
assert_range_eq!(data, 3..6, "");
assert_range_eq!(data, 3..3, "");
assert_range_eq!(data, 30..33, "");
/*0: 中
3:
6: V
7: i
8:
11: t
12:
13: N
14: a
15: m */
let ss = "中华Việt Nam";
assert_range_eq!(ss, 3..6, "");
assert_range_eq!(ss, 6..16, "Việt Nam");
assert_range_eq!(ss, 6..=15, "Việt Nam");
assert_range_eq!(ss, 6.., "Việt Nam");
assert_range_eq!(ss, 0..3, "");
assert_range_eq!(ss, 3..7, "华V");
assert_range_eq!(ss, 3..=6, "华V");
assert_range_eq!(ss, 3..3, "");
assert_range_eq!(ss, 3..=2, "");
}
#[test]
fn simple_big() {
fn a_million_letter_x() -> String {
let mut i = 0;
let mut rs = String::new();
@ -324,33 +499,7 @@ mod slice_index {
rs
}
let letters = a_million_letter_x();
assert_eq!(half_a_million_letter_x(), &letters[0..3 * 500000]);
}
#[test]
fn test_slice_2() {
let ss = "中华Việt Nam";
assert_eq!("", &ss[3..6]);
assert_eq!("Việt Nam", &ss[6..16]);
assert_eq!("ab", &"abc"[0..2]);
assert_eq!("bc", &"abc"[1..3]);
assert_eq!("", &"abc"[1..1]);
assert_eq!("", &ss[0..3]);
assert_eq!("华V", &ss[3..7]);
assert_eq!("", &ss[3..3]);
/*0: 中
3:
6: V
7: i
8:
11: t
12:
13: N
14: a
15: m */
assert_range_eq!(letters, 0..3 * 500000, half_a_million_letter_x());
}
#[test]
@ -359,55 +508,210 @@ mod slice_index {
&"中华Việt Nam"[0..2];
}
#[test]
#[should_panic]
fn test_str_slice_rangetoinclusive_max_panics() {
&"hello"[..=usize::max_value()];
}
panic_cases! {
mod rangefrom_len {
let DATA = "abcdef";
#[test]
#[should_panic]
fn test_str_slice_rangeinclusive_max_panics() {
&"hello"[1..=usize::max_value()];
}
let GOOD_INPUT = 6..;
let GOOD_OUTPUT = "";
#[test]
#[should_panic]
fn test_str_slicemut_rangetoinclusive_max_panics() {
let mut s = "hello".to_owned();
let s: &mut str = &mut s;
&mut s[..=usize::max_value()];
}
let BAD_INPUT = 7..;
const EXPECT_MSG = "out of bounds";
#[test]
#[should_panic]
fn test_str_slicemut_rangeinclusive_max_panics() {
let mut s = "hello".to_owned();
let s: &mut str = &mut s;
&mut s[1..=usize::max_value()];
}
#[test]
fn test_str_get_maxinclusive() {
let mut s = "hello".to_owned();
{
let s: &str = &s;
assert_eq!(s.get(..=usize::max_value()), None);
assert_eq!(s.get(1..=usize::max_value()), None);
!!generate_tests!!
}
{
let s: &mut str = &mut s;
assert_eq!(s.get(..=usize::max_value()), None);
assert_eq!(s.get(1..=usize::max_value()), None);
mod rangeto_len {
let DATA = "abcdef";
let GOOD_INPUT = ..6;
let GOOD_OUTPUT = "abcdef";
let BAD_INPUT = ..7;
const EXPECT_MSG = "out of bounds";
!!generate_tests!!
}
mod rangetoinclusive_len {
let DATA = "abcdef";
let GOOD_INPUT = ..=5;
let GOOD_OUTPUT = "abcdef";
let BAD_INPUT = ..=6;
const EXPECT_MSG = "out of bounds";
!!generate_tests!!
}
mod range_len_len {
let DATA = "abcdef";
let GOOD_INPUT = 6..6;
let GOOD_OUTPUT = "";
let BAD_INPUT = 7..7;
const EXPECT_MSG = "out of bounds";
!!generate_tests!!
}
mod rangeinclusive_len_len {
let DATA = "abcdef";
let GOOD_INPUT = 6..=5;
let GOOD_OUTPUT = "";
let BAD_INPUT = 7..=6;
const EXPECT_MSG = "out of bounds";
!!generate_tests!!
}
}
panic_cases! {
mod range_neg_width {
let DATA = "abcdef";
let GOOD_INPUT = 4..4;
let GOOD_OUTPUT = "";
let BAD_INPUT = 4..3;
const EXPECT_MSG = "begin <= end (4 <= 3)";
!!generate_tests!!
}
mod rangeinclusive_neg_width {
let DATA = "abcdef";
let GOOD_INPUT = 4..=3;
let GOOD_OUTPUT = "";
let BAD_INPUT = 4..=2;
const EXPECT_MSG = "begin <= end (4 <= 3)";
!!generate_tests!!
}
}
mod overflow {
panic_cases! {
mod rangeinclusive {
let DATA = "hello";
let BAD_INPUT = 1..=usize::max_value();
const EXPECT_MSG = "maximum usize";
!!generate_tests!!
}
mod rangetoinclusive {
let DATA = "hello";
let BAD_INPUT = ..=usize::max_value();
const EXPECT_MSG = "maximum usize";
!!generate_tests!!
}
}
}
mod boundary {
const DATA: &'static str = "abcαβγ";
const BAD_START: usize = 4;
const GOOD_START: usize = 3;
const BAD_END: usize = 6;
const GOOD_END: usize = 7;
const BAD_END_INCL: usize = BAD_END - 1;
const GOOD_END_INCL: usize = GOOD_END - 1;
// it is especially important to test all of the different range types here
// because some of the logic may be duplicated as part of micro-optimizations
// to dodge unicode boundary checks on half-ranges.
panic_cases! {
mod range_1 {
let DATA = super::DATA;
let BAD_INPUT = super::BAD_START..super::GOOD_END;
const EXPECT_MSG =
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
!!generate_tests!!
}
mod range_2 {
let DATA = super::DATA;
let BAD_INPUT = super::GOOD_START..super::BAD_END;
const EXPECT_MSG =
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
!!generate_tests!!
}
mod rangefrom {
let DATA = super::DATA;
let BAD_INPUT = super::BAD_START..;
const EXPECT_MSG =
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
!!generate_tests!!
}
mod rangeto {
let DATA = super::DATA;
let BAD_INPUT = ..super::BAD_END;
const EXPECT_MSG =
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
!!generate_tests!!
}
mod rangeinclusive_1 {
let DATA = super::DATA;
let BAD_INPUT = super::BAD_START..=super::GOOD_END_INCL;
const EXPECT_MSG =
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
!!generate_tests!!
}
mod rangeinclusive_2 {
let DATA = super::DATA;
let BAD_INPUT = super::GOOD_START..=super::BAD_END_INCL;
const EXPECT_MSG =
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
!!generate_tests!!
}
mod rangetoinclusive {
let DATA = super::DATA;
let BAD_INPUT = ..=super::BAD_END_INCL;
const EXPECT_MSG =
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
!!generate_tests!!
}
}
}
const LOREM_PARAGRAPH: &'static str = "\
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \
ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \
eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \
sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \
tempus vel, gravida nec quam.";
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem \
sit amet dolor ultricies condimentum. Praesent iaculis purus elit, ac malesuada \
quam malesuada in. Duis sed orci eros. Suspendisse sit amet magna mollis, mollis \
nunc luctus, imperdiet mi. Integer fringilla non sem ut lacinia. Fusce varius \
tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec tempus vel, \
gravida nec quam.";
// check the panic includes the prefix of the sliced string
#[test]
@ -421,31 +725,6 @@ mod slice_index {
fn test_slice_fail_truncated_2() {
&LOREM_PARAGRAPH[..1024];
}
#[test]
#[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")]
fn test_slice_fail_boundary_1() {
&"abcαβγ"[4..];
}
#[test]
#[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")]
fn test_slice_fail_boundary_2() {
&"abcαβγ"[2..6];
}
#[test]
fn test_slice_from() {
assert_eq!(&"abcd"[0..], "abcd");
assert_eq!(&"abcd"[2..], "cd");
assert_eq!(&"abcd"[4..], "");
}
#[test]
fn test_slice_to() {
assert_eq!(&"abcd"[..0], "");
assert_eq!(&"abcd"[..2], "ab");
assert_eq!(&"abcd"[..4], "abcd");
}
}
#[test]

View File

@ -376,48 +376,254 @@ fn test_windows_zip() {
assert_eq!(res, [14, 18, 22, 26]);
}
#[test]
fn get_range() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
assert_eq!(v.get(..), Some(&[0, 1, 2, 3, 4, 5][..]));
assert_eq!(v.get(..2), Some(&[0, 1][..]));
assert_eq!(v.get(2..), Some(&[2, 3, 4, 5][..]));
assert_eq!(v.get(1..4), Some(&[1, 2, 3][..]));
assert_eq!(v.get(7..), None);
assert_eq!(v.get(7..10), None);
}
mod slice_index {
// Test a slicing operation that should succeed,
// testing it on all of the indexing methods.
macro_rules! assert_range_eq {
($arr:expr, $range:expr, $expected:expr)
=> {
let mut arr = $arr;
let mut expected = $expected;
{
let s: &[_] = &arr;
let expected: &[_] = &expected;
#[test]
fn get_mut_range() {
let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
assert_eq!(v.get_mut(..), Some(&mut [0, 1, 2, 3, 4, 5][..]));
assert_eq!(v.get_mut(..2), Some(&mut [0, 1][..]));
assert_eq!(v.get_mut(2..), Some(&mut [2, 3, 4, 5][..]));
assert_eq!(v.get_mut(1..4), Some(&mut [1, 2, 3][..]));
assert_eq!(v.get_mut(7..), None);
assert_eq!(v.get_mut(7..10), None);
}
assert_eq!(&s[$range], expected, "(in assertion for: index)");
assert_eq!(s.get($range), Some(expected), "(in assertion for: get)");
unsafe {
assert_eq!(
s.get_unchecked($range), expected,
"(in assertion for: get_unchecked)",
);
}
}
{
let s: &mut [_] = &mut arr;
let expected: &mut [_] = &mut expected;
#[test]
fn get_unchecked_range() {
unsafe {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
assert_eq!(v.get_unchecked(..), &[0, 1, 2, 3, 4, 5][..]);
assert_eq!(v.get_unchecked(..2), &[0, 1][..]);
assert_eq!(v.get_unchecked(2..), &[2, 3, 4, 5][..]);
assert_eq!(v.get_unchecked(1..4), &[1, 2, 3][..]);
assert_eq!(
&mut s[$range], expected,
"(in assertion for: index_mut)",
);
assert_eq!(
s.get_mut($range), Some(&mut expected[..]),
"(in assertion for: get_mut)",
);
unsafe {
assert_eq!(
s.get_unchecked_mut($range), expected,
"(in assertion for: get_unchecked_mut)",
);
}
}
}
}
}
#[test]
fn get_unchecked_mut_range() {
unsafe {
let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
assert_eq!(v.get_unchecked_mut(..), &mut [0, 1, 2, 3, 4, 5][..]);
assert_eq!(v.get_unchecked_mut(..2), &mut [0, 1][..]);
assert_eq!(v.get_unchecked_mut(2..), &mut[2, 3, 4, 5][..]);
assert_eq!(v.get_unchecked_mut(1..4), &mut [1, 2, 3][..]);
// Make sure the macro can actually detect bugs,
// because if it can't, then what are we even doing here?
//
// (Be aware this only demonstrates the ability to detect bugs
// in the FIRST method it calls, as the macro is not designed
// to be used in `should_panic`)
#[test]
#[should_panic(expected = "out of range")]
fn assert_range_eq_can_fail_by_panic() {
assert_range_eq!([0, 1, 2], 0..5, [0, 1, 2]);
}
// (Be aware this only demonstrates the ability to detect bugs
// in the FIRST method it calls, as the macro is not designed
// to be used in `should_panic`)
#[test]
#[should_panic(expected = "==")]
fn assert_range_eq_can_fail_by_inequality() {
assert_range_eq!([0, 1, 2], 0..2, [0, 1, 2]);
}
// Test cases for bad index operations.
//
// This generates `should_panic` test cases for Index/IndexMut
// and `None` test cases for get/get_mut.
macro_rules! panic_cases {
($(
mod $case_name:ident {
let DATA: $Data: ty = $data:expr;
// optional:
//
// a similar input for which DATA[input] succeeds, and the corresponding
// output as an array. This helps validate "critical points" where an
// input range straddles the boundary between valid and invalid.
// (such as the input `len..len`, which is just barely valid)
$(
let GOOD_INPUT = $good:expr;
let GOOD_OUTPUT = $output:expr;
)*
let BAD_INPUT = $bad:expr;
const EXPECT_MSG = $expect_msg:expr;
!!generate_tests!!
}
)*) => {$(
mod $case_name {
#[test]
fn pass() {
let mut v: $Data = $data;
$( assert_range_eq!($data, $good, $output); )*
{
let v: &[_] = &v;
assert_eq!(v.get($bad), None, "(in None assertion for get)");
}
{
let v: &mut [_] = &mut v;
assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)");
}
}
#[test]
#[should_panic(expected = $expect_msg)]
fn index_fail() {
let v: $Data = $data;
let v: &[_] = &v;
let _v = &v[$bad];
}
#[test]
#[should_panic(expected = $expect_msg)]
fn index_mut_fail() {
let mut v: $Data = $data;
let v: &mut [_] = &mut v;
let _v = &mut v[$bad];
}
}
)*};
}
#[test]
fn simple() {
let v = [0, 1, 2, 3, 4, 5];
assert_range_eq!(v, .., [0, 1, 2, 3, 4, 5]);
assert_range_eq!(v, ..2, [0, 1]);
assert_range_eq!(v, ..=1, [0, 1]);
assert_range_eq!(v, 2.., [2, 3, 4, 5]);
assert_range_eq!(v, 1..4, [1, 2, 3]);
assert_range_eq!(v, 1..=3, [1, 2, 3]);
}
panic_cases! {
mod rangefrom_len {
let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5];
let GOOD_INPUT = 6..;
let GOOD_OUTPUT = [];
let BAD_INPUT = 7..;
const EXPECT_MSG = "but ends at"; // perhaps not ideal
!!generate_tests!!
}
mod rangeto_len {
let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5];
let GOOD_INPUT = ..6;
let GOOD_OUTPUT = [0, 1, 2, 3, 4, 5];
let BAD_INPUT = ..7;
const EXPECT_MSG = "out of range";
!!generate_tests!!
}
mod rangetoinclusive_len {
let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5];
let GOOD_INPUT = ..=5;
let GOOD_OUTPUT = [0, 1, 2, 3, 4, 5];
let BAD_INPUT = ..=6;
const EXPECT_MSG = "out of range";
!!generate_tests!!
}
mod range_len_len {
let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5];
let GOOD_INPUT = 6..6;
let GOOD_OUTPUT = [];
let BAD_INPUT = 7..7;
const EXPECT_MSG = "out of range";
!!generate_tests!!
}
mod rangeinclusive_len_len{
let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5];
let GOOD_INPUT = 6..=5;
let GOOD_OUTPUT = [];
let BAD_INPUT = 7..=6;
const EXPECT_MSG = "out of range";
!!generate_tests!!
}
}
panic_cases! {
mod range_neg_width {
let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5];
let GOOD_INPUT = 4..4;
let GOOD_OUTPUT = [];
let BAD_INPUT = 4..3;
const EXPECT_MSG = "but ends at";
!!generate_tests!!
}
mod rangeinclusive_neg_width {
let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5];
let GOOD_INPUT = 4..=3;
let GOOD_OUTPUT = [];
let BAD_INPUT = 4..=2;
const EXPECT_MSG = "but ends at";
!!generate_tests!!
}
}
panic_cases! {
mod rangeinclusive_overflow {
let DATA: [i32; 2] = [0, 1];
// note: using 0 specifically ensures that the result of overflowing is 0..0,
// so that `get` doesn't simply return None for the wrong reason.
let BAD_INPUT = 0 ..= ::std::usize::MAX;
const EXPECT_MSG = "maximum usize";
!!generate_tests!!
}
mod rangetoinclusive_overflow {
let DATA: [i32; 2] = [0, 1];
let BAD_INPUT = ..= ::std::usize::MAX;
const EXPECT_MSG = "maximum usize";
!!generate_tests!!
}
} // panic_cases!
}
#[test]