Not working too well and difficult to track, but has a skeleton there to work from. Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
256 lines
7 KiB
Rust
256 lines
7 KiB
Rust
//! Property-based tests for the local CI simulation.
|
|
//!
|
|
//! These verify invariants that should hold across all possible simulation configurations.
|
|
|
|
use simulation::ci::local_sim::{self, LocalSimConfig};
|
|
use swactor_ci::{EventType, WebhookEvent};
|
|
|
|
fn simple_yaml() -> String {
|
|
r#"
|
|
pipelines:
|
|
check:
|
|
triggers:
|
|
- event: push
|
|
branches: ["*"]
|
|
jobs:
|
|
fmt:
|
|
run: cargo fmt -- --check
|
|
test:
|
|
needs: [fmt]
|
|
run: cargo test
|
|
"#
|
|
.into()
|
|
}
|
|
|
|
fn push(branch: &str, sha: &str) -> WebhookEvent {
|
|
WebhookEvent {
|
|
event_type: EventType::Push,
|
|
repo_owner: "user".into(),
|
|
repo_name: "repo".into(),
|
|
branch: branch.into(),
|
|
commit_sha: sha.into(),
|
|
tag: None,
|
|
}
|
|
}
|
|
|
|
// ─── Property: One-at-a-time ────────────────────────────────────────────────
|
|
|
|
#[test]
|
|
fn property_one_at_a_time_single_push() {
|
|
let config = LocalSimConfig {
|
|
name: "prop-1at1-single".into(),
|
|
num_rounds: 30,
|
|
ci_yaml: simple_yaml(),
|
|
webhook_schedule: vec![(1, push("main", "sha-1"))],
|
|
job_duration: 2,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_one_at_a_time(&trace),
|
|
"at most one job running at a time"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn property_one_at_a_time_burst() {
|
|
let config = LocalSimConfig {
|
|
name: "prop-1at1-burst".into(),
|
|
num_rounds: 100,
|
|
ci_yaml: simple_yaml(),
|
|
webhook_schedule: (1..=10)
|
|
.map(|i| (1, push(&format!("branch-{i}"), &format!("sha-{i}"))))
|
|
.collect(),
|
|
job_duration: 2,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_one_at_a_time(&trace),
|
|
"at most one job running at a time under burst"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn property_one_at_a_time_staggered() {
|
|
let config = LocalSimConfig {
|
|
name: "prop-1at1-stagger".into(),
|
|
num_rounds: 80,
|
|
ci_yaml: simple_yaml(),
|
|
webhook_schedule: vec![
|
|
(1, push("a", "sha-a")),
|
|
(3, push("b", "sha-b")),
|
|
(5, push("c", "sha-c")),
|
|
(7, push("d", "sha-d")),
|
|
],
|
|
job_duration: 3,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_one_at_a_time(&trace),
|
|
"at most one job running at a time under stagger"
|
|
);
|
|
}
|
|
|
|
// ─── Property: Supersede correctness ────────────────────────────────────────
|
|
|
|
#[test]
|
|
fn property_superseded_pipelines_never_run() {
|
|
let config = LocalSimConfig {
|
|
name: "prop-supersede".into(),
|
|
num_rounds: 60,
|
|
ci_yaml: simple_yaml(),
|
|
// Same branch, rapid pushes while jobs are long.
|
|
webhook_schedule: (1..=8)
|
|
.map(|i| (i, push("feature", &format!("sha-ss-{i}"))))
|
|
.collect(),
|
|
job_duration: 4,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_superseded_no_running(&trace),
|
|
"superseded pipelines should never have a running job"
|
|
);
|
|
}
|
|
|
|
// ─── Property: Termination ──────────────────────────────────────────────────
|
|
|
|
#[test]
|
|
fn property_all_non_superseded_terminate() {
|
|
let config = LocalSimConfig {
|
|
name: "prop-terminate".into(),
|
|
num_rounds: 100,
|
|
ci_yaml: simple_yaml(),
|
|
webhook_schedule: vec![
|
|
(1, push("a", "sha-t1")),
|
|
(2, push("b", "sha-t2")),
|
|
(3, push("a", "sha-t3")),
|
|
(10, push("c", "sha-t4")),
|
|
],
|
|
job_duration: 3,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_termination(&trace),
|
|
"all non-superseded pipelines must reach terminal status"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn property_all_webhooks_terminate() {
|
|
let config = LocalSimConfig {
|
|
name: "prop-wh-terminate".into(),
|
|
num_rounds: 100,
|
|
ci_yaml: simple_yaml(),
|
|
webhook_schedule: (1..=5)
|
|
.map(|i| (i * 3, push("main", &format!("sha-wh-{i}"))))
|
|
.collect(),
|
|
job_duration: 2,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_all_webhooks_terminate(&trace),
|
|
"every webhook must produce a terminal status"
|
|
);
|
|
}
|
|
|
|
// ─── Property: DAG ordering ─────────────────────────────────────────────────
|
|
|
|
#[test]
|
|
fn property_dag_ordering_deep_chain() {
|
|
let yaml = r#"
|
|
pipelines:
|
|
deep:
|
|
triggers:
|
|
- event: push
|
|
branches: ["*"]
|
|
jobs:
|
|
a:
|
|
run: echo a
|
|
b:
|
|
needs: [a]
|
|
run: echo b
|
|
c:
|
|
needs: [b]
|
|
run: echo c
|
|
d:
|
|
needs: [c]
|
|
run: echo d
|
|
"#;
|
|
let config = LocalSimConfig {
|
|
name: "prop-dag-deep".into(),
|
|
num_rounds: 50,
|
|
ci_yaml: yaml.into(),
|
|
webhook_schedule: vec![(1, push("main", "sha-dag"))],
|
|
job_duration: 2,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_dag_ordering(&trace),
|
|
"deep DAG ordering must be respected"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn property_dag_ordering_diamond() {
|
|
let yaml = r#"
|
|
pipelines:
|
|
diamond:
|
|
triggers:
|
|
- event: push
|
|
branches: ["*"]
|
|
jobs:
|
|
root:
|
|
run: echo root
|
|
left:
|
|
needs: [root]
|
|
run: echo left
|
|
right:
|
|
needs: [root]
|
|
run: echo right
|
|
merge:
|
|
needs: [left, right]
|
|
run: echo merge
|
|
"#;
|
|
let config = LocalSimConfig {
|
|
name: "prop-dag-diamond".into(),
|
|
num_rounds: 50,
|
|
ci_yaml: yaml.into(),
|
|
webhook_schedule: vec![(1, push("main", "sha-dia"))],
|
|
job_duration: 2,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_dag_ordering(&trace),
|
|
"diamond DAG ordering must be respected"
|
|
);
|
|
}
|
|
|
|
// ─── Property: FIFO across branches ─────────────────────────────────────────
|
|
|
|
#[test]
|
|
fn property_fifo_across_branches() {
|
|
let config = LocalSimConfig {
|
|
name: "prop-fifo".into(),
|
|
num_rounds: 80,
|
|
ci_yaml: simple_yaml(),
|
|
webhook_schedule: vec![
|
|
(1, push("a", "sha-f1")),
|
|
(2, push("b", "sha-f2")),
|
|
(3, push("c", "sha-f3")),
|
|
],
|
|
job_duration: 3,
|
|
..Default::default()
|
|
};
|
|
let trace = local_sim::run_simulation(config);
|
|
assert!(
|
|
local_sim::check_fifo_order(&trace),
|
|
"branches must execute in FIFO queue order"
|
|
);
|
|
}
|