Rollup merge of #50010 - ExpHP:slice-bounds, r=alexcrichton

Give SliceIndex impls a test suite of girth befitting the implementation (and fix a UTF8 boundary check)

So one day I was writing something in my codebase that basically amounted to `impl SliceIndex for (Bound<usize>, Bound<usize>)`, and I said to myself:

*Boy, gee, golly!  I never realized bounds checking was so tricky!*

At some point when I had around 60 lines of tests for it, I decided to go see how the standard library does it to see if I missed any edge cases. ...That's when I discovered that libcore only had about 40 lines of tests for slicing altogether, and none of them even used `..=`.

---

This PR includes:

* **Literally the first appearance of the word `get_unchecked_mut` in any directory named `test` or `tests`.**
* Likewise the first appearance of `get_mut` used with _any type of range argument_ in these directories.
* Tests for the panics on overflow with `..=`.
    * I wanted to test on `[(); usize::MAX]` as well but that takes linear time in debug mode </3
* A horrible and ugly test-generating macro for the `should_panic` tests that increases the DRYness by a single order of magnitude (which IMO wasn't enough, but I didn't want to go any further and risk making the tests inaccessible to next guy).
* Same stuff for str!
    * Actually, the existing `str` tests were pretty good. I just helped filled in the holes.
* [A fix for the bug it caught](https://github.com/rust-lang/rust/issues/50002).  (only one ~~sadly~~)
This commit is contained in:
Alex Crichton 2018-05-10 11:35:17 -05:00 committed by GitHub
commit cff1a263c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 606 additions and 217 deletions

View File

@ -291,113 +291,378 @@ fn test_replace_pattern() {
assert_eq!(data.replace(|c| c == 'γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ");
}
#[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]);
// The current implementation of SliceIndex fails to handle methods
// orthogonally from range types; therefore, it is worth testing
// all of the indexing operations on each input.
mod slice_index {
// Test a slicing operation **that should succeed,**
// testing it on all of the indexing methods.
//
// This is not suitable for testing failure on invalid inputs.
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;
let data = "ประเทศไทย中华";
assert_eq!("", &data[0..3]);
assert_eq!("", &data[3..6]);
assert_eq!("", &data[3..3]);
assert_eq!("", &data[30..33]);
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;
fn a_million_letter_x() -> String {
let mut i = 0;
let mut rs = String::new();
while i < 100000 {
rs.push_str("华华华华华华华华华华");
i += 1;
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)",
);
}
}
}
rs
}
fn half_a_million_letter_x() -> String {
let mut i = 0;
let mut rs = String::new();
while i < 100000 {
rs.push_str("华华华华华");
i += 1;
// 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 that panics, as the macro is not designed
// to be used in `should_panic`)
#[test]
#[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 {
($(
in mod $case_name:ident {
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)
$(
good: data[$good:expr] == $output:expr;
)*
bad: data[$bad:expr];
message: $expect_msg:expr; // must be a literal
}
)*) => {$(
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_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();
while i < 100000 {
rs.push_str("华华华华华华华华华华");
i += 1;
}
rs
}
rs
fn half_a_million_letter_x() -> String {
let mut i = 0;
let mut rs = String::new();
while i < 100000 {
rs.push_str("华华华华华");
i += 1;
}
rs
}
let letters = a_million_letter_x();
assert_range_eq!(letters, 0..3 * 500000, half_a_million_letter_x());
}
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 */
}
#[test]
#[should_panic]
fn test_slice_fail() {
&"中华Việt Nam"[0..2];
}
#[test]
#[should_panic]
fn test_str_slice_rangetoinclusive_max_panics() {
&"hello"[..=usize::max_value()];
}
#[test]
#[should_panic]
fn test_str_slice_rangeinclusive_max_panics() {
&"hello"[1..=usize::max_value()];
}
#[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()];
}
#[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);
#[test]
#[should_panic]
fn test_slice_fail() {
&"中华Việt Nam"[0..2];
}
{
let s: &mut str = &mut s;
assert_eq!(s.get(..=usize::max_value()), None);
assert_eq!(s.get(1..=usize::max_value()), None);
panic_cases! {
in mod rangefrom_len {
data: "abcdef";
good: data[6..] == "";
bad: data[7..];
message: "out of bounds";
}
in mod rangeto_len {
data: "abcdef";
good: data[..6] == "abcdef";
bad: data[..7];
message: "out of bounds";
}
in mod rangetoinclusive_len {
data: "abcdef";
good: data[..=5] == "abcdef";
bad: data[..=6];
message: "out of bounds";
}
in mod range_len_len {
data: "abcdef";
good: data[6..6] == "";
bad: data[7..7];
message: "out of bounds";
}
in mod rangeinclusive_len_len {
data: "abcdef";
good: data[6..=5] == "";
bad: data[7..=6];
message: "out of bounds";
}
}
panic_cases! {
in mod range_neg_width {
data: "abcdef";
good: data[4..4] == "";
bad: data[4..3];
message: "begin <= end (4 <= 3)";
}
in mod rangeinclusive_neg_width {
data: "abcdef";
good: data[4..=3] == "";
bad: data[4..=2];
message: "begin <= end (4 <= 3)";
}
}
mod overflow {
panic_cases! {
in mod rangeinclusive {
data: "hello";
// 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.
bad: data[0..=usize::max_value()];
message: "maximum usize";
}
in mod rangetoinclusive {
data: "hello";
bad: data[..=usize::max_value()];
message: "maximum usize";
}
}
}
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! {
in mod range_1 {
data: super::DATA;
bad: data[super::BAD_START..super::GOOD_END];
message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
}
in mod range_2 {
data: super::DATA;
bad: data[super::GOOD_START..super::BAD_END];
message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
}
in mod rangefrom {
data: super::DATA;
bad: data[super::BAD_START..];
message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
}
in mod rangeto {
data: super::DATA;
bad: data[..super::BAD_END];
message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
}
in mod rangeinclusive_1 {
data: super::DATA;
bad: data[super::BAD_START..=super::GOOD_END_INCL];
message:
"byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of";
}
in mod rangeinclusive_2 {
data: super::DATA;
bad: data[super::GOOD_START..=super::BAD_END_INCL];
message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
}
in mod rangetoinclusive {
data: super::DATA;
bad: data[..=super::BAD_END_INCL];
message:
"byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of";
}
}
}
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.";
// check the panic includes the prefix of the sliced string
#[test]
#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")]
fn test_slice_fail_truncated_1() {
&LOREM_PARAGRAPH[..1024];
}
// check the truncation in the panic message
#[test]
#[should_panic(expected="luctus, im`[...]")]
fn test_slice_fail_truncated_2() {
&LOREM_PARAGRAPH[..1024];
}
}
@ -446,50 +711,6 @@ fn test_is_char_boundary() {
}
}
}
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.";
// check the panic includes the prefix of the sliced string
#[test]
#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")]
fn test_slice_fail_truncated_1() {
&LOREM_PARAGRAPH[..1024];
}
// check the truncation in the panic message
#[test]
#[should_panic(expected="luctus, im`[...]")]
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]
fn test_trim_left_matches() {

View File

@ -2262,6 +2262,12 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! {
panic!("slice index starts at {} but ends at {}", index, end);
}
#[inline(never)]
#[cold]
fn slice_index_overflow_fail() -> ! {
panic!("attempted to index slice up to maximum usize");
}
/// A helper trait used for indexing operations.
#[unstable(feature = "slice_get_slice", issue = "35729")]
#[rustc_on_unimplemented = "slice indices are of type `usize` or ranges of `usize`"]
@ -2538,15 +2544,13 @@ impl<T> SliceIndex<[T]> for ops::RangeInclusive<usize> {
#[inline]
fn index(self, slice: &[T]) -> &[T] {
assert!(self.end != usize::max_value(),
"attempted to index slice up to maximum usize");
if self.end == usize::max_value() { slice_index_overflow_fail(); }
(self.start..self.end + 1).index(slice)
}
#[inline]
fn index_mut(self, slice: &mut [T]) -> &mut [T] {
assert!(self.end != usize::max_value(),
"attempted to index slice up to maximum usize");
if self.end == usize::max_value() { slice_index_overflow_fail(); }
(self.start..self.end + 1).index_mut(slice)
}
}

View File

@ -1849,6 +1849,12 @@ mod traits {
}
}
#[inline(never)]
#[cold]
fn str_index_overflow_fail() -> ! {
panic!("attempted to index str up to maximum usize");
}
#[stable(feature = "str_checked_slicing", since = "1.20.0")]
impl SliceIndex<str> for ops::RangeFull {
type Output = str;
@ -2029,19 +2035,13 @@ mod traits {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
if let Some(end) = self.end.checked_add(1) {
(self.start..end).get(slice)
} else {
None
}
if self.end == usize::max_value() { None }
else { (self.start..self.end+1).get(slice) }
}
#[inline]
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
if let Some(end) = self.end.checked_add(1) {
(self.start..end).get_mut(slice)
} else {
None
}
if self.end == usize::max_value() { None }
else { (self.start..self.end+1).get_mut(slice) }
}
#[inline]
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
@ -2053,14 +2053,12 @@ mod traits {
}
#[inline]
fn index(self, slice: &str) -> &Self::Output {
assert!(self.end != usize::max_value(),
"attempted to index str up to maximum usize");
if self.end == usize::max_value() { str_index_overflow_fail(); }
(self.start..self.end+1).index(slice)
}
#[inline]
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
assert!(self.end != usize::max_value(),
"attempted to index str up to maximum usize");
if self.end == usize::max_value() { str_index_overflow_fail(); }
(self.start..self.end+1).index_mut(slice)
}
}
@ -2072,40 +2070,30 @@ mod traits {
type Output = str;
#[inline]
fn get(self, slice: &str) -> Option<&Self::Output> {
if self.end < usize::max_value() && slice.is_char_boundary(self.end + 1) {
Some(unsafe { self.get_unchecked(slice) })
} else {
None
}
if self.end == usize::max_value() { None }
else { (..self.end+1).get(slice) }
}
#[inline]
fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> {
if self.end < usize::max_value() && slice.is_char_boundary(self.end + 1) {
Some(unsafe { self.get_unchecked_mut(slice) })
} else {
None
}
if self.end == usize::max_value() { None }
else { (..self.end+1).get_mut(slice) }
}
#[inline]
unsafe fn get_unchecked(self, slice: &str) -> &Self::Output {
let ptr = slice.as_ptr();
super::from_utf8_unchecked(slice::from_raw_parts(ptr, self.end + 1))
(..self.end+1).get_unchecked(slice)
}
#[inline]
unsafe fn get_unchecked_mut(self, slice: &mut str) -> &mut Self::Output {
let ptr = slice.as_ptr();
super::from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr as *mut u8, self.end + 1))
(..self.end+1).get_unchecked_mut(slice)
}
#[inline]
fn index(self, slice: &str) -> &Self::Output {
assert!(self.end != usize::max_value(),
"attempted to index str up to maximum usize");
if self.end == usize::max_value() { str_index_overflow_fail(); }
(..self.end+1).index(slice)
}
#[inline]
fn index_mut(self, slice: &mut str) -> &mut Self::Output {
assert!(self.end != usize::max_value(),
"attempted to index str up to maximum usize");
if self.end == usize::max_value() { str_index_overflow_fail(); }
(..self.end+1).index_mut(slice)
}
}

View File

@ -376,48 +376,224 @@ 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);
}
// The current implementation of SliceIndex fails to handle methods
// orthogonally from range types; therefore, it is worth testing
// all of the indexing operations on each input.
mod slice_index {
// This checks all six indexing methods, given an input range that
// should succeed. (it is NOT suitable for testing invalid inputs)
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 that panics, 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 {
($(
// each test case needs a unique name to namespace the tests
in mod $case_name:ident {
data: $data:expr;
// optional:
//
// one or more similar inputs 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)
$(
good: data[$good:expr] == $output:expr;
)*
bad: data[$bad:expr];
message: $expect_msg:expr;
}
)*) => {$(
mod $case_name {
#[test]
fn pass() {
let mut v = $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;
let v: &[_] = &v;
let _v = &v[$bad];
}
#[test]
#[should_panic(expected = $expect_msg)]
fn index_mut_fail() {
let mut v = $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! {
in mod rangefrom_len {
data: [0, 1, 2, 3, 4, 5];
good: data[6..] == [];
bad: data[7..];
message: "but ends at"; // perhaps not ideal
}
in mod rangeto_len {
data: [0, 1, 2, 3, 4, 5];
good: data[..6] == [0, 1, 2, 3, 4, 5];
bad: data[..7];
message: "out of range";
}
in mod rangetoinclusive_len {
data: [0, 1, 2, 3, 4, 5];
good: data[..=5] == [0, 1, 2, 3, 4, 5];
bad: data[..=6];
message: "out of range";
}
in mod range_len_len {
data: [0, 1, 2, 3, 4, 5];
good: data[6..6] == [];
bad: data[7..7];
message: "out of range";
}
in mod rangeinclusive_len_len {
data: [0, 1, 2, 3, 4, 5];
good: data[6..=5] == [];
bad: data[7..=6];
message: "out of range";
}
}
panic_cases! {
in mod range_neg_width {
data: [0, 1, 2, 3, 4, 5];
good: data[4..4] == [];
bad: data[4..3];
message: "but ends at";
}
in mod rangeinclusive_neg_width {
data: [0, 1, 2, 3, 4, 5];
good: data[4..=3] == [];
bad: data[4..=2];
message: "but ends at";
}
}
panic_cases! {
in mod rangeinclusive_overflow {
data: [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.
bad: data[0 ..= ::std::usize::MAX];
message: "maximum usize";
}
in mod rangetoinclusive_overflow {
data: [0, 1];
bad: data[..= ::std::usize::MAX];
message: "maximum usize";
}
} // panic_cases!
}
#[test]

View File

@ -8,4 +8,4 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// All `str` tests live in collectionstests::str
// All `str` tests live in liballoc/tests