diff --git a/CHANGELOG b/CHANGELOG index a5d36e4d..c08ef207 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added fuzzing and miri code safety analysis to our CI pipelines. - Removed requirement of `alloc` in `no_std` ennvironments without the `write` feature. - Make multi-digit optimizations in integer parsing optional. +- Much higher miri coverage including for proptests. ### Changed @@ -29,6 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Writing `-0.0` with a leading `-`. - Reduced binary siezes when the compact feature was enabled. - Improved performance of integer and float parsing, particularly with small integers. +- Removed almost all unsafety in `lexical-util` and clearly documented the preconditions to use safely. +- Removed almost all unsafety in `lexical-write-integer` and clearly documented the preconditions to use safely. ### Removed diff --git a/lexical-parse-float/tests/api_tests.rs b/lexical-parse-float/tests/api_tests.rs index 3c402bd7..17cbad64 100644 --- a/lexical-parse-float/tests/api_tests.rs +++ b/lexical-parse-float/tests/api_tests.rs @@ -1,3 +1,6 @@ +mod util; + +use crate::util::default_proptest_config; #[cfg(feature = "format")] use core::num; use lexical_parse_float::{FromLexical, FromLexicalWithOptions, Options}; @@ -13,7 +16,6 @@ use lexical_util::format::NumberFormatBuilder; use lexical_util::format::STANDARD; use lexical_util::num::Float; use proptest::prelude::*; -use quickcheck::quickcheck; #[test] fn special_bytes_test() { @@ -179,7 +181,6 @@ fn f32_radix_test() { } #[test] -#[cfg_attr(miri, ignore)] fn parse_f32_test() { let parse = move |x| f32::from_lexical_partial(x); @@ -242,7 +243,6 @@ fn parse_f32_test() { } #[test] -#[cfg_attr(miri, ignore)] fn parse_f64_test() { let parse = move |x| f64::from_lexical_partial(x); @@ -1250,15 +1250,13 @@ fn float_equal(x: F, y: F) -> bool { } } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn f32_roundtrip_quickcheck(x: f32) -> bool { let string = x.to_string(); let result = f32::from_lexical(string.as_bytes()); result.map_or(false, |y| float_equal(x, y)) } - #[cfg_attr(miri, ignore)] fn f32_short_decimal_quickcheck(x: f32) -> bool { let string = format!("{:.4}", x); let actual = f32::from_lexical(string.as_bytes()); @@ -1266,7 +1264,6 @@ quickcheck! { actual.map_or(false, |y| expected.map_or(false, |x| float_equal(x, y))) } - #[cfg_attr(miri, ignore)] fn f32_long_decimal_quickcheck(x: f32) -> bool { let string = format!("{:.100}", x); let actual = f32::from_lexical(string.as_bytes()); @@ -1274,7 +1271,6 @@ quickcheck! { actual.map_or(false, |y| expected.map_or(false, |x| float_equal(x, y))) } - #[cfg_attr(miri, ignore)] fn f32_short_exponent_quickcheck(x: f32) -> bool { let string = format!("{:.4e}", x); let actual = f32::from_lexical(string.as_bytes()); @@ -1282,7 +1278,6 @@ quickcheck! { actual.map_or(false, |y| expected.map_or(false, |x| float_equal(x, y))) } - #[cfg_attr(miri, ignore)] fn f32_long_exponent_quickcheck(x: f32) -> bool { let string = format!("{:.100e}", x); let actual = f32::from_lexical(string.as_bytes()); @@ -1290,14 +1285,12 @@ quickcheck! { actual.map_or(false, |y| expected.map_or(false, |x| float_equal(x, y))) } - #[cfg_attr(miri, ignore)] fn f64_roundtrip_quickcheck(x: f64) -> bool { let string = x.to_string(); let result = f64::from_lexical(string.as_bytes()); result.map_or(false, |y| float_equal(x, y)) } - #[cfg_attr(miri, ignore)] fn f64_short_decimal_quickcheck(x: f64) -> bool { let string = format!("{:.4}", x); let actual = f64::from_lexical(string.as_bytes()); @@ -1305,7 +1298,6 @@ quickcheck! { actual.map_or(false, |y| expected.map_or(false, |x| float_equal(x, y))) } - #[cfg_attr(miri, ignore)] fn f64_long_decimal_quickcheck(x: f64) -> bool { let string = format!("{:.100}", x); let actual = f64::from_lexical(string.as_bytes()); @@ -1313,7 +1305,6 @@ quickcheck! { actual.map_or(false, |y| expected.map_or(false, |x| float_equal(x, y))) } - #[cfg_attr(miri, ignore)] fn f64_short_exponent_quickcheck(x: f64) -> bool { let string = format!("{:.4e}", x); let actual = f64::from_lexical(string.as_bytes()); @@ -1321,7 +1312,6 @@ quickcheck! { actual.map_or(false, |y| expected.map_or(false, |x| float_equal(x, y))) } - #[cfg_attr(miri, ignore)] fn f64_long_exponent_quickcheck(x: f64) -> bool { let string = format!("{:.100e}", x); let actual = f64::from_lexical(string.as_bytes()); @@ -1330,7 +1320,6 @@ quickcheck! { } #[cfg(feature = "f16")] - #[cfg_attr(miri, ignore)] fn f16_roundtrip_quickcheck(bits: u16) -> bool { let x = f16::from_bits(bits); let string = x.as_f32().to_string(); @@ -1339,7 +1328,6 @@ quickcheck! { } #[cfg(feature = "f16")] - #[cfg_attr(miri, ignore)] fn bf16_roundtrip_quickcheck(bits: u16) -> bool { let x = bf16::from_bits(bits); let string = x.as_f32().to_string(); @@ -1349,8 +1337,9 @@ quickcheck! { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn f32_invalid_proptest(i in r"[+-]?[0-9]{2}[^\deE]?\.[^\deE]?[0-9]{2}[^\deE]?e[+-]?[0-9]+[^\deE]") { let res = f32::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1358,7 +1347,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_double_sign_proptest(i in r"[+-]{2}[0-9]{2}\.[0-9]{2}e[+-]?[0-9]+") { let res = f32::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1369,7 +1357,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_sign_or_dot_only_proptest(i in r"[+-]?\.?") { let res = f32::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1380,7 +1367,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_double_exponent_sign_proptest(i in r"[+-]?[0-9]{2}\.[0-9]{2}e[+-]{2}[0-9]+") { let res = f32::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1388,7 +1374,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_missing_exponent_proptest(i in r"[+-]?[0-9]{2}\.[0-9]{2}e[+-]?") { let res = f32::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1396,28 +1381,24 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_roundtrip_display_proptest(i in f32::MIN..f32::MAX) { let input: String = format!("{}", i); prop_assert_eq!(i, f32::from_lexical(input.as_bytes()).unwrap()); } #[test] - #[cfg_attr(miri, ignore)] fn f32_roundtrip_debug_proptest(i in f32::MIN..f32::MAX) { let input: String = format!("{:?}", i); prop_assert_eq!(i, f32::from_lexical(input.as_bytes()).unwrap()); } #[test] - #[cfg_attr(miri, ignore)] fn f32_roundtrip_scientific_proptest(i in f32::MIN..f32::MAX) { let input: String = format!("{:e}", i); prop_assert_eq!(i, f32::from_lexical(input.as_bytes()).unwrap()); } #[test] - #[cfg_attr(miri, ignore)] fn f64_invalid_proptest(i in r"[+-]?[0-9]{2}[^\deE]?\.[^\deE]?[0-9]{2}[^\deE]?e[+-]?[0-9]+[^\deE]") { let res = f64::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1425,7 +1406,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_double_sign_proptest(i in r"[+-]{2}[0-9]{2}\.[0-9]{2}e[+-]?[0-9]+") { let res = f64::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1436,7 +1416,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_sign_or_dot_only_proptest(i in r"[+-]?\.?") { let res = f64::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1447,7 +1426,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_double_exponent_sign_proptest(i in r"[+-]?[0-9]{2}\.[0-9]{2}e[+-]{2}[0-9]+") { let res = f64::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1455,7 +1433,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_missing_exponent_proptest(i in r"[+-]?[0-9]{2}\.[0-9]{2}e[+-]?") { let res = f64::from_lexical(i.as_bytes()); prop_assert!(res.is_err()); @@ -1463,21 +1440,18 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_roundtrip_display_proptest(i in f64::MIN..f64::MAX) { let input: String = format!("{}", i); prop_assert_eq!(i, f64::from_lexical(input.as_bytes()).unwrap()); } #[test] - #[cfg_attr(miri, ignore)] fn f64_roundtrip_debug_proptest(i in f64::MIN..f64::MAX) { let input: String = format!("{:?}", i); prop_assert_eq!(i, f64::from_lexical(input.as_bytes()).unwrap()); } #[test] - #[cfg_attr(miri, ignore)] fn f64_roundtrip_scientific_proptest(i in f64::MIN..f64::MAX) { let input: String = format!("{:e}", i); prop_assert_eq!(i, f64::from_lexical(input.as_bytes()).unwrap()); diff --git a/lexical-parse-float/tests/util.rs b/lexical-parse-float/tests/util.rs new file mode 100644 index 00000000..e87a0836 --- /dev/null +++ b/lexical-parse-float/tests/util.rs @@ -0,0 +1,57 @@ +#![allow(dead_code, unused_imports)] + +use proptest::prelude::*; +pub(crate) use quickcheck::QuickCheck; + +pub fn default_proptest_config() -> ProptestConfig { + ProptestConfig { + cases: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().cases + }, + max_shrink_iters: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().max_shrink_iters + }, + failure_persistence: if cfg!(miri) { + None + } else { + ProptestConfig::default().failure_persistence + }, + ..ProptestConfig::default() + } +} + +// This is almost identical to quickcheck's itself, just to add default arguments +// https://docs.rs/quickcheck/1.0.3/src/quickcheck/lib.rs.html#43-67 +// The code is unlicensed. +#[macro_export] +macro_rules! default_quickcheck { + (@as_items $($i:item)*) => ($($i)*); + { + $( + $(#[$m:meta])* + fn $fn_name:ident($($arg_name:ident : $arg_ty:ty),*) -> $ret:ty { + $($code:tt)* + } + )* + } => ( + $crate::default_quickcheck! { + @as_items + $( + #[test] + $(#[$m])* + fn $fn_name() { + fn prop($($arg_name: $arg_ty),*) -> $ret { + $($code)* + } + $crate::util::QuickCheck::new() + .max_tests(if cfg!(miri) { 10 } else { 10_000 }) + .quickcheck(prop as fn($($arg_ty),*) -> $ret); + } + )* + } + ) +} diff --git a/lexical-parse-integer/tests/algorithm_tests.rs b/lexical-parse-integer/tests/algorithm_tests.rs index 14f24a32..35d9ba24 100644 --- a/lexical-parse-integer/tests/algorithm_tests.rs +++ b/lexical-parse-integer/tests/algorithm_tests.rs @@ -1,8 +1,8 @@ #![cfg(not(feature = "compact"))] -#[cfg(feature = "power-of-two")] mod util; +use crate::util::default_proptest_config; use lexical_parse_integer::algorithm; use lexical_parse_integer::options::SMALL_NUMBERS; use lexical_util::format::STANDARD; @@ -180,8 +180,9 @@ fn algorithm_128_test() { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn parse_4digits_proptest( a in 0x30u32..0x39, b in 0x30u32..0x39, @@ -196,7 +197,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn parse_8digits_proptest( a in 0x30u64..0x39, b in 0x30u64..0x39, diff --git a/lexical-parse-integer/tests/api_tests.rs b/lexical-parse-integer/tests/api_tests.rs index 93863e8a..e3a1b4be 100644 --- a/lexical-parse-integer/tests/api_tests.rs +++ b/lexical-parse-integer/tests/api_tests.rs @@ -1,6 +1,6 @@ -#[cfg(feature = "power-of-two")] mod util; +use crate::util::default_proptest_config; use lexical_parse_integer::{FromLexical, FromLexicalWithOptions, Options}; use lexical_util::error::Error; #[cfg(feature = "format")] @@ -402,8 +402,9 @@ macro_rules! is_invalid_digit_match { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "power-of-two")] fn i32_binary_roundtrip_display_proptest(i in i32::MIN..i32::MAX) { let options = Options::new(); @@ -418,361 +419,301 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn u8_invalid_proptest(i in r"[+]?[0-9]{2}\D") { is_invalid_digit_match!(u8::from_lexical(i.as_bytes()), 2 | 3); } #[test] - #[cfg_attr(miri, ignore)] fn u8_overflow_proptest(i in r"[+]?[1-9][0-9]{3}") { is_overflow!(u8::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u8_negative_proptest(i in r"[-][1-9][0-9]{2}") { is_invalid_digit!(u8::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u8_double_sign_proptest(i in r"[+]{2}[0-9]{2}") { is_invalid_digit_match!(u8::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn u8_sign_only_proptest(i in r"[+]") { is_empty!(u8::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u8_trailing_digits_proptest(i in r"[+]?[0-9]{2}\D[0-9]{2}") { is_invalid_digit_match!(u8::from_lexical(i.as_bytes()), 2 | 3); } #[test] - #[cfg_attr(miri, ignore)] fn i8_invalid_proptest(i in r"[+-]?[0-9]{2}\D") { is_invalid_digit_match!(i8::from_lexical(i.as_bytes()), 2 | 3); } #[test] - #[cfg_attr(miri, ignore)] fn i8_overflow_proptest(i in r"[+]?[1-9][0-9]{3}") { is_overflow!(i8::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i8_underflow_proptest(i in r"[-][1-9][0-9]{3}") { is_underflow!(i8::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i8_double_sign_proptest(i in r"[+-]{2}[0-9]{2}") { is_invalid_digit_match!(i8::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn i8_sign_only_proptest(i in r"[+-]") { is_empty!(i8::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i8_trailing_digits_proptest(i in r"[+-]?[0-9]{2}\D[0-9]{2}") { is_invalid_digit_match!(i8::from_lexical(i.as_bytes()), 2 | 3); } #[test] - #[cfg_attr(miri, ignore)] fn u16_invalid_proptest(i in r"[+]?[0-9]{4}\D") { is_invalid_digit_match!(u16::from_lexical(i.as_bytes()), 4 | 5); } #[test] - #[cfg_attr(miri, ignore)] fn u16_overflow_proptest(i in r"[+]?[1-9][0-9]{5}") { is_overflow!(u16::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u16_negative_proptest(i in r"[-][1-9][0-9]{4}") { is_invalid_digit!(u16::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u16_double_sign_proptest(i in r"[+]{2}[0-9]{4}") { is_invalid_digit_match!(u16::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn u16_sign_only_proptest(i in r"[+]") { is_empty!(u16::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u16_trailing_digits_proptest(i in r"[+]?[0-9]{4}\D[0-9]{2}") { is_invalid_digit_match!(u16::from_lexical(i.as_bytes()), 4 | 5); } #[test] - #[cfg_attr(miri, ignore)] fn i16_invalid_proptest(i in r"[+-]?[0-9]{4}\D") { is_invalid_digit_match!(i16::from_lexical(i.as_bytes()), 4 | 5); } #[test] - #[cfg_attr(miri, ignore)] fn i16_overflow_proptest(i in r"[+]?[1-9][0-9]{5}") { is_overflow!(i16::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i16_underflow_proptest(i in r"[-][1-9][0-9]{5}") { is_underflow!(i16::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i16_double_sign_proptest(i in r"[+-]{2}[0-9]{4}") { is_invalid_digit_match!(i16::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn i16_sign_only_proptest(i in r"[+-]") { is_empty!(i16::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i16_trailing_digits_proptest(i in r"[+-]?[0-9]{4}\D[0-9]{2}") { is_invalid_digit_match!(i16::from_lexical(i.as_bytes()), 4 | 5); } #[test] - #[cfg_attr(miri, ignore)] fn u32_invalid_proptest(i in r"[+]?[0-9]{9}\D") { is_invalid_digit_match!(u32::from_lexical(i.as_bytes()), 9 | 10); } #[test] - #[cfg_attr(miri, ignore)] fn u32_overflow_proptest(i in r"[+]?[1-9][0-9]{10}") { is_overflow!(u32::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u32_negative_proptest(i in r"[-][1-9][0-9]{9}") { is_invalid_digit!(u32::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u32_double_sign_proptest(i in r"[+]{2}[0-9]{9}") { is_invalid_digit_match!(u32::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn u32_sign_only_proptest(i in r"[+]") { is_empty!(u32::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u32_trailing_digits_proptest(i in r"[+]?[0-9]{9}\D[0-9]{2}") { is_invalid_digit_match!(u32::from_lexical(i.as_bytes()), 9 | 10); } #[test] - #[cfg_attr(miri, ignore)] fn i32_invalid_proptest(i in r"[+-]?[0-9]{9}\D") { is_invalid_digit_match!(i32::from_lexical(i.as_bytes()), 9 | 10); } #[test] - #[cfg_attr(miri, ignore)] fn i32_overflow_proptest(i in r"[+]?[1-9][0-9]{10}") { is_overflow!(i32::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i32_underflow_proptest(i in r"-[1-9][0-9]{10}") { is_underflow!(i32::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i32_double_sign_proptest(i in r"[+-]{2}[0-9]{9}") { is_invalid_digit_match!(i32::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn i32_sign_only_proptest(i in r"[+-]") { is_empty!(i32::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i32_trailing_digits_proptest(i in r"[+-]?[0-9]{9}\D[0-9]{2}") { is_invalid_digit_match!(i32::from_lexical(i.as_bytes()), 9 | 10); } #[test] - #[cfg_attr(miri, ignore)] fn u64_invalid_proptest(i in r"[+]?[0-9]{19}\D") { is_invalid_digit_match!(u64::from_lexical(i.as_bytes()), 19 | 20); } #[test] - #[cfg_attr(miri, ignore)] fn u64_overflow_proptest(i in r"[+]?[1-9][0-9]{21}") { is_overflow!(u64::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u64_negative_proptest(i in r"[-][1-9][0-9]{21}") { is_invalid_digit!(u64::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u64_double_sign_proptest(i in r"[+]{2}[0-9]{19}") { is_invalid_digit_match!(u64::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn u64_sign_only_proptest(i in r"[+]") { is_empty!(u64::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u64_trailing_digits_proptest(i in r"[+]?[0-9]{19}\D[0-9]{2}") { is_invalid_digit_match!(u64::from_lexical(i.as_bytes()), 19 | 20); } #[test] - #[cfg_attr(miri, ignore)] fn i64_invalid_proptest(i in r"[+-]?[0-9]{18}\D") { is_invalid_digit_match!(i64::from_lexical(i.as_bytes()), 18 | 19); } #[test] - #[cfg_attr(miri, ignore)] fn i64_overflow_proptest(i in r"[+]?[1-9][0-9]{19}") { is_overflow!(i64::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i64_underflow_proptest(i in r"-[1-9][0-9]{19}") { is_underflow!(i64::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i64_double_sign_proptest(i in r"[+-]{2}[0-9]{18}") { is_invalid_digit_match!(i64::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn i64_sign_only_proptest(i in r"[+-]") { is_empty!(i64::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i64_trailing_digits_proptest(i in r"[+-]?[0-9]{18}\D[0-9]{2}") { is_invalid_digit_match!(i64::from_lexical(i.as_bytes()), 18 | 19); } #[test] - #[cfg_attr(miri, ignore)] fn u128_invalid_proptest(i in r"[+]?[0-9]{38}\D") { is_invalid_digit_match!(u128::from_lexical(i.as_bytes()), 38 | 39); } #[test] - #[cfg_attr(miri, ignore)] fn u128_overflow_proptest(i in r"[+]?[1-9][0-9]{39}") { is_overflow!(u128::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u128_negative_proptest(i in r"[-][1-9][0-9]{39}") { is_invalid_digit!(u128::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u128_double_sign_proptest(i in r"[+]{2}[0-9]{38}") { is_invalid_digit_match!(u128::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn u128_sign_only_proptest(i in r"[+]") { is_empty!(u128::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn u128_trailing_digits_proptest(i in r"[+]?[0-9]{38}\D[0-9]{2}") { is_invalid_digit_match!(u128::from_lexical(i.as_bytes()), 38 | 39); } #[test] - #[cfg_attr(miri, ignore)] fn i128_invalid_proptest(i in r"[+-]?[0-9]{38}\D") { is_invalid_digit_match!(i128::from_lexical(i.as_bytes()), 38 | 39); } #[test] - #[cfg_attr(miri, ignore)] fn i128_overflow_proptest(i in r"[+]?[1-9][0-9]{39}") { is_overflow!(i128::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i128_underflow_proptest(i in r"-[1-9][0-9]{39}") { is_underflow!(i128::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i128_double_sign_proptest(i in r"[+-]{2}[0-9]{38}") { is_invalid_digit_match!(i128::from_lexical(i.as_bytes()), 1); } #[test] - #[cfg_attr(miri, ignore)] fn i128_sign_only_proptest(i in r"[+-]") { is_empty!(i128::from_lexical(i.as_bytes())); } #[test] - #[cfg_attr(miri, ignore)] fn i128_trailing_digits_proptest(i in r"[+-]?[0-9]{38}\D[0-9]{2}") { is_invalid_digit_match!(i128::from_lexical(i.as_bytes()), 38 | 39); } diff --git a/lexical-parse-integer/tests/util.rs b/lexical-parse-integer/tests/util.rs index f4210baa..8d71bdfb 100644 --- a/lexical-parse-integer/tests/util.rs +++ b/lexical-parse-integer/tests/util.rs @@ -1,7 +1,64 @@ +#![allow(dead_code, unused_imports)] + #[cfg(feature = "power-of-two")] use lexical_util::format::NumberFormatBuilder; +use proptest::prelude::*; +pub(crate) use quickcheck::QuickCheck; #[cfg(feature = "power-of-two")] pub const fn from_radix(radix: u8) -> u128 { NumberFormatBuilder::from_radix(radix) } + +pub fn default_proptest_config() -> ProptestConfig { + ProptestConfig { + cases: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().cases + }, + max_shrink_iters: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().max_shrink_iters + }, + failure_persistence: if cfg!(miri) { + None + } else { + ProptestConfig::default().failure_persistence + }, + ..ProptestConfig::default() + } +} + +// This is almost identical to quickcheck's itself, just to add default arguments +// https://docs.rs/quickcheck/1.0.3/src/quickcheck/lib.rs.html#43-67 +// The code is unlicensed. +#[macro_export] +macro_rules! default_quickcheck { + (@as_items $($i:item)*) => ($($i)*); + { + $( + $(#[$m:meta])* + fn $fn_name:ident($($arg_name:ident : $arg_ty:ty),*) -> $ret:ty { + $($code:tt)* + } + )* + } => ( + $crate::default_quickcheck! { + @as_items + $( + #[test] + $(#[$m])* + fn $fn_name() { + fn prop($($arg_name: $arg_ty),*) -> $ret { + $($code)* + } + $crate::util::QuickCheck::new() + .max_tests(if cfg!(miri) { 10 } else { 10_000 }) + .quickcheck(prop as fn($($arg_ty),*) -> $ret); + } + )* + } + ) +} diff --git a/lexical-util/tests/bf16_tests.rs b/lexical-util/tests/bf16_tests.rs index 9e54c0c9..45c88a97 100644 --- a/lexical-util/tests/bf16_tests.rs +++ b/lexical-util/tests/bf16_tests.rs @@ -1,9 +1,11 @@ #![cfg(feature = "f16")] +mod util; + +use crate::util::default_proptest_config; use lexical_util::bf16::bf16; use lexical_util::num::Float; use proptest::prelude::*; -use quickcheck::quickcheck; #[test] fn as_f32_test() { @@ -45,8 +47,7 @@ fn math_tests() { assert_eq!(bf16::ONE % bf16::ONE, bf16::ZERO); } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn f32_roundtrip_quickcheck(x: u16) -> bool { let f = bf16::from_bits(x).as_f32(); bf16::from_f32(f).to_bits() == x @@ -54,8 +55,9 @@ quickcheck! { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn f32_roundtrip_proptest(x in u16::MIN..u16::MAX) { let f = bf16::from_bits(x).as_f32(); prop_assert_eq!(bf16::from_f32(f).to_bits(), x); diff --git a/lexical-util/tests/div128_tests.rs b/lexical-util/tests/div128_tests.rs index 9d6c5bf8..9ec71e98 100644 --- a/lexical-util/tests/div128_tests.rs +++ b/lexical-util/tests/div128_tests.rs @@ -1,13 +1,17 @@ #![cfg(not(feature = "compact"))] #![cfg(feature = "write")] +mod util; + +use crate::util::default_proptest_config; use lexical_util::div128::u128_divrem; use lexical_util::step::u64_step; use proptest::{prop_assert_eq, proptest}; proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn u128_divrem_proptest(i in u128::MIN..u128::MAX) { let (hi, lo) = u128_divrem(i, 10); let step = u64_step(10); @@ -17,7 +21,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn u128_divrem_radix_proptest(i in u128::MIN..u128::MAX, radix in 2u32..=36) { // Simulate a const expr. diff --git a/lexical-util/tests/f16_tests.rs b/lexical-util/tests/f16_tests.rs index 728d5e9e..dae79aa9 100644 --- a/lexical-util/tests/f16_tests.rs +++ b/lexical-util/tests/f16_tests.rs @@ -1,9 +1,10 @@ #![cfg(feature = "f16")] +mod util; + use lexical_util::f16::f16; use lexical_util::num::Float; use proptest::prelude::*; -use quickcheck::quickcheck; #[test] fn as_f32_test() { @@ -42,8 +43,7 @@ fn math_tests() { assert_eq!(f16::ONE % f16::ONE, f16::ZERO); } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn f32_roundtrip_quickcheck(x: u16) -> bool { let f = f16::from_bits(x).as_f32(); if f.is_nan() { @@ -55,8 +55,9 @@ quickcheck! { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn f32_roundtrip_proptest(x in u16::MIN..u16::MAX) { let f = f16::from_bits(x).as_f32(); if f.is_nan() { diff --git a/lexical-util/tests/mul_tests.rs b/lexical-util/tests/mul_tests.rs index cb87fe7e..38792048 100644 --- a/lexical-util/tests/mul_tests.rs +++ b/lexical-util/tests/mul_tests.rs @@ -1,8 +1,8 @@ +mod util; + use lexical_util::mul::{mul, mulhi}; -use quickcheck::quickcheck; -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn mul_u16_quickcheck(x: u16, y: u16) -> bool { let (hi, lo) = mul::(x, y); let hi = hi as u32; @@ -11,7 +11,6 @@ quickcheck! { ((hi << 16) | lo) == expected } - #[cfg_attr(miri, ignore)] fn mul_u32_quickcheck(x: u32, y: u32) -> bool { let (hi, lo) = mul::(x, y); let hi = hi as u64; @@ -20,7 +19,6 @@ quickcheck! { ((hi << 32) | lo) == expected } - #[cfg_attr(miri, ignore)] fn mul_u64_quickcheck(x: u64, y: u64) -> bool { let (hi, lo) = mul::(x, y); let hi = hi as u128; @@ -29,21 +27,18 @@ quickcheck! { ((hi << 64) | lo) == expected } - #[cfg_attr(miri, ignore)] fn mulhi_u16_quickcheck(x: u16, y: u16) -> bool { let actual = mulhi::(x, y); let expected = (x as u32 * y as u32) >> 16; actual == expected as u16 } - #[cfg_attr(miri, ignore)] fn mulhi_u32_quickcheck(x: u32, y: u32) -> bool { let actual = mulhi::(x, y); let expected = (x as u64 * y as u64) >> 32; actual == expected as u32 } - #[cfg_attr(miri, ignore)] fn mulhi_u64_quickcheck(x: u64, y: u64) -> bool { let actual = mulhi::(x, y); let expected = (x as u128 * y as u128) >> 64; diff --git a/lexical-util/tests/util.rs b/lexical-util/tests/util.rs new file mode 100644 index 00000000..e87a0836 --- /dev/null +++ b/lexical-util/tests/util.rs @@ -0,0 +1,57 @@ +#![allow(dead_code, unused_imports)] + +use proptest::prelude::*; +pub(crate) use quickcheck::QuickCheck; + +pub fn default_proptest_config() -> ProptestConfig { + ProptestConfig { + cases: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().cases + }, + max_shrink_iters: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().max_shrink_iters + }, + failure_persistence: if cfg!(miri) { + None + } else { + ProptestConfig::default().failure_persistence + }, + ..ProptestConfig::default() + } +} + +// This is almost identical to quickcheck's itself, just to add default arguments +// https://docs.rs/quickcheck/1.0.3/src/quickcheck/lib.rs.html#43-67 +// The code is unlicensed. +#[macro_export] +macro_rules! default_quickcheck { + (@as_items $($i:item)*) => ($($i)*); + { + $( + $(#[$m:meta])* + fn $fn_name:ident($($arg_name:ident : $arg_ty:ty),*) -> $ret:ty { + $($code:tt)* + } + )* + } => ( + $crate::default_quickcheck! { + @as_items + $( + #[test] + $(#[$m])* + fn $fn_name() { + fn prop($($arg_name: $arg_ty),*) -> $ret { + $($code)* + } + $crate::util::QuickCheck::new() + .max_tests(if cfg!(miri) { 10 } else { 10_000 }) + .quickcheck(prop as fn($($arg_ty),*) -> $ret); + } + )* + } + ) +} diff --git a/lexical-write-float/tests/algorithm_tests.rs b/lexical-write-float/tests/algorithm_tests.rs index 5c47f499..ec8fe631 100644 --- a/lexical-write-float/tests/algorithm_tests.rs +++ b/lexical-write-float/tests/algorithm_tests.rs @@ -1,5 +1,8 @@ #![cfg(not(feature = "compact"))] +mod util; + +use crate::util::default_proptest_config; use core::num; use lexical_util::constants::BUFFER_SIZE; use lexical_util::format::NumberFormatBuilder; @@ -8,7 +11,6 @@ use lexical_write_float::algorithm::DragonboxFloat; use lexical_write_float::float::{ExtendedFloat80, RawFloat}; use lexical_write_float::{algorithm, Options, RoundMode}; use proptest::prelude::*; -use quickcheck::quickcheck; const DECIMAL: u128 = NumberFormatBuilder::decimal(); @@ -748,8 +750,7 @@ fn is_left_endpoint_test() { assert_eq!(algorithm::is_left_endpoint::(4), false); } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn f32_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -764,7 +765,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f64_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -781,8 +781,9 @@ quickcheck! { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn f32_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -796,7 +797,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); diff --git a/lexical-write-float/tests/api_tests.rs b/lexical-write-float/tests/api_tests.rs index 7bd7cf9c..78b3aa1f 100644 --- a/lexical-write-float/tests/api_tests.rs +++ b/lexical-write-float/tests/api_tests.rs @@ -1,3 +1,6 @@ +mod util; + +use crate::util::default_proptest_config; #[cfg(feature = "f16")] use lexical_util::bf16::bf16; use lexical_util::constants::BUFFER_SIZE; @@ -6,7 +9,6 @@ use lexical_util::f16::f16; use lexical_util::format::STANDARD; use lexical_write_float::{Options, ToLexical, ToLexicalWithOptions}; use proptest::prelude::*; -use quickcheck::quickcheck; #[test] fn error_tests() { @@ -84,8 +86,7 @@ fn hex_test() { assert_eq!(result, b"3.039^12"); } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn f32_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let actual = unsafe { std::str::from_utf8_unchecked(f.to_lexical(&mut buffer)) }; @@ -97,7 +98,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f64_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let actual = unsafe { std::str::from_utf8_unchecked(f.to_lexical(&mut buffer)) }; @@ -111,8 +111,9 @@ quickcheck! { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn f32_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let actual = unsafe { std::str::from_utf8_unchecked(f.to_lexical(&mut buffer)) }; @@ -125,7 +126,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let actual = unsafe { std::str::from_utf8_unchecked(f.to_lexical(&mut buffer)) }; @@ -139,7 +139,6 @@ proptest! { #[test] #[cfg(feature = "f16")] - #[cfg_attr(miri, ignore)] fn f16_proptest(bits in u16::MIN..u16::MAX) { use lexical_util::num::Float; @@ -156,7 +155,6 @@ proptest! { #[test] #[cfg(feature = "f16")] - #[cfg_attr(miri, ignore)] fn bf16_proptest(bits in u16::MIN..u16::MAX) { use lexical_util::num::Float; diff --git a/lexical-write-float/tests/binary_tests.rs b/lexical-write-float/tests/binary_tests.rs index e4881821..edc9094e 100644 --- a/lexical-write-float/tests/binary_tests.rs +++ b/lexical-write-float/tests/binary_tests.rs @@ -1,7 +1,9 @@ #![cfg(feature = "power-of-two")] mod parse_radix; +mod util; +use crate::util::default_proptest_config; use core::num; use lexical_util::constants::{FormattedSize, BUFFER_SIZE}; use lexical_util::format::NumberFormatBuilder; @@ -11,7 +13,6 @@ use lexical_write_float::{binary, Options}; use lexical_write_integer::write::WriteInteger; use parse_radix::{parse_f32, parse_f64}; use proptest::prelude::*; -use quickcheck::quickcheck; const BINARY: u128 = NumberFormatBuilder::binary(); const BASE4: u128 = NumberFormatBuilder::from_radix(4); @@ -1089,8 +1090,7 @@ fn write_float_test() { write_float::<_, BINARY>(1.2345678901234567890e2f64, &round, "1111011.1"); } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn f32_binary_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -1104,7 +1104,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f32_octal_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -1118,7 +1117,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f64_binary_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -1132,7 +1130,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f64_octal_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -1148,8 +1145,9 @@ quickcheck! { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn f32_binary_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -1162,7 +1160,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_octal_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -1175,7 +1172,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_binary_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -1188,7 +1184,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_octal_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); diff --git a/lexical-write-float/tests/compact_tests.rs b/lexical-write-float/tests/compact_tests.rs index ecf6bb7b..84ad117a 100644 --- a/lexical-write-float/tests/compact_tests.rs +++ b/lexical-write-float/tests/compact_tests.rs @@ -1,5 +1,8 @@ #![cfg(feature = "compact")] +mod util; + +use crate::util::default_proptest_config; use core::num; use lexical_util::constants::BUFFER_SIZE; use lexical_util::format::NumberFormatBuilder; @@ -7,7 +10,6 @@ use lexical_util::num::Float; use lexical_write_float::float::{ExtendedFloat80, RawFloat}; use lexical_write_float::{compact, Options, RoundMode}; use proptest::prelude::*; -use quickcheck::quickcheck; const DECIMAL: u128 = NumberFormatBuilder::decimal(); @@ -759,8 +761,7 @@ fn f64_roundtrip_test() { } } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn f32_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -775,7 +776,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f64_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -792,8 +792,9 @@ quickcheck! { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn f32_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -807,7 +808,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); diff --git a/lexical-write-float/tests/radix_tests.rs b/lexical-write-float/tests/radix_tests.rs index a404633d..27babc88 100644 --- a/lexical-write-float/tests/radix_tests.rs +++ b/lexical-write-float/tests/radix_tests.rs @@ -1,7 +1,9 @@ #![cfg(feature = "radix")] mod parse_radix; +mod util; +use crate::util::default_proptest_config; use approx::{assert_relative_eq, relative_eq}; use core::num; use lexical_util::constants::{FormattedSize, BUFFER_SIZE}; @@ -12,7 +14,6 @@ use lexical_write_float::{radix, Options}; use lexical_write_integer::write::WriteInteger; use parse_radix::{parse_f32, parse_f64}; use proptest::prelude::*; -use quickcheck::quickcheck; const BASE3: u128 = NumberFormatBuilder::from_radix(3); const BASE5: u128 = NumberFormatBuilder::from_radix(5); @@ -281,7 +282,6 @@ macro_rules! test_all { } #[test] -#[cfg_attr(miri, ignore)] fn f32_radix_roundtrip_test() { let mut buffer = [b'\x00'; 1200]; let options = Options::new(); @@ -291,7 +291,6 @@ fn f32_radix_roundtrip_test() { } #[test] -#[cfg_attr(miri, ignore)] fn f64_radix_roundtrip_test() { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::new(); @@ -361,8 +360,7 @@ macro_rules! is_overflow { }; } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn f32_base3_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -376,7 +374,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f32_base5_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -390,7 +387,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f32_base21_quickcheck(f: f32) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().exponent(b'^').build().unwrap(); @@ -404,7 +400,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f64_base3_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -418,7 +413,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f64_base5_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -432,7 +426,6 @@ quickcheck! { } } - #[cfg_attr(miri, ignore)] fn f64_base21_quickcheck(f: f64) -> bool { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().exponent(b'^').build().unwrap(); @@ -448,8 +441,9 @@ quickcheck! { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn f32_base3_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -463,7 +457,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base5_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -477,7 +470,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base21_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().exponent(b'^').build().unwrap(); @@ -491,7 +483,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base3_short_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -508,7 +499,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base5_short_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -525,7 +515,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base21_short_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -543,7 +532,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base3_long_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -560,7 +548,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base5_long_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -577,7 +564,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base21_long_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -595,7 +581,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base3_short_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -614,7 +599,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base5_short_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -633,7 +617,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base21_short_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -653,7 +636,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base3_long_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -672,7 +654,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base5_long_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -691,7 +672,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f32_base21_long_exponent_proptest(f in f32::MIN..f32::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -711,7 +691,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base3_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -725,7 +704,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base5_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().build().unwrap(); @@ -739,7 +717,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base21_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; BUFFER_SIZE]; let options = Options::builder().exponent(b'^').build().unwrap(); @@ -753,7 +730,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base3_short_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -770,7 +746,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base5_short_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -787,7 +762,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base21_short_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -805,7 +779,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base3_long_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -822,7 +795,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base5_long_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -839,7 +811,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base21_long_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -857,7 +828,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base3_short_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -876,7 +846,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base5_short_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -895,7 +864,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base21_short_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -915,7 +883,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base3_long_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -934,7 +901,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base5_long_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() @@ -953,7 +919,6 @@ proptest! { } #[test] - #[cfg_attr(miri, ignore)] fn f64_base21_long_exponent_proptest(f in f64::MIN..f64::MAX) { let mut buffer = [b'\x00'; 512]; let options = Options::builder() diff --git a/lexical-write-float/tests/util.rs b/lexical-write-float/tests/util.rs new file mode 100644 index 00000000..e87a0836 --- /dev/null +++ b/lexical-write-float/tests/util.rs @@ -0,0 +1,57 @@ +#![allow(dead_code, unused_imports)] + +use proptest::prelude::*; +pub(crate) use quickcheck::QuickCheck; + +pub fn default_proptest_config() -> ProptestConfig { + ProptestConfig { + cases: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().cases + }, + max_shrink_iters: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().max_shrink_iters + }, + failure_persistence: if cfg!(miri) { + None + } else { + ProptestConfig::default().failure_persistence + }, + ..ProptestConfig::default() + } +} + +// This is almost identical to quickcheck's itself, just to add default arguments +// https://docs.rs/quickcheck/1.0.3/src/quickcheck/lib.rs.html#43-67 +// The code is unlicensed. +#[macro_export] +macro_rules! default_quickcheck { + (@as_items $($i:item)*) => ($($i)*); + { + $( + $(#[$m:meta])* + fn $fn_name:ident($($arg_name:ident : $arg_ty:ty),*) -> $ret:ty { + $($code:tt)* + } + )* + } => ( + $crate::default_quickcheck! { + @as_items + $( + #[test] + $(#[$m])* + fn $fn_name() { + fn prop($($arg_name: $arg_ty),*) -> $ret { + $($code)* + } + $crate::util::QuickCheck::new() + .max_tests(if cfg!(miri) { 10 } else { 10_000 }) + .quickcheck(prop as fn($($arg_ty),*) -> $ret); + } + )* + } + ) +} diff --git a/lexical-write-integer/tests/api_tests.rs b/lexical-write-integer/tests/api_tests.rs index 4d3b0476..fa04f932 100644 --- a/lexical-write-integer/tests/api_tests.rs +++ b/lexical-write-integer/tests/api_tests.rs @@ -1,6 +1,6 @@ -#[cfg(feature = "radix")] mod util; +use crate::util::default_proptest_config; use core::fmt::Debug; use core::str::{from_utf8_unchecked, FromStr}; #[cfg(feature = "radix")] @@ -10,7 +10,6 @@ use lexical_util::format::NumberFormatBuilder; use lexical_util::format::STANDARD; use lexical_write_integer::{Options, ToLexical, ToLexicalWithOptions}; use proptest::prelude::*; -use quickcheck::quickcheck; #[cfg(feature = "radix")] use util::from_radix; @@ -1186,220 +1185,186 @@ fn u128_pow10_test() { } } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn u8_quickcheck(i: u8) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn u16_quickcheck(i: u16) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn u32_quickcheck(i: u32) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn u64_quickcheck(i: u64) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn u128_quickcheck(i: u128) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn usize_quickcheck(i: usize) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn i8_quickcheck(i: i8) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn i16_quickcheck(i: i16) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn i32_quickcheck(i: i32) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn i64_quickcheck(i: i64) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn i128_quickcheck(i: i128) -> bool { i == roundtrip(i) } - #[cfg_attr(miri, ignore)] fn isize_quickcheck(i: isize) -> bool { i == roundtrip(i) } } proptest! { + #![proptest_config(default_proptest_config())] + #[test] - #[cfg_attr(miri, ignore)] fn u8_proptest(i in u8::MIN..u8::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn i8_proptest(i in i8::MIN..i8::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn u16_proptest(i in u16::MIN..u16::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn i16_proptest(i in i16::MIN..i16::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn u32_proptest(i in u32::MIN..u32::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn i32_proptest(i in i32::MIN..i32::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn u64_proptest(i in u64::MIN..u64::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn i64_proptest(i in i64::MIN..i64::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn u128_proptest(i in u128::MIN..u128::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn i128_proptest(i in i128::MIN..i128::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn usize_proptest(i in usize::MIN..usize::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] fn isize_proptest(i in isize::MIN..isize::MAX) { prop_assert_eq!(i, roundtrip(i)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn u8_proptest_radix(i in u8::MIN..u8::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn i8_proptest_radix(i in i8::MIN..i8::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn u16_proptest_radix(i in u16::MIN..u16::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn i16_proptest_radix(i in i16::MIN..i16::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn u32_proptest_radix(i in u32::MIN..u32::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn i32_proptest_radix(i in i32::MIN..i32::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn u64_proptest_radix(i in u64::MIN..u64::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn i64_proptest_radix(i in i64::MIN..i64::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn u128_proptest_radix(i in u128::MIN..u128::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn i128_proptest_radix(i in i128::MIN..i128::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn usize_proptest_radix(i in usize::MIN..usize::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); } #[test] - #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] fn isize_proptest_radix(i in isize::MIN..isize::MAX, radix in 2u32..=36) { prop_assert_eq!(i, roundtrip_radix(i, radix)); diff --git a/lexical-write-integer/tests/decimal_tests.rs b/lexical-write-integer/tests/decimal_tests.rs index 4b83ecc2..6c345d9e 100644 --- a/lexical-write-integer/tests/decimal_tests.rs +++ b/lexical-write-integer/tests/decimal_tests.rs @@ -1,8 +1,9 @@ #![cfg(not(feature = "compact"))] +mod util; + use lexical_util::num::UnsignedInteger; use lexical_write_integer::decimal::{self, Decimal, DigitCount}; -use quickcheck::quickcheck; #[test] fn fast_log2_test() { @@ -412,28 +413,23 @@ fn slow_digit_count(x: T) -> usize { x.to_string().len() } -quickcheck! { - #[cfg_attr(miri, ignore)] +default_quickcheck! { fn fast_log2_quickcheck(x: u32) -> bool { slow_log2(x) == decimal::fast_log2(x) } - #[cfg_attr(miri, ignore)] fn u32_digit_count_quickcheck(x: u32) -> bool { slow_digit_count(x) == x.digit_count() } - #[cfg_attr(miri, ignore)] fn u64_digit_count_quickcheck(x: u64) -> bool { slow_digit_count(x) == x.digit_count() } - #[cfg_attr(miri, ignore)] fn u128_digit_count_quickcheck(x: u128) -> bool { slow_digit_count(x) == x.digit_count() } - #[cfg_attr(miri, ignore)] fn u32toa_quickcheck(x: u32) -> bool { let actual = x.to_string(); let mut buffer = [b'\x00'; 16]; @@ -441,7 +437,6 @@ quickcheck! { &buffer[..actual.len()] == actual.as_bytes() } - #[cfg_attr(miri, ignore)] fn u64toa_quickcheck(x: u64) -> bool { let actual = x.to_string(); let mut buffer = [b'\x00'; 32]; @@ -449,7 +444,6 @@ quickcheck! { &buffer[..actual.len()] == actual.as_bytes() } - #[cfg_attr(miri, ignore)] fn u128toa_quickcheck(x: u128) -> bool { let actual = x.to_string(); let mut buffer = [b'\x00'; 48]; diff --git a/lexical-write-integer/tests/radix_tests.rs b/lexical-write-integer/tests/radix_tests.rs index 5bf19671..5ec8bc7d 100644 --- a/lexical-write-integer/tests/radix_tests.rs +++ b/lexical-write-integer/tests/radix_tests.rs @@ -3,11 +3,11 @@ mod util; +use crate::util::{default_proptest_config, from_radix}; use core::str::from_utf8_unchecked; use lexical_util::constants::BUFFER_SIZE; use lexical_write_integer::write::WriteInteger; use proptest::prelude::*; -use util::from_radix; #[test] #[cfg(feature = "radix")] @@ -167,6 +167,8 @@ fn u128toa_mockup(x: u128, radix: u32) -> Result<(), TestCaseError> { } proptest! { + #![proptest_config(default_proptest_config())] + #[test] #[cfg_attr(miri, ignore)] #[cfg(feature = "radix")] diff --git a/lexical-write-integer/tests/util.rs b/lexical-write-integer/tests/util.rs index f4210baa..8d71bdfb 100644 --- a/lexical-write-integer/tests/util.rs +++ b/lexical-write-integer/tests/util.rs @@ -1,7 +1,64 @@ +#![allow(dead_code, unused_imports)] + #[cfg(feature = "power-of-two")] use lexical_util::format::NumberFormatBuilder; +use proptest::prelude::*; +pub(crate) use quickcheck::QuickCheck; #[cfg(feature = "power-of-two")] pub const fn from_radix(radix: u8) -> u128 { NumberFormatBuilder::from_radix(radix) } + +pub fn default_proptest_config() -> ProptestConfig { + ProptestConfig { + cases: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().cases + }, + max_shrink_iters: if cfg!(miri) { + 10 + } else { + ProptestConfig::default().max_shrink_iters + }, + failure_persistence: if cfg!(miri) { + None + } else { + ProptestConfig::default().failure_persistence + }, + ..ProptestConfig::default() + } +} + +// This is almost identical to quickcheck's itself, just to add default arguments +// https://docs.rs/quickcheck/1.0.3/src/quickcheck/lib.rs.html#43-67 +// The code is unlicensed. +#[macro_export] +macro_rules! default_quickcheck { + (@as_items $($i:item)*) => ($($i)*); + { + $( + $(#[$m:meta])* + fn $fn_name:ident($($arg_name:ident : $arg_ty:ty),*) -> $ret:ty { + $($code:tt)* + } + )* + } => ( + $crate::default_quickcheck! { + @as_items + $( + #[test] + $(#[$m])* + fn $fn_name() { + fn prop($($arg_name: $arg_ty),*) -> $ret { + $($code)* + } + $crate::util::QuickCheck::new() + .max_tests(if cfg!(miri) { 10 } else { 10_000 }) + .quickcheck(prop as fn($($arg_ty),*) -> $ret); + } + )* + } + ) +}