use super::bins::{Matcher, RealItem};
use super::coverpoint::{CovEvent, Coverage, Coverpoint};
use super::decl::{ArrayKind, BinBody, BinDecl, BinKind, Bound, CoverpointDecl, RangeItem};
use super::intset::IntSet;
use super::options::{Level, Options, TypeOptions};
use super::value::{CovError, Eval, Expr, KernelId, NoKernels, TypeInfo, Val};
fn r(lo: i128, hi: i128) -> RangeItem {
RangeItem::Range(Bound::Expr(Expr::int(lo)), Bound::Expr(Expr::int(hi)))
}
fn s(v: i128) -> RangeItem {
RangeItem::Single(Expr::int(v))
}
fn rr(lo: f64, hi: f64) -> RangeItem {
RangeItem::Range(Bound::Expr(Expr::real(lo)), Bound::Expr(Expr::real(hi)))
}
fn dollar_to(hi: i128) -> RangeItem {
RangeItem::Range(Bound::Dollar, Bound::Expr(Expr::int(hi)))
}
fn to_dollar(lo: i128) -> RangeItem {
RangeItem::Range(Bound::Expr(Expr::int(lo)), Bound::Dollar)
}
fn bin(name: &str, items: Vec<RangeItem>) -> BinDecl {
BinDecl::ranges(name, items)
}
fn open(name: &str, items: Vec<RangeItem>) -> BinDecl {
BinDecl::ranges(name, items).array(ArrayKind::Open)
}
fn fixed(name: &str, n: i128, items: Vec<RangeItem>) -> BinDecl {
BinDecl::ranges(name, items).array(ArrayKind::Fixed(Expr::int(n)))
}
fn decl(ty: TypeInfo, bins: Vec<BinDecl>) -> CoverpointDecl {
let mut d = CoverpointDecl::new("cp", ty, Expr::Kernel(0));
d.bins = bins;
d
}
fn build_with(
d: &CoverpointDecl,
ev: &mut dyn Eval,
ctor: &[Val],
) -> Result<(Coverpoint, Vec<String>), CovError> {
Coverpoint::new(d, &Options::default(), &TypeOptions::default(), ev, ctor)
}
fn build(d: &CoverpointDecl) -> (Coverpoint, Vec<String>) {
build_with(d, &mut NoKernels, &[]).expect("coverpoint builds")
}
fn hit(cp: &mut Coverpoint, v: i128) -> Vec<CovEvent> {
cp.sample_value(&Val::Int(v), &mut NoKernels, &[]).unwrap()
}
fn hit_real(cp: &mut Coverpoint, v: f64) -> Vec<CovEvent> {
cp.sample_value(&Val::Real(v), &mut NoKernels, &[]).unwrap()
}
fn names(cp: &Coverpoint) -> Vec<&str> {
cp.bins.iter().map(|b| b.name.as_str()).collect()
}
fn ints(cp: &Coverpoint, name: &str) -> Vec<(i128, i128)> {
match &cp.bins.iter().find(|b| b.name == name).unwrap().matcher {
Matcher::Ints(s) => s.intervals().to_vec(),
other => panic!("{name} is not an integral bin: {other:?}"),
}
}
fn reals(cp: &Coverpoint, name: &str) -> Vec<RealItem> {
match &cp.bins.iter().find(|b| b.name == name).unwrap().matcher {
Matcher::Reals(i) => i.clone(),
other => panic!("{name} is not a real bin: {other:?}"),
}
}
struct Fn1<F: FnMut(KernelId, &[Val]) -> Val>(F);
impl<F: FnMut(KernelId, &[Val]) -> Val> Eval for Fn1<F> {
fn eval(&mut self, id: KernelId, args: &[Val]) -> Result<Val, CovError> {
Ok((self.0)(id, args))
}
}
fn int_arg(args: &[Val], i: usize) -> i128 {
match args[i] {
Val::Int(v) => v,
ref o => panic!("arg {i} is {o:?}"),
}
}
#[test]
fn single_bin_covers_its_range_list_and_counts_hits() {
let d = decl(
TypeInfo::unsigned(10),
vec![bin("a", vec![r(0, 63), s(65)])],
);
let (mut cp, w) = build(&d);
assert!(w.is_empty());
assert_eq!(names(&cp), ["a"]);
assert_eq!(ints(&cp, "a"), [(0, 63), (65, 65)]);
hit(&mut cp, 5);
hit(&mut cp, 65);
hit(&mut cp, 64);
assert_eq!(cp.hits("a"), Some(2));
}
#[test]
fn open_array_makes_one_bin_per_distinct_value() {
let d = decl(
TypeInfo::unsigned(10),
vec![open("b", vec![r(127, 150), r(148, 191)])],
);
let (cp, _) = build(&d);
assert_eq!(cp.bins.len(), 65);
assert_eq!(cp.bins[0].name, "b[127]");
assert_eq!(cp.bins[64].name, "b[191]");
let d = decl(
TypeInfo::unsigned(10),
vec![open("c", vec![s(200), s(201), s(202)])],
);
let (cp, _) = build(&d);
assert_eq!(names(&cp), ["c[200]", "c[201]", "c[202]"]);
}
#[test]
fn dollar_is_the_type_maximum_and_minimum() {
let d = decl(
TypeInfo::unsigned(10),
vec![bin("d", vec![to_dollar(1000)])],
);
let (cp, _) = build(&d);
assert_eq!(ints(&cp, "d"), [(1000, 1023)]);
let d = decl(TypeInfo::signed(4), vec![bin("lo", vec![dollar_to(-6)])]);
let (cp, _) = build(&d);
assert_eq!(ints(&cp, "lo"), [(-8, -6)]);
}
#[test]
fn fixed_bins_distribute_values_in_order() {
let d = decl(
TypeInfo::unsigned(8),
vec![fixed("fixed", 4, vec![r(1, 10), s(1), s(4), s(7)])],
);
let (cp, _) = build(&d);
assert_eq!(names(&cp), ["fixed[0]", "fixed[1]", "fixed[2]", "fixed[3]"]);
assert_eq!(ints(&cp, "fixed[0]"), [(1, 3)]);
assert_eq!(ints(&cp, "fixed[1]"), [(4, 6)]);
assert_eq!(ints(&cp, "fixed[2]"), [(7, 9)]);
assert_eq!(ints(&cp, "fixed[3]"), [(1, 1), (4, 4), (7, 7), (10, 10)]);
let (mut cp, _) = build(&d);
hit(&mut cp, 4);
assert_eq!(cp.hits("fixed[1]"), Some(1));
assert_eq!(cp.hits("fixed[3]"), Some(1));
}
#[test]
fn fixed_bins_with_more_bins_than_values_leave_empty_bins() {
let d = decl(
TypeInfo::unsigned(8),
vec![fixed("fixed", 5, vec![s(1), s(4), s(7)])],
);
let (mut cp, _) = build(&d);
assert_eq!(cp.bins.len(), 5);
assert_eq!(ints(&cp, "fixed[3]"), []);
assert!(cp.bins[3].empty && cp.bins[4].empty);
assert!(!cp.bins[0].empty);
assert_eq!(
cp.coverage(),
Coverage {
covered: 0,
total: 3
}
);
hit(&mut cp, 4);
assert_eq!(
cp.coverage(),
Coverage {
covered: 1,
total: 3
}
);
}
#[test]
fn fixed_bins_last_bin_takes_the_remainder() {
let d = decl(TypeInfo::unsigned(8), vec![fixed("f", 3, vec![r(0, 9)])]);
let (cp, _) = build(&d);
assert_eq!(ints(&cp, "f[0]"), [(0, 2)]);
assert_eq!(ints(&cp, "f[1]"), [(3, 5)]);
assert_eq!(ints(&cp, "f[2]"), [(6, 9)]);
}
#[test]
fn fixed_bin_count_must_be_positive() {
let d = decl(TypeInfo::unsigned(8), vec![fixed("f", 0, vec![r(0, 9)])]);
let err = build_with(&d, &mut NoKernels, &[]).unwrap_err();
assert!(
matches!(err, CovError::NonPositiveBins { n: 0, .. }),
"{err:?}"
);
}
#[test]
fn huge_open_array_is_a_named_error_not_a_hang() {
let d = decl(
TypeInfo::unsigned(32),
vec![open("b", vec![r(0, 0xFFFF_FFFF)])],
);
let err = build_with(&d, &mut NoKernels, &[]).unwrap_err();
assert!(matches!(err, CovError::TooManyValues { .. }), "{err:?}");
}
#[test]
fn constructor_arguments_reach_bin_ranges() {
let d = decl(
TypeInfo::signed(32),
vec![BinDecl::ranges(
"good",
vec![RangeItem::Range(
Bound::Expr(Expr::Kernel(1)),
Bound::Expr(Expr::Kernel(2)),
)],
)],
);
let mk = |lo: i128, hi: i128| {
let mut ev = Fn1(|id, args: &[Val]| match id {
1 => Val::Int(int_arg(args, 0)),
2 => Val::Int(int_arg(args, 1)),
_ => panic!("kernel {id}"),
});
build_with(&d, &mut ev, &[Val::Int(lo), Val::Int(hi)])
.unwrap()
.0
};
assert_eq!(ints(&mk(0, 50), "good"), [(0, 50)]);
assert_eq!(ints(&mk(120, 600), "good"), [(120, 600)]);
}
#[test]
fn default_bin_catches_the_rest_and_is_not_covered() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 3)]),
BinDecl::default_bin("others").array(ArrayKind::Open),
],
);
let (mut cp, _) = build(&d);
hit(&mut cp, 1);
hit(&mut cp, 5);
hit(&mut cp, 5);
hit(&mut cp, 9);
assert_eq!(cp.hits("a"), Some(1));
let others = cp.bins.iter().find(|b| b.name == "others").unwrap();
assert_eq!(others.hits, 3);
assert_eq!(others.default_values.get(&5), Some(&2));
assert_eq!(others.default_values.get(&9), Some(&1));
assert_eq!(
cp.coverage(),
Coverage {
covered: 1,
total: 1
}
);
}
#[test]
fn a_lone_default_bin_suppresses_auto_bins() {
let d = decl(TypeInfo::unsigned(3), vec![BinDecl::default_bin("d")]);
let (cp, _) = build(&d);
assert_eq!(names(&cp), ["d"]);
assert_eq!(
cp.coverage(),
Coverage {
covered: 0,
total: 0
}
);
}
#[test]
fn bin_iff_guards_the_count() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 7)]).iff(Expr::Kernel(5)),
bin("b", vec![r(0, 7)]),
],
);
let en = std::cell::Cell::new(true);
let (mut cp, _) = build(&d);
let mut ev = Fn1(|id, _: &[Val]| {
assert_eq!(id, 5);
Val::Int(en.get() as i128)
});
cp.sample_value(&Val::Int(2), &mut ev, &[]).unwrap();
en.set(false);
cp.sample_value(&Val::Int(2), &mut ev, &[]).unwrap();
assert_eq!(cp.hits("a"), Some(1));
assert_eq!(cp.hits("b"), Some(2));
}
#[test]
fn coverpoint_iff_false_means_not_sampled() {
let mut d = decl(TypeInfo::unsigned(4), vec![bin("a", vec![r(0, 15)])]);
d.iff = Some(Expr::Kernel(2));
d.expr = Expr::Kernel(1);
let (mut cp, _) = build(&d);
let mut ev = Fn1(|id, args: &[Val]| match id {
1 => args[0].clone(),
2 => args[1].clone(),
_ => panic!(),
});
cp.sample(&mut ev, &[Val::Int(3), Val::Int(0)]).unwrap();
assert_eq!(cp.hits("a"), Some(0));
assert_eq!(cp.samples, 0);
cp.sample(&mut ev, &[Val::Int(3), Val::Int(1)]).unwrap();
assert_eq!(cp.hits("a"), Some(1));
assert_eq!(cp.samples, 1);
}
#[test]
fn x_or_z_sample_matches_no_bin_not_even_default() {
let d = decl(
TypeInfo::unsigned(4),
vec![bin("a", vec![r(0, 3)]), BinDecl::default_bin("d")],
);
let (mut cp, _) = build(&d);
cp.sample_value(&Val::Unknown, &mut NoKernels, &[]).unwrap();
assert_eq!(cp.hits("a"), Some(0));
assert_eq!(cp.hits("d"), Some(0));
}
#[test]
fn auto_bins_one_per_value_when_few() {
let d = decl(TypeInfo::unsigned(3), vec![]);
let (cp, _) = build(&d);
assert_eq!(cp.bins.len(), 8);
assert_eq!(cp.bins[0].name, "auto[0]");
assert_eq!(cp.bins[7].name, "auto[7]");
}
#[test]
fn auto_bins_remainder_goes_to_the_last_bin() {
let mut d = decl(TypeInfo::unsigned(3), vec![]);
d.options.push(("auto_bin_max".into(), Expr::int(3)));
let (cp, _) = build(&d);
assert_eq!(names(&cp), ["auto[0:1]", "auto[2:3]", "auto[4:7]"]);
assert_eq!(ints(&cp, "auto[4:7]"), [(4, 7)]);
}
#[test]
fn auto_bin_max_defaults_to_64_and_comes_from_the_covergroup() {
let d = decl(TypeInfo::unsigned(8), vec![]);
let (cp, _) = build(&d);
assert_eq!(cp.bins.len(), 64);
assert_eq!(cp.bins[0].name, "auto[0:3]");
assert_eq!(cp.bins[63].name, "auto[252:255]");
let mut cg = Options::default();
cg.set(Level::Covergroup, "auto_bin_max", &Val::Int(4))
.unwrap();
let (cp, _) = Coverpoint::new(&d, &cg, &TypeOptions::default(), &mut NoKernels, &[]).unwrap();
assert_eq!(cp.bins.len(), 4);
assert_eq!(cp.options.auto_bin_max, 4);
}
#[test]
fn auto_bins_of_an_enum_are_named_after_the_enumerators() {
let mut d = decl(TypeInfo::unsigned(2), vec![]);
d.enum_values = Some(vec![
("RED".into(), 0),
("GREEN".into(), 1),
("BLUE".into(), 3),
]);
let (mut cp, _) = build(&d);
assert_eq!(names(&cp), ["auto[RED]", "auto[GREEN]", "auto[BLUE]"]);
hit(&mut cp, 3);
hit(&mut cp, 2); assert_eq!(cp.hits("auto[BLUE]"), Some(1));
assert_eq!(
cp.coverage(),
Coverage {
covered: 1,
total: 3
}
);
}
#[test]
fn auto_bins_skip_two_state_excluded_values_and_signed_domain() {
let d = decl(TypeInfo::signed(3), vec![]);
let (cp, _) = build(&d);
assert_eq!(cp.bins[0].name, "auto[-4]");
assert_eq!(cp.bins[7].name, "auto[3]");
}
#[test]
fn auto_bins_with_only_ignore_bins_ct_example() {
let mut d = decl(
TypeInfo::unsigned(3),
vec![bin("ign", vec![r(0, 1), r(5, 6)]).kind(BinKind::Ignore)],
);
d.options.push(("auto_bin_max".into(), Expr::int(4)));
let (mut cp, _) = build(&d);
let empty: Vec<_> = cp
.bins
.iter()
.filter(|b| b.empty)
.map(|b| b.name.as_str())
.collect();
assert_eq!(empty, ["auto[0:1]"]);
assert_eq!(cp.coverage().total, 3);
for v in [0, 1, 5, 6] {
hit(&mut cp, v);
}
assert_eq!(cp.coverage().covered, 0, "ignored values count nowhere");
hit(&mut cp, 4);
hit(&mut cp, 7);
assert_eq!(
cp.coverage(),
Coverage {
covered: 2,
total: 3
}
);
}
#[test]
fn ignore_bins_remove_values_from_other_bins() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 7)]),
bin("ign", vec![r(2, 3)]).kind(BinKind::Ignore),
fixed("f", 2, vec![r(0, 3)]),
],
);
let (mut cp, _) = build(&d);
hit(&mut cp, 2);
assert_eq!(cp.hits("a"), Some(0));
assert_eq!(cp.hits("f[0]"), Some(0));
hit(&mut cp, 1);
assert_eq!(cp.hits("a"), Some(1));
assert_eq!(cp.hits("f[0]"), Some(1));
assert!(cp.bins.iter().find(|b| b.name == "f[1]").unwrap().empty);
assert_eq!(cp.coverage().total, 2);
}
#[test]
fn ignored_values_are_not_caught_by_default() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 3)]),
bin("ign", vec![r(8, 9)]).kind(BinKind::Ignore),
BinDecl::default_bin("d"),
],
);
let (mut cp, _) = build(&d);
hit(&mut cp, 8);
hit(&mut cp, 12);
assert_eq!(cp.hits("d"), Some(1));
}
#[test]
fn illegal_bins_raise_an_event_and_exclude_the_value() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 15)]),
bin("bad", vec![r(13, 15)]).kind(BinKind::Illegal),
],
);
let (mut cp, _) = build(&d);
assert!(hit(&mut cp, 4).is_empty());
assert_eq!(
hit(&mut cp, 14),
[CovEvent::IllegalBin {
coverpoint: "cp".into(),
bin: "bad".into(),
value: "14".into()
}]
);
assert_eq!(cp.hits("a"), Some(1));
assert_eq!(
cp.coverage(),
Coverage {
covered: 1,
total: 1
}
);
}
#[test]
fn illegal_bin_with_false_iff_is_silent() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 15)]),
bin("bad", vec![r(13, 15)])
.kind(BinKind::Illegal)
.iff(Expr::int(0)),
],
);
let (mut cp, _) = build(&d);
assert!(hit(&mut cp, 14).is_empty());
assert_eq!(cp.hits("a"), Some(0), "still removed from other bins");
}
#[test]
fn illegal_default_reports_unmatched_values() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 3)]),
BinDecl::default_bin("rest").kind(BinKind::Illegal),
],
);
let (mut cp, _) = build(&d);
assert!(hit(&mut cp, 1).is_empty());
assert_eq!(hit(&mut cp, 9).len(), 1);
}
#[test]
fn ignoring_the_default_bin_is_rejected() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 3)]),
BinDecl::default_bin("d").kind(BinKind::Ignore),
],
);
assert!(matches!(
build_with(&d, &mut NoKernels, &[]),
Err(CovError::Unsupported(_))
));
}
#[test]
fn wildcard_bin_treats_x_z_as_dont_care() {
let d = decl(
TypeInfo::unsigned(4),
vec![
bin(
"g",
vec![RangeItem::Single(Expr::Const(Val::Pat {
bits: 0b1100,
care: 0b1100,
}))],
)
.wildcard(),
],
);
let (mut cp, _) = build(&d);
for v in [11, 12, 13, 14, 15, 0] {
hit(&mut cp, v);
}
assert_eq!(cp.hits("g"), Some(4));
}
#[test]
fn wildcard_array_expands_one_bin_per_matching_value() {
let d = decl(
TypeInfo::unsigned(3),
vec![
open(
"w",
vec![RangeItem::Single(Expr::Const(Val::Pat {
bits: 0b100,
care: 0b101,
}))],
)
.wildcard(),
],
);
let (cp, _) = build(&d);
assert_eq!(names(&cp), ["w[4]", "w[6]"]);
}
#[test]
fn wildcard_fixed_bins_distribute_the_expanded_values() {
let d = decl(
TypeInfo::unsigned(3),
vec![
fixed(
"w",
2,
vec![RangeItem::Single(Expr::Const(Val::Pat {
bits: 0,
care: 0b100,
}))],
)
.wildcard(),
],
);
let (cp, _) = build(&d);
assert_eq!(ints(&cp, "w[0]"), [(0, 1)]);
assert_eq!(ints(&cp, "w[1]"), [(2, 3)]);
}
#[test]
fn unsigned_negative_and_xz_bins_are_dropped_with_warnings() {
let d = decl(
TypeInfo::unsigned(4),
vec![bin(
"b",
vec![s(1), RangeItem::Single(Expr::Const(Val::Unknown)), s(-3)],
)],
);
let (cp, w) = build(&d);
assert_eq!(ints(&cp, "b"), [(1, 1)]);
assert_eq!(w.len(), 2, "{w:?}");
}
#[test]
fn value_resolution_examples_b1_to_b4() {
let b1 = bin("b1", vec![s(1), r(2, 5), r(6, 10)]);
let b2 = bin("b2", vec![s(-1), r(1, 10), s(15)]);
let (cp, w) = build(&decl(TypeInfo::unsigned(3), vec![b1.clone(), b2.clone()]));
assert_eq!(ints(&cp, "b1"), [(1, 7)], "b1 = {{1,[2:5],[6:7]}}");
assert_eq!(ints(&cp, "b2"), [(1, 7)], "b2 = {{[1:7]}}");
assert_eq!(
w.len(),
1 + 3,
"one for [6:10], then -1, [1:10] and 15: {w:?}"
);
let b3 = bin("b3", vec![s(1), r(2, 5), r(6, 10)]);
let b4 = bin("b4", vec![s(-1), r(1, 10), s(15)]);
let (cp, w) = build(&decl(TypeInfo::signed(3), vec![b3, b4]));
assert_eq!(ints(&cp, "b3"), [(1, 3)], "b3 = {{1,[2:3]}}");
assert_eq!(ints(&cp, "b4"), [(-1, -1), (1, 3)], "b4 = {{-1,[1:3]}}");
assert_eq!(w.len(), 2 + 2, "{w:?}");
}
#[test]
fn range_entirely_outside_the_type_does_not_participate() {
let (cp, w) = build(&decl(
TypeInfo::unsigned(3),
vec![bin("b", vec![s(1), r(20, 30)])],
));
assert_eq!(ints(&cp, "b"), [(1, 1)]);
assert_eq!(w.len(), 1);
}
#[test]
fn reversed_range_is_empty_with_a_warning() {
let (cp, w) = build(&decl(
TypeInfo::unsigned(4),
vec![bin("b", vec![s(1), r(9, 5)])],
));
assert_eq!(ints(&cp, "b"), [(1, 1)]);
assert_eq!(w.len(), 1);
}
#[test]
fn tolerance_ranges_truncate_and_swap() {
let tol = |c: i128, t: Expr, rel: bool| RangeItem::Tolerance {
center: Expr::int(c),
tol: t,
relative: rel,
};
let d = decl(
TypeInfo::signed(8),
vec![
bin("abs", vec![tol(10, Expr::int(2), false)]),
bin("abs_real", vec![tol(10, Expr::real(2.7), false)]),
bin("rel", vec![tol(7, Expr::int(25), true)]),
bin("rel_neg", vec![tol(-7, Expr::int(25), true)]),
],
);
let (cp, _) = build(&d);
assert_eq!(ints(&cp, "abs"), [(8, 12)]);
assert_eq!(ints(&cp, "abs_real"), [(8, 12)]);
assert_eq!(ints(&cp, "rel"), [(6, 8)]);
assert_eq!(ints(&cp, "rel_neg"), [(-8, -6)]);
}
fn mod3() -> Expr {
Expr::Kernel(9)
}
fn mod3_ev() -> impl Eval {
Fn1(|id, args: &[Val]| {
assert_eq!(id, 9);
Val::Int((int_arg(args, args.len() - 1) % 3 == 0) as i128)
})
}
#[test]
fn with_clause_filters_the_range_list() {
let d = decl(
TypeInfo::unsigned(8),
vec![open("mod3", vec![r(0, 255)]).with(mod3())],
);
let (mut cp, _) = build_with(&d, &mut mod3_ev(), &[]).unwrap();
assert_eq!(cp.bins.len(), 86);
assert_eq!(cp.bins[0].name, "mod3[0]");
assert_eq!(cp.bins[85].name, "mod3[255]");
hit(&mut cp, 9);
hit(&mut cp, 10);
assert_eq!(cp.hits("mod3[9]"), Some(1));
}
#[test]
fn with_clause_sees_constructor_arguments_before_item() {
let d = decl(
TypeInfo::unsigned(8),
vec![bin("m", vec![r(0, 20)]).with(Expr::Kernel(4))],
);
let mut ev = Fn1(|_, args: &[Val]| {
let (k, item) = (int_arg(args, 0), int_arg(args, 1));
Val::Int((item % k == 0) as i128)
});
let (cp, _) = build_with(&d, &mut ev, &[Val::Int(7)]).unwrap();
assert_eq!(ints(&cp, "m"), [(0, 0), (7, 7), (14, 14)]);
}
#[test]
fn with_filters_before_distribution_unless_distribute_first() {
let d = decl(
TypeInfo::unsigned(4),
vec![fixed("f", 2, vec![r(1, 7)]).with(Expr::Kernel(1))],
);
let ne1 = |_: KernelId, args: &[Val]| Val::Int((int_arg(args, 0) != 1) as i128);
let (cp, _) = build_with(&d, &mut Fn1(ne1), &[]).unwrap();
assert_eq!(ints(&cp, "f[0]"), [(2, 4)]);
assert_eq!(ints(&cp, "f[1]"), [(5, 7)]);
let mut t = TypeOptions::default();
t.set(Level::Covergroup, "distribute_first", &Val::Int(1))
.unwrap();
let (cp, _) = Coverpoint::new(&d, &Options::default(), &t, &mut Fn1(ne1), &[]).unwrap();
assert_eq!(ints(&cp, "f[0]"), [(2, 3)]);
assert_eq!(ints(&cp, "f[1]"), [(4, 7)]);
}
#[test]
fn with_preserves_duplicates_and_order_in_fixed_bins() {
let d = decl(
TypeInfo::unsigned(4),
vec![fixed("f", 2, vec![s(5), s(2), s(5), s(9)]).with(Expr::Kernel(1))],
);
let (cp, _) = build_with(
&d,
&mut Fn1(|_, a: &[Val]| Val::Int((int_arg(a, 0) != 9) as i128)),
&[],
)
.unwrap();
assert_eq!(ints(&cp, "f[0]"), [(5, 5)]);
assert_eq!(ints(&cp, "f[1]"), [(2, 2), (5, 5)]);
}
#[test]
fn set_expression_defines_the_bin_from_an_array() {
let d = decl(
TypeInfo::unsigned(4),
vec![BinDecl {
name: "s".into(),
kind: BinKind::Normal,
array: ArrayKind::Open,
wildcard: false,
iff: None,
body: BinBody::Set(Expr::Kernel(3)),
}],
);
let mut ev = Fn1(|_, args: &[Val]| {
Val::List(vec![
Val::Int(4),
Val::Int(1),
Val::Int(int_arg(args, 0)),
Val::Int(99),
])
});
let (cp, w) = build_with(&d, &mut ev, &[Val::Int(7)]).unwrap();
assert_eq!(names(&cp), ["s[4]", "s[1]", "s[7]"]);
assert_eq!(w.len(), 1, "99 is not a 4-bit value");
}
fn real_cp(bins: Vec<BinDecl>, interval: f64) -> Coverpoint {
let d = decl(TypeInfo::real(), bins);
let mut t = TypeOptions::default();
t.set(Level::Covergroup, "real_interval", &Val::Real(interval))
.unwrap();
Coverpoint::new(&d, &Options::default(), &t, &mut NoKernels, &[])
.unwrap()
.0
}
#[test]
fn real_ranges_divide_into_interval_bins() {
let cp = real_cp(vec![open("a3", vec![rr(1.0, 5.0)])], 1.0);
assert_eq!(
names(&cp),
["a3[1.0:2.0)", "a3[2.0:3.0)", "a3[3.0:4.0)", "a3[4.0:5.0]"]
);
}
#[test]
fn real_range_not_wider_than_the_interval_is_one_closed_bin() {
let cp = real_cp(
vec![
open("a1", vec![rr(0.75, 0.85)]),
open("a4", vec![rr(1.0, 2.0)]),
],
1.0,
);
assert_eq!(names(&cp), ["a1[0.75:0.85]", "a4[1.0:2.0]"]);
}
#[test]
fn real_fixed_bins_a2_has_empty_tail_bins() {
let mut cp = real_cp(vec![fixed("a2", 3, vec![rr(0.75, 0.85)])], 1.0);
assert_eq!(names(&cp), ["a2[0]", "a2[1]", "a2[2]"]);
assert!(!cp.bins[0].empty && cp.bins[1].empty && cp.bins[2].empty);
hit_real(&mut cp, 0.8);
assert_eq!(cp.hits("a2[0]"), Some(1));
assert_eq!(
cp.coverage(),
Coverage {
covered: 1,
total: 1
}
);
}
#[test]
fn real_small_interval_b1_has_ten_bins() {
let mut cp = real_cp(vec![open("b1", vec![rr(0.75, 0.85)])], 0.01);
assert_eq!(cp.bins.len(), 10);
assert_eq!(cp.bins[0].name, "b1[0.75:0.76)");
assert_eq!(cp.bins[1].name, "b1[0.76:0.77)");
assert_eq!(cp.bins[9].name, "b1[0.84:0.85]");
hit_real(&mut cp, 0.85);
hit_real(&mut cp, 0.76);
assert_eq!(cp.hits("b1[0.84:0.85]"), Some(1));
assert_eq!(cp.hits("b1[0.76:0.77)"), Some(1));
assert_eq!(cp.hits("b1[0.75:0.76)"), Some(0));
}
#[test]
fn real_bin_counts_b2_to_b5() {
let tol = |c: f64, t: f64| RangeItem::Tolerance {
center: Expr::real(c),
tol: Expr::real(t),
relative: false,
};
let cp = real_cp(
vec![
open("b2", vec![rr(0.75, 0.85), rr(0.90, 0.92)]),
open(
"b3",
vec![rr(0.75, 0.80), RangeItem::Single(Expr::real(0.902))],
),
open(
"b4",
vec![rr(0.75, 0.80), RangeItem::Single(Expr::real(0.752))],
),
open(
"b5",
vec![
rr(0.75, 0.85),
tol(0.753, 0.01),
RangeItem::Single(Expr::real(0.902)),
],
),
],
0.01,
);
let count = |p: &str| cp.bins.iter().filter(|b| b.name.starts_with(p)).count();
assert_eq!(
(count("b2["), count("b3["), count("b4["), count("b5[")),
(12, 6, 6, 13)
);
assert!(names(&cp).contains(&"b5[0.743:0.753)"));
assert!(names(&cp).contains(&"b5[0.753:0.763]"));
assert!(names(&cp).contains(&"b5[0.902]"));
}
#[test]
fn real_open_array_merges_identical_intervals_a3_example() {
let d = RangeItem::Tolerance {
center: Expr::real(1.5),
tol: Expr::real(0.5),
relative: false,
};
let cp = real_cp(
vec![open(
"a3",
vec![
rr(1.0, 4.0),
rr(2.0, 5.0),
rr(1.1, 3.1),
RangeItem::Single(Expr::real(3.3)),
d,
],
)],
1.0,
);
assert_eq!(
names(&cp),
[
"a3[1.0:2.0)",
"a3[2.0:3.0)",
"a3[3.0:4.0]",
"a3[3.0:4.0)",
"a3[4.0:5.0]",
"a3[1.1:2.1)",
"a3[2.1:3.1]",
"a3[3.3]",
"a3[1.0:2.0]",
]
);
}
#[test]
fn real_fixed_bins_distribute_items_like_integers() {
let cp = real_cp(
vec![fixed(
"fixed",
4,
vec![
rr(1.0, 4.0),
rr(2.0, 5.0),
RangeItem::Single(Expr::real(1.0)),
RangeItem::Single(Expr::real(4.0)),
RangeItem::Single(Expr::real(7.0)),
],
)],
1.0,
);
let n = |i: usize| reals(&cp, &format!("fixed[{i}]")).len();
assert_eq!((n(0), n(1), n(2), n(3)), (2, 2, 2, 3));
let g1 = reals(&cp, "fixed[1]");
assert!(g1[0].hi_closed && g1[0].lo == 3.0 && g1[0].hi == 4.0);
assert!(!g1[1].hi_closed && g1[1].lo == 2.0);
}
#[test]
fn real_dollar_range_is_a_single_undivided_bin() {
let cp = real_cp(vec![open("a", vec![dollar_real(0.75)])], 1.0);
assert_eq!(cp.bins.len(), 1);
let cp = real_cp(
vec![open(
"a",
vec![RangeItem::Single(Expr::real(3.0)), real_to_dollar(0.75)],
)],
1.0,
);
assert_eq!(cp.bins.len(), 2);
let cp = real_cp(
vec![fixed(
"a",
4,
vec![RangeItem::Single(Expr::real(3.0)), real_to_dollar(0.75)],
)],
1.0,
);
assert_eq!(cp.bins.len(), 4);
assert_eq!(cp.bins.iter().filter(|b| b.empty).count(), 2);
}
fn dollar_real(hi: f64) -> RangeItem {
RangeItem::Range(Bound::Dollar, Bound::Expr(Expr::real(hi)))
}
fn real_to_dollar(lo: f64) -> RangeItem {
RangeItem::Range(Bound::Expr(Expr::real(lo)), Bound::Dollar)
}
#[test]
fn real_cvp_a_example_tolerances_and_default() {
let highz = RangeItem::Tolerance {
center: Expr::real(-100.0),
tol: Expr::real(0.1),
relative: false,
};
let x_st = RangeItem::Tolerance {
center: Expr::int(100),
tol: Expr::real(1.0),
relative: true,
};
let mut cp = real_cp(
vec![
bin("highz", vec![highz]),
bin("x_st", vec![x_st]),
bin("a5", vec![real_to_dollar(0.1)]),
BinDecl::default_bin("a6"),
],
1.0,
);
for v in [-100.05, -99.95, 99.0, 100.5, 101.0, 101.5, 0.1, 50.0, -3.0] {
hit_real(&mut cp, v);
}
assert_eq!(cp.hits("highz"), Some(2));
assert_eq!(cp.hits("x_st"), Some(3), "99.0, 100.5 and 101.0 inclusive");
assert_eq!(
cp.hits("a5"),
Some(6),
"every sample at or above 0.1, x_st ones too"
);
assert_eq!(cp.hits("a6"), Some(1), "only -3.0 is in no bin");
}
#[test]
fn real_coverpoint_needs_explicit_bins() {
let d = decl(TypeInfo::real(), vec![]);
assert!(matches!(
build_with(&d, &mut NoKernels, &[]),
Err(CovError::RealNeedsBins { .. })
));
}
#[test]
fn real_default_bin_cannot_be_an_array() {
let d = decl(
TypeInfo::real(),
vec![
bin("a", vec![rr(0.0, 1.0)]),
BinDecl::default_bin("d").array(ArrayKind::Open),
],
);
assert!(matches!(
build_with(&d, &mut NoKernels, &[]),
Err(CovError::RealDefaultArray { .. })
));
}
#[test]
fn real_coverpoints_reject_with_and_wildcard() {
let w = decl(
TypeInfo::real(),
vec![bin("a", vec![rr(0.0, 1.0)]).with(Expr::int(1))],
);
assert!(matches!(
build_with(&w, &mut NoKernels, &[]),
Err(CovError::RealUnsupported { .. })
));
let w = decl(
TypeInfo::real(),
vec![bin("a", vec![rr(0.0, 1.0)]).wildcard()],
);
assert!(matches!(
build_with(&w, &mut NoKernels, &[]),
Err(CovError::RealUnsupported { .. })
));
}
#[test]
fn integer_bins_on_a_real_coverpoint_are_cast_to_real() {
let mut cp = real_cp(vec![bin("xstate", vec![s(100)])], 1.0);
hit_real(&mut cp, 100.0);
hit_real(&mut cp, 100.0001);
assert_eq!(cp.hits("xstate"), Some(1));
}
#[test]
fn at_least_sets_the_covered_threshold_and_inherits_from_the_covergroup() {
let d = decl(
TypeInfo::unsigned(2),
vec![bin("a", vec![r(0, 1)]), bin("b", vec![r(2, 3)])],
);
let mut cg = Options::default();
cg.set(Level::Covergroup, "at_least", &Val::Int(2)).unwrap();
let (mut cp, _) =
Coverpoint::new(&d, &cg, &TypeOptions::default(), &mut NoKernels, &[]).unwrap();
hit(&mut cp, 0);
assert_eq!(cp.coverage().covered, 0);
hit(&mut cp, 1);
assert_eq!(
cp.coverage(),
Coverage {
covered: 1,
total: 2
}
);
let mut d2 = d.clone();
d2.options.push(("at_least".into(), Expr::int(1)));
let (mut cp, _) =
Coverpoint::new(&d2, &cg, &TypeOptions::default(), &mut NoKernels, &[]).unwrap();
hit(&mut cp, 0);
assert_eq!(cp.coverage().covered, 1);
}
#[test]
fn coverpoint_options_are_checked_by_level() {
let mut d = decl(TypeInfo::unsigned(2), vec![bin("a", vec![r(0, 1)])]);
d.options
.push(("cross_num_print_missing".into(), Expr::int(1)));
assert!(matches!(
build_with(&d, &mut NoKernels, &[]),
Err(CovError::OptionLevel { .. })
));
}
#[test]
fn detect_overlap_warns_for_overlapping_bins() {
let mut d = decl(
TypeInfo::unsigned(4),
vec![
bin("a", vec![r(0, 5)]),
bin("b", vec![r(5, 9)]),
bin("c", vec![r(10, 12)]),
],
);
let (_, w) = build(&d);
assert!(w.is_empty());
d.options.push(("detect_overlap".into(), Expr::int(1)));
let (_, w) = build(&d);
assert_eq!(w.len(), 1, "{w:?}");
assert!(w[0].contains("a") && w[0].contains("b"));
}
#[test]
fn coverage_percent_follows_19_11() {
assert_eq!(
Coverage {
covered: 1,
total: 4
}
.percent(),
Some(25.0)
);
assert_eq!(
Coverage {
covered: 0,
total: 0
}
.percent(),
None
);
}
#[test]
fn bin_count_with_nothing_to_cover_is_zero_over_zero() {
let d = decl(
TypeInfo::unsigned(2),
vec![bin("i", vec![r(0, 3)]).kind(BinKind::Ignore)],
);
let (cp, _) = build(&d);
assert!(cp.bins.iter().all(|b| b.kind != BinKind::Normal || b.empty));
assert_eq!(
cp.coverage(),
Coverage {
covered: 0,
total: 0
}
);
}
#[test]
fn coverpoint_wider_than_64_bits_is_a_named_error() {
let d = decl(TypeInfo::unsigned(65), vec![bin("a", vec![s(1)])]);
assert!(matches!(
build_with(&d, &mut NoKernels, &[]),
Err(CovError::WidthTooLarge { .. })
));
}
#[test]
fn sampled_values_wrap_to_the_coverpoint_type() {
let d = decl(TypeInfo::unsigned(4), vec![bin("a", vec![s(3)])]);
let (mut cp, _) = build(&d);
hit(&mut cp, 19);
assert_eq!(cp.hits("a"), Some(1));
let d = decl(TypeInfo::signed(4), vec![bin("n", vec![s(-1)])]);
let (mut cp, _) = build(&d);
hit(&mut cp, 15);
assert_eq!(cp.hits("n"), Some(1));
}
#[test]
fn intset_is_used_for_matching_wide_values() {
let d = decl(
TypeInfo::unsigned(64),
vec![bin("big", vec![to_dollar(1 << 40)])],
);
let (mut cp, _) = build(&d);
hit(&mut cp, 1 << 50);
hit(&mut cp, 5);
assert_eq!(cp.hits("big"), Some(1));
assert_eq!(
IntSet::from_intervals([(1i128 << 40, u64::MAX as i128)]).count(),
(u64::MAX as u128) - (1u128 << 40) + 1
);
}