Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,35 @@ jobs:
cargo-v3-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}-
cargo-v3-${{ runner.os }}-

- name: v3 Compiler Tests
run: cargo test -p v3-compiler
# Layer 1 (coarse CI): wall-clock gates on `cargo test -p v3-compiler`.
#
# 120s gate: applies to the lane2 Stage 2d symbolic-cost integration
# binary only — that is the ζ (#537) regression surface (many `#[test]`
# each doing full compile). A warm full `cargo test -p v3-compiler` is
# still ~400s locally (testgen / rustc roundtrips), so a 120s cap on the
# *entire* suite would fail every run until suite-wide setup is shared;
# tighten the second gate when that baseline drops.
- name: v3 tests with 120s budget
run: |
start=$(date +%s)
cargo test -p v3-compiler --test lane2_stage_2d_symbolic_cost_test
elapsed=$(( $(date +%s) - start ))
echo "v3 test wall time: ${elapsed}s"
if [ "$elapsed" -gt 120 ]; then
echo "::error::v3 tests took ${elapsed}s (budget: 120s) — share bootstrap setup via OnceLock or collapse fine-grained tests"
exit 1
fi

- name: v3 tests (full suite, 600s budget)
run: |
start=$(date +%s)
cargo test -p v3-compiler
elapsed=$(( $(date +%s) - start ))
echo "v3 full-suite wall time: ${elapsed}s"
if [ "$elapsed" -gt 600 ]; then
echo "::error::v3 full-suite tests took ${elapsed}s (budget: 600s). Likely cause: integration tests redoing full bootstrap + compile per #[test] without shared setup, or an unexpected suite-wide slowdown. Share compile results via OnceLock/module cache, collapse fine-grained tests, or trim redundant work."
exit 1
fi

- name: v3 Compiler Clippy
run: cargo clippy -p v3-compiler --all-targets -- -D warnings
Expand Down
31 changes: 31 additions & 0 deletions src/v3/compiler/tests/common/budgeted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//! Per-test wall-clock budgets for integration tests.
//!
//! Principle: a single `#[test]` that spends multiple seconds is usually
//! either hanging or redoing full-pipeline work that should be shared
//! (`std::sync::OnceLock`, module-level cache, or fewer parametrized cases).
//!
//! **Consumers:** same-PR as the helper (E-6-style discipline). Use the
//! `budgeted_test!` macro from any `tests/*.rs` that declares `mod common;`
//! (each integration test binary is its own crate; the macro expands to
//! `$crate::common::budgeted::...`).

use std::time::{Duration, Instant};

/// Default single-test budget in milliseconds (2s).
pub const DEFAULT_BUDGET_MS: u64 = 2000;

/// Run `f` and panic if wall time exceeds `budget_ms`.
pub fn with_budget_ms<F: FnOnce()>(budget_ms: u64, f: F) {
let budget = Duration::from_millis(budget_ms);
let start = Instant::now();
f();
let elapsed = start.elapsed();
assert!(
elapsed <= budget,
"test exceeded wall-clock budget: {:?} > {:?} — share expensive setup (OnceLock / \
module cache) or collapse fine-grained tests; full bootstrap + compile per test is not \
allowed",
elapsed,
budget
);
}
39 changes: 39 additions & 0 deletions src/v3/compiler/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
//! because the OS eventually reaps `/tmp` and because panics in tests
//! benefit from leaving artifacts visible.

pub mod budgeted;

use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
Expand Down Expand Up @@ -232,3 +234,40 @@ pub fn require_fixture_cost_usize(lookup: CostLookup, context: &str) -> usize {
panic!("complexity lens emitted cost that does not fit in usize for {context}: {cost}")
})
}

// ────────────────────────────────────────────────────────────────────
// Per-test wall-clock budget macro (Layer 2 ratchet)
// ────────────────────────────────────────────────────────────────────
//
// `#[macro_export]` places this at the integration-test crate root so
// any `tests/*.rs` that declares `mod common;` can invoke
// `budgeted_test! { ... }` unqualified.

/// Per-test wall-clock budget (default 2s) for integration tests.
///
/// **Requires** `mod common;` at the top of the same `tests/*.rs` file: each
/// integration test binary is its own crate, and the macro expands to
/// `$crate::common::budgeted::with_budget_ms` / `DEFAULT_BUDGET_MS`, which only
/// exist when `common` is linked (`budgeted` is a public submodule).
///
/// Forms:
/// - `budgeted_test! { name, { ... } }` — default 2000 ms.
/// - `budgeted_test! { 5000, name, { ... } }` — custom budget in ms.
#[macro_export]
macro_rules! budgeted_test {
($ms:literal, $name:ident, $body:block) => {
#[test]
fn $name() {
$crate::common::budgeted::with_budget_ms($ms, || $body);
}
};
($name:ident, $body:block) => {
#[test]
fn $name() {
$crate::common::budgeted::with_budget_ms(
$crate::common::budgeted::DEFAULT_BUDGET_MS,
|| $body,
);
}
};
}
Loading
Loading