diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cd5243e7b..de50bec4a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -244,14 +244,23 @@ jobs: - name: Run tests under Miri run: | + set -eo pipefail + # Work around https://github.com/rust-lang/miri/issues/3125 [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ] && cargo clean - + + # Spawn twice the number of workers as there are CPU cores. + THREADS=$(echo "$(nproc) * 2" | bc) + echo "Running Miri tests with $THREADS threads" | tee -a $GITHUB_STEP_SUMMARY + + cargo install cargo-nextest + # Run under both the stacked borrows model (default) and under the tree # borrows model to ensure we're compliant with both. for EXTRA_FLAGS in "" "-Zmiri-tree-borrows"; do MIRIFLAGS="$MIRIFLAGS $EXTRA_FLAGS" ./cargo.sh +${{ matrix.toolchain }} \ - miri test \ + miri nextest run \ + --test-threads "$THREADS" \ --package ${{ matrix.crate }} \ --target ${{ matrix.target }} \ ${{ matrix.features }} @@ -464,6 +473,7 @@ jobs: cargo metadata &> /dev/null & cargo install cargo-readme --version 3.2.0 &> /dev/null & cargo install --locked kani-verifier &> /dev/null & + cargo install cargo-nextest &> /dev/null & cargo kani setup &> /dev/null & wait diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 523cdfe34d..7459b79371 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -1526,8 +1526,9 @@ mod tests { } } - #[test] - fn test_ptr_try_cast_into_soundness() { + mod test_ptr_try_cast_into_soundness { + use super::*; + // This test is designed so that if `Ptr::try_cast_into_xxx` are // buggy, it will manifest as unsoundness that Miri can detect. @@ -1650,9 +1651,21 @@ mod tests { trailing: [T], } + // Each test case becomes its own `#[test]` function. We do this because + // this test in particular takes far, far longer to execute under Miri + // than all of our other tests combined. Previously, we had these + // execute sequentially in a single test function. We run Miri tests in + // parallel in CI, but this test being sequential meant that most of + // that parallelism was wasted, as all other tests would finish in a + // fraction of the total execution time, leaving this test to execute on + // a single thread for the remainder of the test. By putting each test + // case in its own function, we permit better use of available + // parallelism. macro_rules! test { - ($($ty:ty),*) => { - $({ + ($test_name:ident: $ty:ty) => { + #[test] + #[allow(non_snake_case)] + fn $test_name() { const S: usize = core::mem::size_of::<$ty>(); const N: usize = if S == 0 { 4 } else { S * 4 }; test::<$ty, _, N>([None]); @@ -1667,11 +1680,15 @@ mod tests { test::<[$ty], _, N>([None, Some(0), Some(1), Some(2), Some(3)]); test::, _, N>([None, Some(0), Some(1), Some(2), Some(3)]); } - })* + } + }; + ($ty:ident) => { + test!($ty: $ty); }; + ($($ty:ident),*) => { $(test!($ty);)* } } - test!(()); + test!(empty_tuple: ()); test!(u8, u16, u32, u64, u128, usize, AU64); test!(i8, i16, i32, i64, i128, isize); test!(f32, f64);