diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2018-05-10 11:35:17 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-05-10 11:35:17 -0500 |
| commit | cff1a263c9e7744df286d2518e0c6ca3191dc681 (patch) | |
| tree | 2d39f779a1fba6ca894db351eac6242edae37f92 /src/libcore/tests | |
| parent | ecd9898b60b601f69113c64b77650a09d7678edf (diff) | |
| parent | f1d7b453fed6acefc68f90752922b37c6e3ac7a4 (diff) | |
| download | rust-cff1a263c9e7744df286d2518e0c6ca3191dc681.tar.gz rust-cff1a263c9e7744df286d2518e0c6ca3191dc681.zip | |
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~~)
Diffstat (limited to 'src/libcore/tests')
| -rw-r--r-- | src/libcore/tests/slice.rs | 250 | ||||
| -rw-r--r-- | src/libcore/tests/str.rs | 2 |
2 files changed, 214 insertions, 38 deletions
diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index c81e5e97cbb..19b5c86c474 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -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; + + 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; + + 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_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); -} + // 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]); + } -#[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][..]); + // (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] -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][..]); + // 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] diff --git a/src/libcore/tests/str.rs b/src/libcore/tests/str.rs index 08daafccc54..343c9596c53 100644 --- a/src/libcore/tests/str.rs +++ b/src/libcore/tests/str.rs @@ -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 |
