use std::time::Duration;
use tacet::{helpers::InputPair, timing_test_checked, AttackerModel, Outcome, TimingOracle};
#[test]
fn smoke_test() {
let inputs = InputPair::new(|| 1u32, || 2u32);
let outcome = TimingOracle::for_attacker(AttackerModel::AdjacentNetwork)
.time_budget(Duration::from_secs(5))
.max_samples(100) .warmup(10)
.test(inputs, |x| {
std::hint::black_box(x + 1);
});
match outcome {
Outcome::Pass {
leak_probability,
samples_used,
..
} => {
assert!((0.0..=1.0).contains(&leak_probability));
assert!(samples_used > 0);
}
Outcome::Fail {
leak_probability,
samples_used,
..
} => {
assert!((0.0..=1.0).contains(&leak_probability));
assert!(samples_used > 0);
}
Outcome::Inconclusive {
leak_probability,
samples_used,
..
} => {
assert!((0.0..=1.0).contains(&leak_probability));
assert!(samples_used > 0);
}
Outcome::Unmeasurable { .. } => (),
Outcome::Research(_) => (), }
}
#[test]
fn builder_api() {
let oracle = TimingOracle::for_attacker(AttackerModel::AdjacentNetwork)
.time_budget(Duration::from_secs(30))
.max_samples(1000)
.warmup(100)
.outlier_percentile(0.99);
let config = oracle.config();
assert_eq!(config.max_samples, 1000);
assert_eq!(config.warmup, 100);
}
#[test]
fn convenience_function() {
let inputs = InputPair::new(|| 42u32, || 42u32);
let outcome = TimingOracle::for_attacker(AttackerModel::AdjacentNetwork)
.time_budget(Duration::from_secs(5))
.max_samples(200)
.test(inputs, |x| {
std::hint::black_box(*x);
});
match outcome {
Outcome::Pass {
leak_probability,
samples_used,
..
} => {
assert!((0.0..=1.0).contains(&leak_probability));
assert!(samples_used > 0);
}
Outcome::Fail {
leak_probability, ..
} => {
panic!(
"Unexpected fail for constant-time operation: P={:.1}%",
leak_probability * 100.0
);
}
Outcome::Inconclusive {
leak_probability,
samples_used,
..
} => {
assert!((0.0..=1.0).contains(&leak_probability));
assert!(samples_used > 0);
}
Outcome::Unmeasurable { .. } => (),
Outcome::Research(_) => (),
}
}
#[test]
#[allow(clippy::redundant_closure)]
fn macro_api() {
let outcome = timing_test_checked! {
oracle: TimingOracle::for_attacker(AttackerModel::AdjacentNetwork).time_budget(Duration::from_secs(5)).max_samples(100),
baseline: || 42u32,
sample: || rand::random::<u32>(),
measure: |x| {
std::hint::black_box(*x);
},
};
match outcome {
Outcome::Pass {
leak_probability,
samples_used,
..
} => {
assert!((0.0..=1.0).contains(&leak_probability));
assert!(samples_used > 0);
}
Outcome::Fail {
leak_probability,
samples_used,
..
} => {
assert!((0.0..=1.0).contains(&leak_probability));
assert!(samples_used > 0);
}
Outcome::Inconclusive {
leak_probability,
samples_used,
..
} => {
assert!((0.0..=1.0).contains(&leak_probability));
assert!(samples_used > 0);
}
Outcome::Unmeasurable { .. } => (),
Outcome::Research(_) => (),
}
}
#[test]
fn result_serialization() {
let inputs = InputPair::new(|| (), || ());
let outcome = TimingOracle::for_attacker(AttackerModel::AdjacentNetwork)
.time_budget(Duration::from_secs(5))
.max_samples(100)
.test(inputs, |_| {});
let json = serde_json::to_string(&outcome).expect("Should serialize");
assert!(json.contains("leak_probability") || json.contains("Unmeasurable"));
}