diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 0aa2d20f4e..c08fd58512 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -17,6 +17,7 @@ on: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 + DUMP_SIMULATION_SEEDS: /tmp/simulation-seeds concurrency: group: ${{ github.workflow }}-${{ github.ref_name }} @@ -68,12 +69,14 @@ jobs: cargo +${{ matrix.rust-toolchain }} check $BUILD_TYPE --all-targets --features ci - name: Run tests and determine coverage + env: + RUST_LOG: trace run: | # shellcheck disable=SC2086 if [ "${{ matrix.rust-toolchain }}" == "stable" ]; then - RUST_LOG=trace cargo +${{ matrix.rust-toolchain }} llvm-cov nextest $BUILD_TYPE --features ci --no-fail-fast --lcov --output-path lcov.info + cargo +${{ matrix.rust-toolchain }} llvm-cov nextest $BUILD_TYPE --features ci --no-fail-fast --lcov --output-path lcov.info else - RUST_LOG=trace cargo +${{ matrix.rust-toolchain }} nextest run $BUILD_TYPE --features ci --no-fail-fast + cargo +${{ matrix.rust-toolchain }} nextest run $BUILD_TYPE --features ci --no-fail-fast fi - name: Run client/server transfer @@ -121,6 +124,17 @@ jobs: RUSTFLAGS="-Z sanitizer=$sanitizer" RUSTDOCFLAGS="-Z sanitizer=$sanitizer" cargo +nightly nextest run -Z build-std --features ci --target "$TARGET" done + - name: Print simulation seeds + if: env.DUMP_SIMULATION_SEEDS + run: ls -1 ${{ env.DUMP_SIMULATION_SEEDS }} + + - name: Save simulation seeds artifact + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: simulation-seeds + path: /tmp/simulation-seeds + compression-level: 9 + bench: needs: [check] if: > diff --git a/test-fixture/src/sim/mod.rs b/test-fixture/src/sim/mod.rs index 17e7f2b465..91bccc6bbd 100644 --- a/test-fixture/src/sim/mod.rs +++ b/test-fixture/src/sim/mod.rs @@ -15,12 +15,13 @@ use std::{ cell::RefCell, cmp::min, fmt::Debug, + fs::write, ops::{Deref, DerefMut}, rc::Rc, time::{Duration, Instant}, }; -use neqo_common::{qdebug, qinfo, qtrace, Datagram, Encoder}; +use neqo_common::{qdebug, qerror, qinfo, qtrace, Datagram, Encoder}; use neqo_transport::Output; use rng::Random; use NodeState::{Active, Idle, Waiting}; @@ -64,11 +65,7 @@ macro_rules! simulate { let f: Box _> = Box::new($v); nodes.push(Box::new(f(&fixture))); )* - let mut sim = Simulator::new(stringify!($n), nodes); - if let Ok(seed) = std::env::var("SIMULATION_SEED") { - sim.seed_str(seed); - } - sim.run(); + Simulator::new(stringify!($n), nodes).run(); } }; } @@ -151,23 +148,33 @@ impl Simulator { .into_iter() .chain(it.map(|node| NodeHolder { node, state: Idle })) .collect::>(); - Self { + let mut sim = Self { name, nodes, rng: Rc::default(), + }; + // Seed from the `SIMULATION_SEED` environment variable, if set. + if let Ok(seed) = std::env::var("SIMULATION_SEED") { + sim.seed_str(seed); } - } - - pub fn seed(&mut self, seed: [u8; 32]) { - self.rng = Rc::new(RefCell::new(Random::new(&seed))); + // Dump the seed to a file to the directory in the `DUMP_SIMULATION_SEEDS` environment + // variable, if set. + if let Ok(dir) = std::env::var("DUMP_SIMULATION_SEEDS") { + let seed_str = sim.rng.borrow().seed_str(); + let path = format!("{dir}/{seed_str}"); + if write(&path, sim.rng.borrow().seed_str()).is_err() { + qerror!("Failed to write seed to {path}"); + } + } + sim } /// Seed from a hex string. /// # Panics /// When the provided string is not 32 bytes of hex (64 characters). pub fn seed_str(&mut self, seed: impl AsRef) { - let seed = Encoder::from_hex(seed); - self.seed(<[u8; 32]>::try_from(seed.as_ref()).unwrap()); + let seed = <[u8; 32]>::try_from(Encoder::from_hex(seed).as_ref()).unwrap(); + self.rng = Rc::new(RefCell::new(Random::new(&seed))); } fn next_time(&self, now: Instant) -> Instant {