From 5ab4c811caba9cfac1488919efae6c6675e68e4c Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:36:27 -0400 Subject: [PATCH 1/8] str/slice: factor out overflow error messages --- src/libcore/slice/mod.rs | 12 ++++++++---- src/libcore/str/mod.rs | 18 ++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 83e8a6e4b683a..93ebc23ac0b0e 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -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 SliceIndex<[T]> for ops::RangeInclusive { #[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) } } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index b39d9feb35b7e..f7555700ebc20 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -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 for ops::RangeFull { type Output = str; @@ -2053,14 +2059,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) } } @@ -2098,14 +2102,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.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) } } From 6b749b011350830f47a93f1efce986b3c02c4342 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:36:46 -0400 Subject: [PATCH 2/8] Clean up the other Slice*Inclusive impls for str A previous PR fixed one method that was legitimately buggy; this cleans up the rest to be less diverse, mirroring the corresponding impls on [T] to the greatest extent possible without introducing any unnecessary UTF-8 boundary checks at 0. --- src/libcore/str/mod.rs | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index f7555700ebc20..df7b2f25a86df 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -2035,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 { @@ -2076,29 +2070,21 @@ 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 { From 4fab1674c3db2cf4b2d1546a7120d8d56a85c355 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:36:56 -0400 Subject: [PATCH 3/8] update libcore's comment about str tests --- src/libcore/tests/str.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/tests/str.rs b/src/libcore/tests/str.rs index 08daafccc5404..343c9596c5383 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 From 0842dc67238969e39b0a08d2c4314ceefd19caa2 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:02 -0400 Subject: [PATCH 4/8] collect str SliceIndex tests into a mod GitHub users: I think you can add ?w=1 to the url for a vastly cleaner whitespace-ignoring diff --- src/liballoc/tests/str.rs | 277 +++++++++++++++++++------------------- 1 file changed, 140 insertions(+), 137 deletions(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index a03b61ec97e51..c9536cbe16855 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -291,113 +291,160 @@ 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]); - - let data = "ประเทศไทย中华"; - assert_eq!("ป", &data[0..3]); - assert_eq!("ร", &data[3..6]); - assert_eq!("", &data[3..3]); - assert_eq!("华", &data[30..33]); - - fn a_million_letter_x() -> String { - let mut i = 0; - let mut rs = String::new(); - while i < 100000 { - rs.push_str("华华华华华华华华华华"); - i += 1; +mod slice_index { + #[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]); + + let data = "ประเทศไทย中华"; + assert_eq!("ป", &data[0..3]); + assert_eq!("ร", &data[3..6]); + assert_eq!("", &data[3..3]); + assert_eq!("华", &data[30..33]); + + 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; + 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 } - rs + let letters = a_million_letter_x(); + assert_eq!(half_a_million_letter_x(), &letters[0..3 * 500000]); } - 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"; + #[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_eq!("华", &ss[3..6]); - assert_eq!("Việt Nam", &ss[6..16]); + #[test] + #[should_panic] + fn test_slice_fail() { + &"中华Việt Nam"[0..2]; + } - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); + #[test] + #[should_panic] + fn test_str_slice_rangetoinclusive_max_panics() { + &"hello"[..=usize::max_value()]; + } - 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_str_slice_rangeinclusive_max_panics() { + &"hello"[1..=usize::max_value()]; + } -#[test] -#[should_panic] -fn test_slice_fail() { - &"中华Việt Nam"[0..2]; -} + #[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_slice_rangetoinclusive_max_panics() { - &"hello"[..=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] -#[should_panic] -fn test_str_slice_rangeinclusive_max_panics() { - &"hello"[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); + } + { + let s: &mut str = &mut s; + assert_eq!(s.get(..=usize::max_value()), None); + assert_eq!(s.get(1..=usize::max_value()), None); + } + } -#[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()]; -} + 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] -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] + #[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] -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(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]; } - { - let s: &mut str = &mut s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + + #[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"); } } @@ -446,50 +493,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() { From ce66f5d9185aa2b81159fa61597bbb6e4cf2847f Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:08 -0400 Subject: [PATCH 5/8] flesh out tests for SliceIndex m*n lines of implementation deserves m*n lines of tests --- src/liballoc/tests/str.rs | 477 +++++++++++++++++++++++++++++-------- src/libcore/tests/slice.rs | 282 +++++++++++++++++++--- 2 files changed, 622 insertions(+), 137 deletions(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index c9536cbe16855..bfba9a6b39355 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -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] + #[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 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]); + 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"; + + !!generate_tests!! + } + + 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!! + } } - #[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()]; + 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!! + } } - #[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); + 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!! + } } - { - let s: &mut str = &mut s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + } + + 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] diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 53fdfa0682742..5272c7427d91d 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -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; + + 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); -} - -#[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][..]); + // 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]); } -} -#[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][..]); + // (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] From 02b3da1200df47ea7343dd2cd960b8afe983ac9c Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:19 -0400 Subject: [PATCH 6/8] decrease false negatives for str overflow test --- src/liballoc/tests/str.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index bfba9a6b39355..696ce79f36920 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -602,7 +602,9 @@ mod slice_index { mod rangeinclusive { let DATA = "hello"; - let BAD_INPUT = 1..=usize::max_value(); + // 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..=usize::max_value(); const EXPECT_MSG = "maximum usize"; !!generate_tests!! From 030aa9b112b5b52207186b375d9fc09607bbed48 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:29 -0400 Subject: [PATCH 7/8] revise macro in slice tests --- src/libcore/tests/slice.rs | 154 +++++++++++++++---------------------- 1 file changed, 62 insertions(+), 92 deletions(-) diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 5272c7427d91d..d2bda65de5562 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -376,9 +376,12 @@ fn test_windows_zip() { assert_eq!(res, [14, 18, 22, 26]); } +// 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 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) => { @@ -423,7 +426,7 @@ mod slice_index { // 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 + // 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")] @@ -446,30 +449,29 @@ mod slice_index { // and `None` test cases for get/get_mut. macro_rules! panic_cases { ($( - mod $case_name:ident { - let DATA: $Data: ty = $data:expr; + // each test case needs a unique name to namespace the tests + in mod $case_name:ident { + data: $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. + // 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) $( - let GOOD_INPUT = $good:expr; - let GOOD_OUTPUT = $output:expr; + good: data[$good:expr] == $output:expr; )* - let BAD_INPUT = $bad:expr; - const EXPECT_MSG = $expect_msg:expr; - - !!generate_tests!! + bad: data[$bad:expr]; + message: $expect_msg:expr; } )*) => {$( mod $case_name { #[test] fn pass() { - let mut v: $Data = $data; + let mut v = $data; $( assert_range_eq!($data, $good, $output); )* @@ -487,7 +489,7 @@ mod slice_index { #[test] #[should_panic(expected = $expect_msg)] fn index_fail() { - let v: $Data = $data; + let v = $data; let v: &[_] = &v; let _v = &v[$bad]; } @@ -495,7 +497,7 @@ mod slice_index { #[test] #[should_panic(expected = $expect_msg)] fn index_mut_fail() { - let mut v: $Data = $data; + let mut v = $data; let v: &mut [_] = &mut v; let _v = &mut v[$bad]; } @@ -516,112 +518,80 @@ mod slice_index { } panic_cases! { - mod rangefrom_len { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; - - let GOOD_INPUT = 6..; - let GOOD_OUTPUT = []; + in mod rangefrom_len { + data: [0, 1, 2, 3, 4, 5]; - let BAD_INPUT = 7..; - const EXPECT_MSG = "but ends at"; // perhaps not ideal - - !!generate_tests!! + good: data[6..] == []; + bad: data[7..]; + message: "but ends at"; // perhaps not ideal } - 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]; + in mod rangeto_len { + data: [0, 1, 2, 3, 4, 5]; - let BAD_INPUT = ..7; - const EXPECT_MSG = "out of range"; - - !!generate_tests!! + good: data[..6] == [0, 1, 2, 3, 4, 5]; + bad: data[..7]; + message: "out of range"; } - 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]; + in mod rangetoinclusive_len { + data: [0, 1, 2, 3, 4, 5]; - let BAD_INPUT = ..=6; - const EXPECT_MSG = "out of range"; - - !!generate_tests!! + good: data[..=5] == [0, 1, 2, 3, 4, 5]; + bad: data[..=6]; + message: "out of range"; } - mod range_len_len { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; - - let GOOD_INPUT = 6..6; - let GOOD_OUTPUT = []; + in mod range_len_len { + data: [0, 1, 2, 3, 4, 5]; - let BAD_INPUT = 7..7; - const EXPECT_MSG = "out of range"; - - !!generate_tests!! + good: data[6..6] == []; + bad: data[7..7]; + message: "out of range"; } - 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"; + in mod rangeinclusive_len_len { + data: [0, 1, 2, 3, 4, 5]; - !!generate_tests!! + good: data[6..=5] == []; + bad: data[7..=6]; + message: "out of range"; } } 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"; + in mod range_neg_width { + data: [0, 1, 2, 3, 4, 5]; - !!generate_tests!! + good: data[4..4] == []; + bad: data[4..3]; + message: "but ends at"; } - mod rangeinclusive_neg_width { - let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + in mod rangeinclusive_neg_width { + data: [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!! + good: data[4..=3] == []; + bad: data[4..=2]; + message: "but ends at"; } } panic_cases! { - mod rangeinclusive_overflow { - let DATA: [i32; 2] = [0, 1]; + 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. - let BAD_INPUT = 0 ..= ::std::usize::MAX; - const EXPECT_MSG = "maximum usize"; - - !!generate_tests!! + bad: data[0 ..= ::std::usize::MAX]; + message: "maximum usize"; } - mod rangetoinclusive_overflow { - let DATA: [i32; 2] = [0, 1]; - - let BAD_INPUT = ..= ::std::usize::MAX; - const EXPECT_MSG = "maximum usize"; + in mod rangetoinclusive_overflow { + data: [0, 1]; - !!generate_tests!! + bad: data[..= ::std::usize::MAX]; + message: "maximum usize"; } } // panic_cases! } From f1d7b453fed6acefc68f90752922b37c6e3ac7a4 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:36 -0400 Subject: [PATCH 8/8] revise test gen macro for str --- src/liballoc/tests/str.rs | 225 ++++++++++++++------------------------ 1 file changed, 81 insertions(+), 144 deletions(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 696ce79f36920..2edd41a70b945 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -291,11 +291,14 @@ fn test_replace_pattern() { assert_eq!(data.replace(|c| c == 'γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ"); } +// 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. // - // DO NOT use this in `should_panic` tests, unless you are testing the macro itself. + // This is not suitable for testing failure on invalid inputs. macro_rules! assert_range_eq { ($s:expr, $range:expr, $expected:expr) => { @@ -340,7 +343,7 @@ mod slice_index { // 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 + // 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")] @@ -363,8 +366,8 @@ mod slice_index { // and `None` test cases for get/get_mut. macro_rules! panic_cases { ($( - mod $case_name:ident { - let DATA = $data:expr; + in mod $case_name:ident { + data: $data:expr; // optional: // @@ -373,14 +376,11 @@ mod slice_index { // 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; + good: data[$good:expr] == $output:expr; )* - let BAD_INPUT = $bad:expr; - const EXPECT_MSG = $expect_msg:expr; // must be a literal - - !!generate_tests!! + bad: data[$bad:expr]; + message: $expect_msg:expr; // must be a literal } )*) => {$( mod $case_name { @@ -509,114 +509,72 @@ mod slice_index { } panic_cases! { - mod rangefrom_len { - let DATA = "abcdef"; - - let GOOD_INPUT = 6..; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 7..; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangefrom_len { + data: "abcdef"; + good: data[6..] == ""; + bad: data[7..]; + message: "out of bounds"; } - 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!! + in mod rangeto_len { + data: "abcdef"; + good: data[..6] == "abcdef"; + bad: data[..7]; + message: "out of bounds"; } - 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!! + in mod rangetoinclusive_len { + data: "abcdef"; + good: data[..=5] == "abcdef"; + bad: data[..=6]; + message: "out of bounds"; } - 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!! + in mod range_len_len { + data: "abcdef"; + good: data[6..6] == ""; + bad: data[7..7]; + message: "out of bounds"; } - 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!! + in mod rangeinclusive_len_len { + data: "abcdef"; + good: data[6..=5] == ""; + bad: data[7..=6]; + message: "out of bounds"; } } 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!! + in mod range_neg_width { + data: "abcdef"; + good: data[4..4] == ""; + bad: data[4..3]; + message: "begin <= end (4 <= 3)"; } - 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!! + in mod rangeinclusive_neg_width { + data: "abcdef"; + good: data[4..=3] == ""; + bad: data[4..=2]; + message: "begin <= end (4 <= 3)"; } } mod overflow { panic_cases! { - - mod rangeinclusive { - let DATA = "hello"; - + 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. - let BAD_INPUT = 0..=usize::max_value(); - const EXPECT_MSG = "maximum usize"; - - !!generate_tests!! + bad: data[0..=usize::max_value()]; + message: "maximum usize"; } - mod rangetoinclusive { - let DATA = "hello"; - - let BAD_INPUT = ..=usize::max_value(); - const EXPECT_MSG = "maximum usize"; - - !!generate_tests!! + in mod rangetoinclusive { + data: "hello"; + bad: data[..=usize::max_value()]; + message: "maximum usize"; } } } @@ -635,74 +593,53 @@ mod slice_index { // 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 = + 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"; - - !!generate_tests!! } - mod range_2 { - let DATA = super::DATA; - - let BAD_INPUT = super::GOOD_START..super::BAD_END; - const EXPECT_MSG = + 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"; - - !!generate_tests!! } - mod rangefrom { - let DATA = super::DATA; - - let BAD_INPUT = super::BAD_START..; - const EXPECT_MSG = + 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"; - - !!generate_tests!! } - mod rangeto { - let DATA = super::DATA; - - let BAD_INPUT = ..super::BAD_END; - const EXPECT_MSG = + 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"; - - !!generate_tests!! } - mod rangeinclusive_1 { - let DATA = super::DATA; - - let BAD_INPUT = super::BAD_START..=super::GOOD_END_INCL; - const EXPECT_MSG = + 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"; - - !!generate_tests!! } - mod rangeinclusive_2 { - let DATA = super::DATA; - - let BAD_INPUT = super::GOOD_START..=super::BAD_END_INCL; - const EXPECT_MSG = + 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"; - - !!generate_tests!! } - mod rangetoinclusive { - let DATA = super::DATA; - - let BAD_INPUT = ..=super::BAD_END_INCL; - const EXPECT_MSG = + 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"; - - !!generate_tests!! } } }