Skip to content

Commit

Permalink
Run Miri on CI
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Jan 12, 2022
1 parent 28c5577 commit 5adb71f
Show file tree
Hide file tree
Showing 22 changed files with 110 additions and 29 deletions.
22 changes: 21 additions & 1 deletion .github/workflows/ci.yml
@@ -1,5 +1,8 @@
name: CI

permissions:
contents: read

on:
pull_request:
push:
Expand All @@ -10,8 +13,12 @@ on:
- cron: '0 1 * * *'

env:
RUSTFLAGS: -D warnings
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUSTFLAGS: -D warnings
RUSTUP_MAX_RETRIES: 10

defaults:
run:
Expand Down Expand Up @@ -229,6 +236,18 @@ jobs:
- run: ci/no_atomic_cas.sh
- run: git diff --exit-code

miri:
name: cargo miri test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup toolchain install nightly --component miri && rustup default nightly
# futures-executor uses boxed futures so many tests trigger https://github.com/rust-lang/miri/issues/1038
- run: cargo miri test --workspace --exclude futures-executor --all-features
env:
MIRIFLAGS: -Zmiri-disable-isolation # TODO: use -Zmiri-tag-raw-pointers

san:
name: cargo test -Z sanitizer=${{ matrix.sanitizer }}
strategy:
Expand Down Expand Up @@ -261,6 +280,7 @@ jobs:
- run: cargo clippy --workspace --all-features --all-targets

fmt:
name: cargo fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand Down
1 change: 1 addition & 0 deletions futures-channel/tests/mpsc-close.rs
Expand Up @@ -147,6 +147,7 @@ fn single_receiver_drop_closes_channel_and_drains() {

// Stress test that `try_send()`s occurring concurrently with receiver
// close/drops don't appear as successful sends.
#[cfg_attr(miri, ignore)] // Miri is too slow
#[test]
fn stress_try_send_as_receiver_closes() {
const AMT: usize = 10000;
Expand Down
25 changes: 23 additions & 2 deletions futures-channel/tests/mpsc.rs
Expand Up @@ -200,6 +200,9 @@ fn tx_close_gets_none() {

#[test]
fn stress_shared_unbounded() {
#[cfg(miri)]
const AMT: u32 = 100;
#[cfg(not(miri))]
const AMT: u32 = 10000;
const NTHREADS: u32 = 8;
let (tx, rx) = mpsc::unbounded::<i32>();
Expand Down Expand Up @@ -229,6 +232,9 @@ fn stress_shared_unbounded() {

#[test]
fn stress_shared_bounded_hard() {
#[cfg(miri)]
const AMT: u32 = 100;
#[cfg(not(miri))]
const AMT: u32 = 10000;
const NTHREADS: u32 = 8;
let (tx, rx) = mpsc::channel::<i32>(0);
Expand Down Expand Up @@ -259,6 +265,9 @@ fn stress_shared_bounded_hard() {
#[allow(clippy::same_item_push)]
#[test]
fn stress_receiver_multi_task_bounded_hard() {
#[cfg(miri)]
const AMT: usize = 100;
#[cfg(not(miri))]
const AMT: usize = 10_000;
const NTHREADS: u32 = 2;

Expand Down Expand Up @@ -327,6 +336,11 @@ fn stress_receiver_multi_task_bounded_hard() {
/// after sender dropped.
#[test]
fn stress_drop_sender() {
#[cfg(miri)]
const ITER: usize = 100;
#[cfg(not(miri))]
const ITER: usize = 10000;

fn list() -> impl Stream<Item = i32> {
let (tx, rx) = mpsc::channel(1);
thread::spawn(move || {
Expand All @@ -335,7 +349,7 @@ fn stress_drop_sender() {
rx
}

for _ in 0..10000 {
for _ in 0..ITER {
let v: Vec<_> = block_on(list().collect());
assert_eq!(v, vec![1, 2, 3]);
}
Expand Down Expand Up @@ -380,9 +394,12 @@ fn stress_close_receiver_iter() {
}
}

#[cfg_attr(miri, ignore)] // Miri is too slow
#[test]
fn stress_close_receiver() {
for _ in 0..10000 {
const ITER: usize = 10000;

for _ in 0..ITER {
stress_close_receiver_iter();
}
}
Expand All @@ -397,6 +414,9 @@ async fn stress_poll_ready_sender(mut sender: mpsc::Sender<u32>, count: u32) {
#[allow(clippy::same_item_push)]
#[test]
fn stress_poll_ready() {
#[cfg(miri)]
const AMT: u32 = 100;
#[cfg(not(miri))]
const AMT: u32 = 1000;
const NTHREADS: u32 = 8;

Expand Down Expand Up @@ -424,6 +444,7 @@ fn stress_poll_ready() {
stress(16);
}

#[cfg_attr(miri, ignore)] // Miri is too slow
#[test]
fn try_send_1() {
const N: usize = 3000;
Expand Down
14 changes: 12 additions & 2 deletions futures-channel/tests/oneshot.rs
Expand Up @@ -35,6 +35,11 @@ fn cancel_notifies() {

#[test]
fn cancel_lots() {
#[cfg(miri)]
const N: usize = 100;
#[cfg(not(miri))]
const N: usize = 20000;

let (tx, rx) = mpsc::channel::<(Sender<_>, mpsc::Sender<_>)>();
let t = thread::spawn(move || {
for (mut tx, tx2) in rx {
Expand All @@ -43,7 +48,7 @@ fn cancel_lots() {
}
});

for _ in 0..20000 {
for _ in 0..N {
let (otx, orx) = oneshot::channel::<u32>();
let (tx2, rx2) = mpsc::channel();
tx.send((otx, tx2)).unwrap();
Expand Down Expand Up @@ -101,14 +106,19 @@ fn is_canceled() {

#[test]
fn cancel_sends() {
#[cfg(miri)]
const N: usize = 100;
#[cfg(not(miri))]
const N: usize = 20000;

let (tx, rx) = mpsc::channel::<Sender<_>>();
let t = thread::spawn(move || {
for otx in rx {
let _ = otx.send(42);
}
});

for _ in 0..20000 {
for _ in 0..N {
let (otx, mut orx) = oneshot::channel::<u32>();
tx.send(otx).unwrap();

Expand Down
5 changes: 5 additions & 0 deletions futures-util/src/compat/compat01as03.rs
Expand Up @@ -64,6 +64,7 @@ pub trait Future01CompatExt: Future01 {
/// [`Future<Output = Result<T, E>>`](futures_core::future::Future).
///
/// ```
/// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514
/// # futures::executor::block_on(async {
/// # // TODO: These should be all using `futures::compat`, but that runs up against Cargo
/// # // feature issues
Expand All @@ -90,6 +91,7 @@ pub trait Stream01CompatExt: Stream01 {
/// [`Stream<Item = Result<T, E>>`](futures_core::stream::Stream).
///
/// ```
/// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514
/// # futures::executor::block_on(async {
/// use futures::stream::StreamExt;
/// use futures_util::compat::Stream01CompatExt;
Expand Down Expand Up @@ -119,6 +121,7 @@ pub trait Sink01CompatExt: Sink01 {
/// [`Sink<T, Error = E>`](futures_sink::Sink).
///
/// ```
/// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514
/// # futures::executor::block_on(async {
/// use futures::{sink::SinkExt, stream::StreamExt};
/// use futures_util::compat::{Stream01CompatExt, Sink01CompatExt};
Expand Down Expand Up @@ -362,6 +365,7 @@ mod io {
/// [`AsyncRead`](futures_io::AsyncRead).
///
/// ```
/// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514
/// # futures::executor::block_on(async {
/// use futures::io::AsyncReadExt;
/// use futures_util::compat::AsyncRead01CompatExt;
Expand Down Expand Up @@ -391,6 +395,7 @@ mod io {
/// [`AsyncWrite`](futures_io::AsyncWrite).
///
/// ```
/// # if cfg!(miri) { return; } // https://github.com/rust-lang/futures-rs/issues/2514
/// # futures::executor::block_on(async {
/// use futures::io::AsyncWriteExt;
/// use futures_util::compat::AsyncWrite01CompatExt;
Expand Down
1 change: 1 addition & 0 deletions futures-util/src/compat/executor.rs
Expand Up @@ -17,6 +17,7 @@ pub trait Executor01CompatExt: Executor01<Executor01Future> + Clone + Send + 'st
/// futures 0.3 [`Spawn`](futures_task::Spawn).
///
/// ```
/// # if cfg!(miri) { return; } // Miri does not support epoll
/// use futures::task::SpawnExt;
/// use futures::future::{FutureExt, TryFutureExt};
/// use futures_util::compat::Executor01CompatExt;
Expand Down
1 change: 1 addition & 0 deletions futures-util/src/stream/try_stream/mod.rs
Expand Up @@ -894,6 +894,7 @@ pub trait TryStreamExt: TryStream {
/// Wraps a [`TryStream`] into a stream compatible with libraries using
/// futures 0.1 `Stream`. Requires the `compat` feature to be enabled.
/// ```
/// # if cfg!(miri) { return; } // Miri does not support epoll
/// use futures::future::{FutureExt, TryFutureExt};
/// # let (tx, rx) = futures::channel::oneshot::channel();
///
Expand Down
3 changes: 3 additions & 0 deletions futures-util/src/task/spawn.rs
Expand Up @@ -34,6 +34,7 @@ pub trait SpawnExt: Spawn {
/// today. Feel free to use this method in the meantime.
///
/// ```
/// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038
/// use futures::executor::ThreadPool;
/// use futures::task::SpawnExt;
///
Expand All @@ -58,6 +59,7 @@ pub trait SpawnExt: Spawn {
/// resolves to the output of the spawned future.
///
/// ```
/// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038
/// use futures::executor::{block_on, ThreadPool};
/// use futures::future;
/// use futures::task::SpawnExt;
Expand Down Expand Up @@ -136,6 +138,7 @@ pub trait LocalSpawnExt: LocalSpawn {
/// resolves to the output of the spawned future.
///
/// ```
/// # if cfg!(miri) { return; } // https://github.com/rust-lang/miri/issues/1038
/// use futures::executor::LocalPool;
/// use futures::task::LocalSpawnExt;
///
Expand Down
1 change: 1 addition & 0 deletions futures/tests/compat.rs
@@ -1,4 +1,5 @@
#![cfg(feature = "compat")]
#![cfg(not(miri))] // Miri does not support epoll

use futures::compat::Future01CompatExt;
use futures::prelude::*;
Expand Down
2 changes: 2 additions & 0 deletions futures/tests/eventual.rs
@@ -1,3 +1,5 @@
#![cfg(not(miri))] // https://github.com/rust-lang/miri/issues/1038

use futures::channel::oneshot;
use futures::executor::ThreadPool;
use futures::future::{self, ok, Future, FutureExt, TryFutureExt};
Expand Down
25 changes: 12 additions & 13 deletions futures/tests/future_join_all.rs
@@ -1,22 +1,24 @@
use futures::executor::block_on;
use futures::future::{join_all, ready, Future, JoinAll};
use futures::pin_mut;
use std::fmt::Debug;

fn assert_done<T, F>(actual_fut: F, expected: T)
#[track_caller]
fn assert_done<T>(actual_fut: impl Future<Output = T>, expected: T)
where
T: PartialEq + Debug,
F: FnOnce() -> Box<dyn Future<Output = T> + Unpin>,
{
let output = block_on(actual_fut());
pin_mut!(actual_fut);
let output = block_on(actual_fut);
assert_eq!(output, expected);
}

#[test]
fn collect_collects() {
assert_done(|| Box::new(join_all(vec![ready(1), ready(2)])), vec![1, 2]);
assert_done(|| Box::new(join_all(vec![ready(1)])), vec![1]);
assert_done(join_all(vec![ready(1), ready(2)]), vec![1, 2]);
assert_done(join_all(vec![ready(1)]), vec![1]);
// REVIEW: should this be implemented?
// assert_done(|| Box::new(join_all(Vec::<i32>::new())), vec![]);
// assert_done(join_all(Vec::<i32>::new()), vec![]);

// TODO: needs more tests
}
Expand All @@ -25,18 +27,15 @@ fn collect_collects() {
fn join_all_iter_lifetime() {
// In futures-rs version 0.1, this function would fail to typecheck due to an overly
// conservative type parameterization of `JoinAll`.
fn sizes(bufs: Vec<&[u8]>) -> Box<dyn Future<Output = Vec<usize>> + Unpin> {
fn sizes(bufs: Vec<&[u8]>) -> impl Future<Output = Vec<usize>> {
let iter = bufs.into_iter().map(|b| ready::<usize>(b.len()));
Box::new(join_all(iter))
join_all(iter)
}

assert_done(|| sizes(vec![&[1, 2, 3], &[], &[0]]), vec![3_usize, 0, 1]);
assert_done(sizes(vec![&[1, 2, 3], &[], &[0]]), vec![3_usize, 0, 1]);
}

#[test]
fn join_all_from_iter() {
assert_done(
|| Box::new(vec![ready(1), ready(2)].into_iter().collect::<JoinAll<_>>()),
vec![1, 2],
)
assert_done(vec![ready(1), ready(2)].into_iter().collect::<JoinAll<_>>(), vec![1, 2])
}
1 change: 1 addition & 0 deletions futures/tests/future_shared.rs
Expand Up @@ -96,6 +96,7 @@ fn drop_in_poll() {
assert_eq!(block_on(future1), 1);
}

#[cfg_attr(miri, ignore)] // https://github.com/rust-lang/miri/issues/1038
#[test]
fn peek() {
let mut local_pool = LocalPool::new();
Expand Down
24 changes: 13 additions & 11 deletions futures/tests/future_try_join_all.rs
@@ -1,24 +1,26 @@
use futures::executor::block_on;
use futures::pin_mut;
use futures_util::future::{err, ok, try_join_all, TryJoinAll};
use std::fmt::Debug;
use std::future::Future;

fn assert_done<T, F>(actual_fut: F, expected: T)
#[track_caller]
fn assert_done<T>(actual_fut: impl Future<Output = T>, expected: T)
where
T: PartialEq + Debug,
F: FnOnce() -> Box<dyn Future<Output = T> + Unpin>,
{
let output = block_on(actual_fut());
pin_mut!(actual_fut);
let output = block_on(actual_fut);
assert_eq!(output, expected);
}

#[test]
fn collect_collects() {
assert_done(|| Box::new(try_join_all(vec![ok(1), ok(2)])), Ok::<_, usize>(vec![1, 2]));
assert_done(|| Box::new(try_join_all(vec![ok(1), err(2)])), Err(2));
assert_done(|| Box::new(try_join_all(vec![ok(1)])), Ok::<_, usize>(vec![1]));
assert_done(try_join_all(vec![ok(1), ok(2)]), Ok::<_, usize>(vec![1, 2]));
assert_done(try_join_all(vec![ok(1), err(2)]), Err(2));
assert_done(try_join_all(vec![ok(1)]), Ok::<_, usize>(vec![1]));
// REVIEW: should this be implemented?
// assert_done(|| Box::new(try_join_all(Vec::<i32>::new())), Ok(vec![]));
// assert_done(try_join_all(Vec::<i32>::new()), Ok(vec![]));

// TODO: needs more tests
}
Expand All @@ -27,18 +29,18 @@ fn collect_collects() {
fn try_join_all_iter_lifetime() {
// In futures-rs version 0.1, this function would fail to typecheck due to an overly
// conservative type parameterization of `TryJoinAll`.
fn sizes(bufs: Vec<&[u8]>) -> Box<dyn Future<Output = Result<Vec<usize>, ()>> + Unpin> {
fn sizes(bufs: Vec<&[u8]>) -> impl Future<Output = Result<Vec<usize>, ()>> {
let iter = bufs.into_iter().map(|b| ok::<usize, ()>(b.len()));
Box::new(try_join_all(iter))
try_join_all(iter)
}

assert_done(|| sizes(vec![&[1, 2, 3], &[], &[0]]), Ok(vec![3_usize, 0, 1]));
assert_done(sizes(vec![&[1, 2, 3], &[], &[0]]), Ok(vec![3_usize, 0, 1]));
}

#[test]
fn try_join_all_from_iter() {
assert_done(
|| Box::new(vec![ok(1), ok(2)].into_iter().collect::<TryJoinAll<_>>()),
vec![ok(1), ok(2)].into_iter().collect::<TryJoinAll<_>>(),
Ok::<_, usize>(vec![1, 2]),
)
}

0 comments on commit 5adb71f

Please sign in to comment.