about summary refs log tree commit diff
path: root/src/liballoc
diff options
context:
space:
mode:
authorMichael Lamparski <diagonaldevice@gmail.com>2018-04-30 07:37:08 -0400
committerMichael Lamparski <diagonaldevice@gmail.com>2018-04-30 07:37:08 -0400
commitce66f5d9185aa2b81159fa61597bbb6e4cf2847f (patch)
tree43701b16ff756b8023a23dc32ebce2407f8bcba2 /src/liballoc
parent0842dc67238969e39b0a08d2c4314ceefd19caa2 (diff)
downloadrust-ce66f5d9185aa2b81159fa61597bbb6e4cf2847f.tar.gz
rust-ce66f5d9185aa2b81159fa61597bbb6e4cf2847f.zip
flesh out tests for SliceIndex
m*n lines of implementation deserves m*n lines of tests
Diffstat (limited to 'src/liballoc')
-rw-r--r--src/liballoc/tests/str.rs477
1 files changed, 378 insertions, 99 deletions
diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs
index c9536cbe168..bfba9a6b393 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]