use std::collections::HashSet;
use super::bins::BinValues;
use super::coverpoint::{CovEvent, Coverage, Coverpoint};
use super::cross::Cross;
use super::decl::{
ArrayKind, BinDecl, BinKind, Bound, CoverpointDecl, CrossBinDecl, CrossDecl, Matches,
RangeItem, SelectExpr,
};
use super::options::{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 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 cp_decl(name: &str, ty: TypeInfo, bins: Vec<BinDecl>) -> CoverpointDecl {
let mut d = CoverpointDecl::new(name, ty, Expr::Kernel(0));
d.bins = bins;
d
}
fn make_cp(d: &CoverpointDecl, ev: &mut dyn Eval, ctor: &[Val]) -> Coverpoint {
Coverpoint::new(d, &Options::default(), &TypeOptions::default(), ev, ctor)
.expect("coverpoint builds")
.0
}
fn cp(name: &str, ty: TypeInfo, bins: Vec<BinDecl>) -> Coverpoint {
make_cp(&cp_decl(name, ty, bins), &mut NoKernels, &[])
}
fn try_cross(
d: &CrossDecl,
cps: &[Coverpoint],
ev: &mut dyn Eval,
ctor: &[Val],
) -> Result<(Cross, Vec<String>), CovError> {
let refs: Vec<&Coverpoint> = cps.iter().collect();
Cross::new(
d,
&refs,
&Options::default(),
&TypeOptions::default(),
ev,
ctor,
)
}
fn cross(d: &CrossDecl, cps: &[Coverpoint]) -> Cross {
try_cross(d, cps, &mut NoKernels, &[])
.expect("cross builds")
.0
}
fn feed_ev(
cps: &mut [Coverpoint],
x: &mut Cross,
vals: &[i128],
ev: &mut dyn Eval,
) -> Vec<CovEvent> {
for (c, v) in cps.iter_mut().zip(vals) {
c.sample_value(&Val::Int(*v), ev, &[]).unwrap();
}
let refs: Vec<&Coverpoint> = cps.iter().collect();
x.sample(&refs, ev, &[]).unwrap()
}
fn feed(cps: &mut [Coverpoint], x: &mut Cross, vals: &[i128]) -> Vec<CovEvent> {
feed_ev(cps, x, vals, &mut NoKernels)
}
fn binsof(cp: &str) -> SelectExpr {
SelectExpr::Binsof {
cp: cp.to_string(),
bin: None,
intersect: None,
}
}
fn binsof_bin(cp: &str, bin: &str) -> SelectExpr {
SelectExpr::Binsof {
cp: cp.to_string(),
bin: Some(bin.to_string()),
intersect: None,
}
}
fn binsof_in(cp: &str, ranges: Vec<RangeItem>) -> SelectExpr {
SelectExpr::Binsof {
cp: cp.to_string(),
bin: None,
intersect: Some(ranges),
}
}
fn not(e: SelectExpr) -> SelectExpr {
SelectExpr::Not(Box::new(e))
}
fn and(a: SelectExpr, b: SelectExpr) -> SelectExpr {
SelectExpr::And(Box::new(a), Box::new(b))
}
fn or(a: SelectExpr, b: SelectExpr) -> SelectExpr {
SelectExpr::Or(Box::new(a), Box::new(b))
}
fn with(sel: SelectExpr, kernel: KernelId, matches: Matches) -> SelectExpr {
SelectExpr::With {
sel: Box::new(sel),
expr: Expr::Kernel(kernel),
matches,
}
}
fn normal(name: &str, select: SelectExpr) -> CrossBinDecl {
CrossBinDecl::new(name, BinKind::Normal, select)
}
fn tuples(x: &Cross, bin: &str) -> Vec<String> {
x.tuples_of(bin).unwrap_or_else(|| panic!("no bin {bin}"))
}
fn auto_names(x: &Cross) -> Vec<String> {
x.auto_bins().into_iter().map(|(n, _)| n).collect()
}
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_of(v: &Val) -> i128 {
match v {
Val::Int(i) => *i,
o => panic!("not an int: {o:?}"),
}
}
fn b2i(b: bool) -> Val {
Val::Int(b as i128)
}
#[test]
fn cross_is_the_cartesian_product_of_all_bins() {
let ty = TypeInfo::unsigned(4);
let cps = [cp("a", ty, vec![]), cp("b", ty, vec![])];
let x = cross(&CrossDecl::new("aXb", &["a", "b"]), &cps);
assert_eq!(x.space(), 256);
assert_eq!(
x.coverage(),
Coverage {
covered: 0,
total: 256
}
);
let names = auto_names(&x);
assert_eq!(names.len(), 256);
assert_eq!(names[0], "<auto[0],auto[0]>");
assert_eq!(names[1], "<auto[0],auto[1]>");
assert_eq!(names[255], "<auto[15],auto[15]>");
}
#[test]
fn cross_of_variable_and_explicit_coverpoint_makes_160_bins() {
let cps = [
cp("b_var", TypeInfo::unsigned(4), vec![]),
cp("A", TypeInfo::unsigned(32), vec![open("yy", vec![r(0, 9)])]),
];
let x = cross(&CrossDecl::new("CC", &["b_var", "A"]), &cps);
let names = auto_names(&x);
assert_eq!(names.len(), 160);
assert_eq!(names[0], "<auto[0],yy[0]>");
assert_eq!(names[1], "<auto[0],yy[1]>");
assert_eq!(names[10], "<auto[1],yy[0]>");
assert_eq!(names[159], "<auto[15],yy[9]>");
}
#[test]
fn default_ignore_and_illegal_bins_make_no_cross_products() {
let ty = TypeInfo::unsigned(4);
let cps = [
cp(
"a",
ty,
vec![
bin("lo", vec![r(0, 3)]),
bin("ign", vec![r(4, 5)]).kind(BinKind::Ignore),
bin("ill", vec![r(6, 7)]).kind(BinKind::Illegal),
BinDecl::default_bin("rest"),
],
),
cp(
"b",
ty,
vec![bin("one", vec![s(1)]), bin("two", vec![s(2)])],
),
];
let x = cross(&CrossDecl::new("c", &["a", "b"]), &cps);
assert_eq!(auto_names(&x), ["<lo,one>", "<lo,two>"]);
}
#[test]
fn sampling_counts_the_tuple_of_the_matching_bins() {
let ty = TypeInfo::unsigned(4);
let mut cps = [
cp(
"a",
ty,
vec![bin("lo", vec![r(0, 7)]), bin("hi", vec![r(8, 15)])],
),
cp(
"b",
ty,
vec![bin("lo", vec![r(0, 7)]), bin("hi", vec![r(8, 15)])],
),
];
let mut x = cross(&CrossDecl::new("c", &["a", "b"]), &cps);
feed(&mut cps, &mut x, &[1, 9]);
feed(&mut cps, &mut x, &[2, 12]);
feed(&mut cps, &mut x, &[9, 9]);
assert_eq!(x.hits("<lo,hi>"), Some(2));
assert_eq!(x.hits("<hi,hi>"), Some(1));
assert_eq!(x.hits("<lo,lo>"), Some(0));
assert_eq!(x.hits("<nope>"), None);
assert_eq!(
x.coverage(),
Coverage {
covered: 2,
total: 4
}
);
assert_eq!(x.samples, 3);
}
fn ct_cps() -> [Coverpoint; 2] {
let ty = TypeInfo::signed(32);
[
cp("i", ty, vec![open("i", vec![r(0, 1)])]),
cp("j", ty, vec![open("j", vec![r(0, 1)])]),
]
}
#[test]
fn ct_example_x1_has_four_auto_bins() {
let cps = ct_cps();
let x = cross(&CrossDecl::new("x1", &["i", "j"]), &cps);
let mut names = auto_names(&x);
names.sort();
assert_eq!(
names,
["<i[0],j[0]>", "<i[0],j[1]>", "<i[1],j[0]>", "<i[1],j[1]>"]
);
}
#[test]
fn ct_example_x2_keeps_auto_bins_outside_the_user_bin() {
let mut cps = ct_cps();
let d = CrossDecl::new("x2", &["i", "j"]).bin(normal("i_zero", binsof_in("i", vec![s(0)])));
let mut x = cross(&d, &cps);
assert_eq!(tuples(&x, "i_zero"), ["<i[0],j[0]>", "<i[0],j[1]>"]);
assert_eq!(auto_names(&x), ["<i[1],j[0]>", "<i[1],j[1]>"]);
assert_eq!(x.coverage().total, 3);
feed(&mut cps, &mut x, &[0, 1]);
feed(&mut cps, &mut x, &[1, 1]);
assert_eq!(x.hits("i_zero"), Some(1));
assert_eq!(x.hits("<i[1],j[1]>"), Some(1));
assert_eq!(x.hits("<i[0],j[1]>"), None);
assert_eq!(
x.coverage(),
Coverage {
covered: 2,
total: 3
}
);
}
#[test]
fn cross_retain_auto_bins_zero_drops_the_automatic_bins() {
let cps = ct_cps();
let mut d = CrossDecl::new("x2", &["i", "j"]).bin(normal("i_zero", binsof_in("i", vec![s(0)])));
d.options
.push(("cross_retain_auto_bins".to_string(), Expr::int(0)));
let x = cross(&d, &cps);
assert!(auto_names(&x).is_empty());
assert_eq!(x.coverage().total, 1);
}
fn abcd_cps() -> [Coverpoint; 2] {
let ty = TypeInfo::unsigned(8);
[
cp(
"a",
ty,
vec![
bin("a1", vec![r(0, 63)]),
bin("a2", vec![r(64, 127)]),
bin("a3", vec![r(128, 191)]),
bin("a4", vec![r(192, 255)]),
],
),
cp(
"b",
ty,
vec![
bin("b1", vec![s(0)]),
bin("b2", vec![r(1, 84)]),
bin("b3", vec![r(85, 169)]),
bin("b4", vec![r(170, 255)]),
],
),
]
}
fn c_decl() -> CrossDecl {
CrossDecl::new("c", &["a", "b"])
.bin(normal("c1", not(binsof_in("a", vec![r(100, 200)]))))
.bin(normal(
"c2",
or(binsof_bin("a", "a2"), binsof_bin("b", "b2")),
))
.bin(normal(
"c3",
and(binsof_bin("a", "a1"), binsof_bin("b", "b4")),
))
}
#[test]
fn lrm_19_6_1_1_select_expressions() {
let cps = abcd_cps();
let x = cross(&c_decl(), &cps);
assert_eq!(
tuples(&x, "c1"),
["<a1,b1>", "<a1,b2>", "<a1,b3>", "<a1,b4>"]
);
assert_eq!(
tuples(&x, "c2"),
[
"<a1,b2>", "<a2,b1>", "<a2,b2>", "<a2,b3>", "<a2,b4>", "<a3,b2>", "<a4,b2>"
]
);
assert_eq!(tuples(&x, "c3"), ["<a1,b4>"]);
assert_eq!(
auto_names(&x),
[
"<a3,b1>", "<a3,b3>", "<a3,b4>", "<a4,b1>", "<a4,b3>", "<a4,b4>"
]
);
assert_eq!(x.coverage().total, 9);
}
#[test]
fn lrm_19_6_1_1_sampling_counts_each_bin_once_per_sample() {
let mut cps = abcd_cps();
let mut x = cross(&c_decl(), &cps);
feed(&mut cps, &mut x, &[70, 80]);
feed(&mut cps, &mut x, &[10, 200]);
feed(&mut cps, &mut x, &[130, 0]);
assert_eq!(x.hits("c1"), Some(1));
assert_eq!(x.hits("c2"), Some(1));
assert_eq!(x.hits("c3"), Some(1));
assert_eq!(x.hits("<a3,b1>"), Some(1));
assert_eq!(
x.coverage(),
Coverage {
covered: 4,
total: 9
}
);
}
#[test]
fn a_bin_with_several_matching_products_counts_once_per_sample() {
let ty = TypeInfo::unsigned(4);
let mut cps = [
cp(
"a",
ty,
vec![bin("a1", vec![s(5)]), bin("a2", vec![r(5, 6)])],
),
cp("b", ty, vec![bin("b1", vec![s(0)])]),
];
let d = CrossDecl::new("c", &["a", "b"]).bin(normal("all", binsof("a")));
let mut x = cross(&d, &cps);
feed(&mut cps, &mut x, &[5, 0]);
assert_eq!(x.hits("all"), Some(1));
feed(&mut cps, &mut x, &[6, 0]);
assert_eq!(x.hits("all"), Some(2));
}
#[test]
fn binsof_names_an_array_by_its_base_name() {
let ty = TypeInfo::unsigned(4);
let cps = [
cp("a", ty, vec![bin("only", vec![s(0)])]),
cp(
"b",
ty,
vec![open("two", vec![r(0, 2)]), bin("other", vec![s(9)])],
),
];
let d = CrossDecl::new("c", &["a", "b"])
.bin(normal("arr", binsof_bin("b", "two")))
.bin(normal("one", binsof_bin("b", "two[1]")));
let x = cross(&d, &cps);
assert_eq!(
tuples(&x, "arr"),
["<only,two[0]>", "<only,two[1]>", "<only,two[2]>"]
);
assert_eq!(tuples(&x, "one"), ["<only,two[1]>"]);
}
#[test]
fn binsof_with_intersect_keeps_bins_that_share_a_value() {
let ty = TypeInfo::unsigned(3);
let cps = [
cp("a", ty, vec![open("a", vec![r(0, 7)])]),
cp("b", ty, vec![bin("b", vec![r(0, 1)])]),
];
let d = CrossDecl::new("c", &["a", "b"])
.bin(normal("hit", binsof_in("a", vec![s(5), r(1, 3)])))
.bin(normal("miss", not(binsof_in("a", vec![s(5), r(1, 3)]))));
let x = cross(&d, &cps);
assert_eq!(
tuples(&x, "hit"),
["<a[1],b>", "<a[2],b>", "<a[3],b>", "<a[5],b>"]
);
assert_eq!(
tuples(&x, "miss"),
["<a[0],b>", "<a[4],b>", "<a[6],b>", "<a[7],b>"]
);
}
#[test]
fn intersect_on_a_wide_bin_overlaps_by_range() {
let cps = abcd_cps();
let d = CrossDecl::new("c", &["a", "b"]).bin(normal("w", binsof_in("a", vec![s(64)])));
let x = cross(&d, &cps);
assert_eq!(tuples(&x, "w").len(), 4);
assert!(tuples(&x, "w").iter().all(|t| t.starts_with("<a2,")));
}
#[test]
fn binsof_on_a_transition_bin_uses_the_last_value() {
let ty = TypeInfo::unsigned(4);
let cps = [
cp(
"t",
ty,
vec![BinDecl::trans(
"up",
vec![vec![
super::decl::TransItem::new(vec![s(1)]),
super::decl::TransItem::new(vec![s(2)]),
]],
)],
),
cp("b", ty, vec![bin("b", vec![s(0)])]),
];
let d = CrossDecl::new("c", &["t", "b"])
.bin(normal("last", binsof_in("t", vec![s(2)])))
.bin(normal("first", binsof_in("t", vec![s(1)])));
let x = cross(&d, &cps);
assert_eq!(tuples(&x, "last"), ["<up,b>"]);
assert!(tuples(&x, "first").is_empty());
assert!(x.bins().iter().find(|b| b.name == "first").unwrap().empty);
assert_eq!(x.coverage().total, 1);
}
#[test]
fn a_transition_bin_forms_a_tuple_when_the_sequence_completes() {
let ty = TypeInfo::unsigned(4);
let mut cps = [
cp(
"t",
ty,
vec![BinDecl::trans(
"up",
vec![vec![
super::decl::TransItem::new(vec![s(1)]),
super::decl::TransItem::new(vec![s(2)]),
]],
)],
),
cp("b", ty, vec![bin("b", vec![s(0)])]),
];
let mut x = cross(&CrossDecl::new("c", &["t", "b"]), &cps);
feed(&mut cps, &mut x, &[1, 0]);
assert_eq!(x.hits("<up,b>"), Some(0));
feed(&mut cps, &mut x, &[2, 0]);
assert_eq!(x.hits("<up,b>"), Some(1));
}
fn apple_cps() -> [Coverpoint; 2] {
let ty = TypeInfo::unsigned(8);
let mut ev = Fn1(|id, args: &[Val]| match id {
1 => b2i(int_of(&args[0]) % 2 == 0),
2 => b2i(int_of(&args[0]) % 3 == 0),
other => panic!("kernel {other}"),
});
let a = cp(
"a",
ty,
vec![open("low", vec![r(0, 127)]), bin("high", vec![r(128, 255)])],
);
let b = make_cp(
&cp_decl(
"b",
ty,
vec![
open("two", vec![r(0, 255)]).with(Expr::Kernel(1)),
open("three", vec![r(0, 255)]).with(Expr::Kernel(2)),
],
),
&mut ev,
&[],
);
[a, b]
}
fn apple_ev() -> Fn1<impl FnMut(KernelId, &[Val]) -> Val> {
Fn1(|id, args: &[Val]| {
let (a, b) = (int_of(&args[0]), int_of(&args[1]));
match id {
10 => b2i(a + b < 257),
11 => b2i(a == b),
12 => b2i(b > 12),
13 => b2i(a & b & 0xFF != 0),
other => panic!("kernel {other}"),
}
})
}
#[test]
fn lrm_19_6_1_2_apple_selects_three_bin_tuples() {
let cps = apple_cps();
let d = CrossDecl::new("X", &["a", "b"]).bin(normal(
"apple",
with(SelectExpr::All, 10, Matches::Count(Expr::int(127))),
));
let (x, _) = try_cross(&d, &cps, &mut apple_ev(), &[]).unwrap();
assert_eq!(
tuples(&x, "apple"),
["<high,two[0]>", "<high,two[2]>", "<high,three[0]>"]
);
}
#[test]
fn lrm_19_6_1_2_cherry_selects_where_a_equals_b() {
let cps = apple_cps();
let sel = with(
and(
binsof_in("b", vec![r(0, 50)]),
SelectExpr::Binsof {
cp: "a".to_string(),
bin: Some("low".to_string()),
intersect: Some(vec![r(0, 50)]),
},
),
11,
Matches::one(),
);
let d = CrossDecl::new("X", &["a", "b"]).bin(normal("cherry", sel));
let (x, _) = try_cross(&d, &cps, &mut apple_ev(), &[]).unwrap();
let t = tuples(&x, "cherry");
assert_eq!(t.len(), 26 + 17);
assert!(t.contains(&"<low[0],two[0]>".to_string()));
assert!(t.contains(&"<low[0],three[0]>".to_string()));
assert!(t.contains(&"<low[48],two[48]>".to_string()));
assert!(t.contains(&"<low[48],three[48]>".to_string()));
assert!(t.contains(&"<low[50],two[50]>".to_string()));
assert!(!t.contains(&"<low[50],three[50]>".to_string()));
assert!(!t.contains(&"<low[1],two[0]>".to_string()));
}
#[test]
fn lrm_19_6_1_2_plum_unites_two_with_selections() {
let cps = apple_cps();
let sel = or(
with(binsof_bin("b", "two"), 12, Matches::one()),
with(binsof_bin("a", "low"), 13, Matches::one()),
);
let d = CrossDecl::new("X", &["a", "b"]).bin(normal("plum", sel));
let (x, _) = try_cross(&d, &cps, &mut apple_ev(), &[]).unwrap();
let mut a_bins: Vec<(String, i128)> = (0..128).map(|k| (format!("low[{k}]"), k)).collect();
a_bins.push(("high".to_string(), 128));
let mut b_bins: Vec<(String, i128)> = Vec::new();
for v in (0..256).step_by(2) {
b_bins.push((format!("two[{v}]"), v));
}
for v in (0..256).step_by(3) {
b_bins.push((format!("three[{v}]"), v));
}
let mut want: HashSet<String> = HashSet::new();
for (an, ak) in &a_bins {
for (bn, bv) in &b_bins {
let first = bn.starts_with("two") && *bv > 12;
let second = an.starts_with("low") && (ak & bv & 0xFF) != 0;
if first || second {
want.insert(format!("<{an},{bn}>"));
}
}
}
let got: HashSet<String> = tuples(&x, "plum").into_iter().collect();
assert_eq!(got.len(), want.len());
assert_eq!(got, want);
assert!(got.contains("<high,two[14]>"));
assert!(!got.contains("<high,two[12]>"));
assert!(got.contains("<low[1],three[3]>"));
assert!(got.contains("<low[2],three[3]>"));
assert!(!got.contains("<low[4],three[3]>"));
}
#[test]
fn with_dollar_requires_every_value_tuple_to_satisfy() {
let ty = TypeInfo::unsigned(4);
let cps = [
cp(
"a",
ty,
vec![bin("lo", vec![r(0, 3)]), bin("mid", vec![r(2, 5)])],
),
cp("b", ty, vec![bin("hi", vec![r(4, 7)])]),
];
let mut ev = Fn1(|_, args: &[Val]| b2i(int_of(&args[0]) < int_of(&args[1])));
let all =
CrossDecl::new("c", &["a", "b"]).bin(normal("all", with(SelectExpr::All, 1, Matches::All)));
let (x, _) = try_cross(&all, &cps, &mut ev, &[]).unwrap();
assert_eq!(tuples(&x, "all"), ["<lo,hi>"]);
let any = CrossDecl::new("c", &["a", "b"])
.bin(normal("any", with(SelectExpr::All, 1, Matches::one())));
let (x, _) = try_cross(&any, &cps, &mut ev, &[]).unwrap();
assert_eq!(tuples(&x, "any"), ["<lo,hi>", "<mid,hi>"]);
}
#[test]
fn with_kernel_receives_constructor_arguments_first() {
let ty = TypeInfo::unsigned(4);
let cps = [
cp("a", ty, vec![open("a", vec![r(0, 3)])]),
cp("b", ty, vec![open("b", vec![r(0, 3)])]),
];
let mut ev = Fn1(|_, args: &[Val]| {
assert_eq!(args.len(), 3);
b2i(int_of(&args[1]) + int_of(&args[2]) == int_of(&args[0]))
});
let d = CrossDecl::new("c", &["a", "b"])
.bin(normal("sum", with(SelectExpr::All, 1, Matches::one())));
let (x, _) = try_cross(&d, &cps, &mut ev, &[Val::Int(3)]).unwrap();
assert_eq!(
tuples(&x, "sum"),
["<a[0],b[3]>", "<a[1],b[2]>", "<a[2],b[1]>", "<a[3],b[0]>"]
);
}
#[test]
fn matches_must_be_positive() {
let cps = abcd_cps();
let d = CrossDecl::new("c", &["a", "b"]).bin(normal(
"z",
with(SelectExpr::All, 1, Matches::Count(Expr::int(0))),
));
let mut ev = Fn1(|_, _: &[Val]| b2i(true));
assert!(matches!(
try_cross(&d, &cps, &mut ev, &[]),
Err(CovError::CrossSelect { .. })
));
}
#[test]
fn with_over_a_huge_space_is_a_named_error_not_a_hang() {
let ty = TypeInfo::unsigned(32);
let cps = [cp("a", ty, vec![]), cp("b", ty, vec![])];
let d =
CrossDecl::new("c", &["a", "b"]).bin(normal("w", with(SelectExpr::All, 1, Matches::All)));
let mut ev = Fn1(|_, _: &[Val]| b2i(true));
assert!(matches!(
try_cross(&d, &cps, &mut ev, &[]),
Err(CovError::TooManyValues { .. })
));
}
fn set_expr(pairs: &[(i128, i128)]) -> Expr {
Expr::Const(Val::List(
pairs
.iter()
.map(|&(x, y)| Val::List(vec![Val::Int(x), Val::Int(y)]))
.collect(),
))
}
fn set_sel(pairs: &[(i128, i128)], matches: Matches) -> SelectExpr {
SelectExpr::Set {
expr: set_expr(pairs),
matches,
}
}
#[test]
fn lrm_19_6_1_4_array_literal_defines_the_bin_tuples() {
let cps = [
cp("a", TypeInfo::signed(32), vec![open("x", vec![r(0, 10)])]),
cp("b", TypeInfo::unsigned(8), vec![open("y", vec![r(0, 20)])]),
];
let d = CrossDecl::new("aXb", &["a", "b"]).bin(normal(
"one",
set_sel(&[(1, 2), (3, 4), (5, 6)], Matches::one()),
));
let x = cross(&d, &cps);
assert_eq!(
tuples(&x, "one"),
["<x[1],y[2]>", "<x[3],y[4]>", "<x[5],y[6]>"]
);
}
#[test]
fn lrm_19_6_1_4_function_built_queues_for_wide_coverpoints() {
let ty = TypeInfo::unsigned(32);
let cps = [cp("a", ty, vec![]), cp("b", ty, vec![])];
let first = "<auto[0:67108863],auto[0:67108863]>";
let one = &[(0, 0), (1, 1), (2, 2)];
let two = &[(0, 0), (2, 2), (4, 4)];
let d = CrossDecl::new("aXb", &["a", "b"])
.bin(normal("one", set_sel(one, Matches::one())))
.bin(normal("two", set_sel(two, Matches::one())))
.bin(normal("one3", set_sel(one, Matches::Count(Expr::int(3)))))
.bin(normal("one4", set_sel(one, Matches::Count(Expr::int(4)))))
.bin(normal("oneall", set_sel(one, Matches::All)));
let x = cross(&d, &cps);
assert_eq!(tuples(&x, "one"), [first]);
assert_eq!(tuples(&x, "two"), [first]);
assert_eq!(tuples(&x, "one3"), [first]);
assert!(tuples(&x, "one4").is_empty());
assert!(tuples(&x, "oneall").is_empty());
}
#[test]
fn set_expression_tuples_are_deduplicated_and_unknown_ones_skipped() {
let ty = TypeInfo::unsigned(32);
let cps = [cp("a", ty, vec![]), cp("b", ty, vec![])];
let list = Expr::Const(Val::List(vec![
Val::List(vec![Val::Int(1), Val::Int(1)]),
Val::List(vec![Val::Int(1), Val::Int(1)]),
Val::List(vec![Val::Unknown, Val::Int(2)]),
]));
let d = CrossDecl::new("c", &["a", "b"]).bin(normal(
"dup",
SelectExpr::Set {
expr: list,
matches: Matches::Count(Expr::int(2)),
},
));
let x = cross(&d, &cps);
assert!(tuples(&x, "dup").is_empty());
}
#[test]
fn set_expression_values_outside_every_bin_select_nothing() {
let ty = TypeInfo::unsigned(8);
let cps = [
cp("a", ty, vec![bin("a", vec![r(0, 3)])]),
cp("b", ty, vec![bin("b", vec![r(0, 3)])]),
];
let d = CrossDecl::new("c", &["a", "b"])
.bin(normal("out", set_sel(&[(1, 9), (9, 1)], Matches::one())));
let x = cross(&d, &cps);
assert!(tuples(&x, "out").is_empty());
}
#[test]
fn set_expression_must_be_a_list_of_tuples_of_the_right_width() {
let ty = TypeInfo::unsigned(8);
let cps = [
cp("a", ty, vec![bin("a", vec![r(0, 3)])]),
cp("b", ty, vec![bin("b", vec![r(0, 3)])]),
];
let wrong = CrossDecl::new("c", &["a", "b"]).bin(normal(
"bad",
SelectExpr::Set {
expr: Expr::Const(Val::List(vec![Val::List(vec![Val::Int(1)])])),
matches: Matches::one(),
},
));
assert!(matches!(
try_cross(&wrong, &cps, &mut NoKernels, &[]),
Err(CovError::CrossSelect { .. })
));
let scalar = CrossDecl::new("c", &["a", "b"]).bin(normal(
"bad",
SelectExpr::Set {
expr: Expr::int(3),
matches: Matches::one(),
},
));
assert!(matches!(
try_cross(&scalar, &cps, &mut NoKernels, &[]),
Err(CovError::CrossSelect { .. })
));
}
#[test]
fn ignore_bins_exclude_products_from_everything() {
let ty = TypeInfo::unsigned(3);
let mut cps = [
cp("a", ty, vec![open("a", vec![r(0, 7)])]),
cp("b", ty, vec![bin("b", vec![r(0, 1)])]),
];
let d = CrossDecl::new("yy", &["a", "b"])
.bin(CrossBinDecl::new(
"ignore",
BinKind::Ignore,
binsof_in("a", vec![s(5), r(1, 3)]),
))
.bin(normal("hi", binsof_in("a", vec![r(4, 7)])));
let mut x = cross(&d, &cps);
assert_eq!(tuples(&x, "hi"), ["<a[4],b>", "<a[6],b>", "<a[7],b>"]);
assert_eq!(auto_names(&x), ["<a[0],b>"]);
assert_eq!(x.coverage().total, 2);
feed(&mut cps, &mut x, &[5, 0]);
feed(&mut cps, &mut x, &[2, 0]);
assert_eq!(x.hits("hi"), Some(0));
assert_eq!(x.coverage().covered, 0);
feed(&mut cps, &mut x, &[6, 1]);
assert_eq!(x.hits("hi"), Some(1));
}
#[test]
fn a_user_bin_left_with_no_products_is_excluded_from_coverage() {
let ty = TypeInfo::unsigned(3);
let cps = [
cp("a", ty, vec![open("a", vec![r(0, 1)])]),
cp("b", ty, vec![bin("b", vec![r(0, 1)])]),
];
let d = CrossDecl::new("c", &["a", "b"])
.bin(CrossBinDecl::new(
"ign",
BinKind::Ignore,
binsof_bin("a", "a[0]"),
))
.bin(normal("gone", binsof_bin("a", "a[0]")))
.bin(normal("kept", binsof_bin("a", "a[1]")));
let x = cross(&d, &cps);
let gone = x.bins().iter().find(|b| b.name == "gone").unwrap();
assert!(gone.empty);
assert_eq!(gone.tuple_count(), 0);
assert_eq!(x.coverage().total, 1);
}
fn illegal_fixture() -> ([Coverpoint; 2], CrossDecl) {
let ty = TypeInfo::unsigned(4);
let cps = [
cp("x", ty, vec![open("x", vec![r(0, 3)])]),
cp("y", ty, vec![open("y", vec![r(0, 3)])]),
];
let d = CrossDecl::new("zz", &["x", "y"]).bin(CrossBinDecl::new(
"illegal",
BinKind::Illegal,
binsof_in("y", vec![s(2)]),
));
(cps, d)
}
#[test]
fn illegal_cross_product_is_a_runtime_error_and_not_covered() {
let (mut cps, d) = illegal_fixture();
let mut x = cross(&d, &cps);
assert_eq!(x.coverage().total, 12);
let ev = feed(&mut cps, &mut x, &[1, 2]);
assert_eq!(
ev,
[CovEvent::IllegalBin {
coverpoint: "zz".to_string(),
bin: "illegal".to_string(),
value: "<x[1],y[2]>".to_string(),
}]
);
assert_eq!(x.hits("illegal"), Some(1));
assert_eq!(x.coverage().covered, 0);
assert!(feed(&mut cps, &mut x, &[1, 1]).is_empty());
}
#[test]
fn illegal_takes_precedence_over_ignore_and_user_bins() {
let (mut cps, d) = illegal_fixture();
let d = d
.bin(CrossBinDecl::new(
"ign",
BinKind::Ignore,
binsof_in("y", vec![r(2, 3)]),
))
.bin(normal("usr", binsof_in("y", vec![r(1, 2)])));
let mut x = cross(&d, &cps);
let ev = feed(&mut cps, &mut x, &[0, 2]);
assert_eq!(ev.len(), 1);
assert_eq!(x.hits("usr"), Some(0));
assert!(feed(&mut cps, &mut x, &[0, 3]).is_empty());
assert_eq!(x.hits("usr"), Some(0));
feed(&mut cps, &mut x, &[0, 1]);
assert_eq!(x.hits("usr"), Some(1));
}
#[test]
fn illegal_bin_value_comes_from_a_constructor_argument() {
let ty = TypeInfo::unsigned(4);
let mut cps = [
cp("x", ty, vec![open("x", vec![r(0, 3)])]),
cp("y", ty, vec![open("y", vec![r(0, 3)])]),
];
let d = CrossDecl::new("zz", &["x", "y"]).bin(CrossBinDecl::new(
"illegal",
BinKind::Illegal,
binsof_in("y", vec![RangeItem::Single(Expr::Kernel(5))]),
));
let mut ev = Fn1(|id, args: &[Val]| {
assert_eq!(id, 5);
args[0].clone()
});
let (mut x, _) = try_cross(&d, &cps, &mut ev, &[Val::Int(3)]).unwrap();
assert!(feed(&mut cps, &mut x, &[0, 2]).is_empty());
assert_eq!(feed(&mut cps, &mut x, &[0, 3]).len(), 1);
}
#[test]
fn cross_iff_false_ignores_the_sample() {
let ty = TypeInfo::unsigned(2);
let mut cps = [cp("a", ty, vec![]), cp("b", ty, vec![])];
let mut d = CrossDecl::new("c", &["a", "b"]);
d.iff = Some(Expr::Kernel(7));
let mut en = true;
let mut ev = Fn1(|_, _: &[Val]| b2i(en));
let mut x = try_cross(&d, &cps, &mut ev, &[]).unwrap().0;
feed_ev(&mut cps, &mut x, &[1, 1], &mut ev);
assert_eq!(x.hits("<auto[1],auto[1]>"), Some(1));
en = false;
let mut ev = Fn1(|_, _: &[Val]| b2i(en));
feed_ev(&mut cps, &mut x, &[1, 1], &mut ev);
assert_eq!(x.hits("<auto[1],auto[1]>"), Some(1));
assert_eq!(x.samples, 1);
}
#[test]
fn cross_bin_iff_guards_only_that_bin() {
let ty = TypeInfo::unsigned(2);
let mut cps = [
cp("a", ty, vec![open("a", vec![r(0, 1)])]),
cp("b", ty, vec![bin("b", vec![r(0, 3)])]),
];
let d = CrossDecl::new("c", &["a", "b"])
.bin(normal("guarded", binsof("a")).iff(Expr::Kernel(3)))
.bin(normal("free", binsof("a")));
let mut on = false;
let mut ev = Fn1(|_, _: &[Val]| b2i(on));
let mut x = try_cross(&d, &cps, &mut ev, &[]).unwrap().0;
feed_ev(&mut cps, &mut x, &[0, 0], &mut ev);
on = true;
let mut ev = Fn1(|_, _: &[Val]| b2i(on));
feed_ev(&mut cps, &mut x, &[0, 0], &mut ev);
assert_eq!(x.hits("guarded"), Some(1));
assert_eq!(x.hits("free"), Some(2));
}
#[test]
fn a_coverpoint_without_a_usable_value_forms_no_tuple() {
let ty = TypeInfo::unsigned(4);
let mut a_decl = cp_decl(
"a",
ty,
vec![
bin("lo", vec![r(0, 3)]),
bin("ign", vec![s(4)]).kind(BinKind::Ignore),
bin("ill", vec![s(5)]).kind(BinKind::Illegal),
BinDecl::default_bin("rest"),
],
);
a_decl.iff = Some(Expr::Kernel(9));
let mut en = true;
let mut ev = Fn1(|id, args: &[Val]| match id {
9 => b2i(en),
0 => args[0].clone(),
_ => panic!(),
});
let a = make_cp(&a_decl, &mut ev, &[]);
let b = cp("b", ty, vec![bin("one", vec![r(0, 15)])]);
let mut cps = [a, b];
let mut x = cross(&CrossDecl::new("c", &["a", "b"]), &cps);
for v in [4, 5, 9] {
let _ = feed(&mut cps, &mut x, &[v, 1]);
}
cps[0]
.sample_value(&Val::Unknown, &mut NoKernels, &[])
.unwrap();
cps[1]
.sample_value(&Val::Int(1), &mut NoKernels, &[])
.unwrap();
let refs: Vec<&Coverpoint> = cps.iter().collect();
x.sample(&refs, &mut NoKernels, &[]).unwrap();
assert_eq!(x.hits("<lo,one>"), Some(0));
assert_eq!(x.samples, 0);
en = false;
let mut ev = Fn1(|id, args: &[Val]| match id {
9 => b2i(en),
0 => args[0].clone(),
_ => panic!(),
});
cps[0].sample(&mut ev, &[]).unwrap();
let refs: Vec<&Coverpoint> = cps.iter().collect();
x.sample(&refs, &mut NoKernels, &[]).unwrap();
assert_eq!(x.samples, 0);
feed(&mut cps, &mut x, &[2, 1]);
assert_eq!(x.hits("<lo,one>"), Some(1));
}
#[test]
fn at_least_applies_to_cross_bins() {
let ty = TypeInfo::unsigned(2);
let mut cps = [
cp("a", ty, vec![bin("a", vec![r(0, 3)])]),
cp("b", ty, vec![bin("b", vec![r(0, 3)])]),
];
let mut d = CrossDecl::new("c", &["a", "b"]).bin(normal("u", binsof("a")));
d.options.push(("at_least".to_string(), Expr::int(2)));
let mut x = cross(&d, &cps);
assert_eq!(
x.coverage(),
Coverage {
covered: 0,
total: 1
}
);
feed(&mut cps, &mut x, &[1, 1]);
assert_eq!(x.coverage().covered, 0);
feed(&mut cps, &mut x, &[2, 2]);
assert_eq!(
x.coverage(),
Coverage {
covered: 1,
total: 1
}
);
}
#[test]
fn at_least_applies_to_automatic_cross_bins() {
let ty = TypeInfo::unsigned(1);
let mut cps = [cp("a", ty, vec![]), cp("b", ty, vec![])];
let mut d = CrossDecl::new("c", &["a", "b"]);
d.options.push(("at_least".to_string(), Expr::int(2)));
let mut x = cross(&d, &cps);
feed(&mut cps, &mut x, &[0, 0]);
assert_eq!(
x.coverage(),
Coverage {
covered: 0,
total: 4
}
);
feed(&mut cps, &mut x, &[0, 0]);
assert_eq!(
x.coverage(),
Coverage {
covered: 1,
total: 4
}
);
}
#[test]
fn cross_inherits_at_least_from_the_covergroup() {
let ty = TypeInfo::unsigned(1);
let mut cps = [cp("a", ty, vec![]), cp("b", ty, vec![])];
let cg = Options {
at_least: 3,
..Options::default()
};
let refs: Vec<&Coverpoint> = cps.iter().collect();
let mut x = Cross::new(
&CrossDecl::new("c", &["a", "b"]),
&refs,
&cg,
&TypeOptions::default(),
&mut NoKernels,
&[],
)
.unwrap()
.0;
assert_eq!(x.options.at_least, 3);
for _ in 0..2 {
feed(&mut cps, &mut x, &[1, 1]);
}
assert_eq!(x.coverage().covered, 0);
feed(&mut cps, &mut x, &[1, 1]);
assert_eq!(x.coverage().covered, 1);
}
#[test]
fn reset_zeroes_every_bin() {
let ct = ct_cps();
let mut cps = ct;
let d = CrossDecl::new("x2", &["i", "j"]).bin(normal("i_zero", binsof_in("i", vec![s(0)])));
let mut x = cross(&d, &cps);
feed(&mut cps, &mut x, &[0, 0]);
feed(&mut cps, &mut x, &[1, 0]);
assert_eq!(x.coverage().covered, 2);
x.reset();
assert_eq!(x.coverage().covered, 0);
assert_eq!(x.samples, 0);
}
#[test]
fn cross_of_real_coverpoints_selects_by_interval_overlap() {
let real = TypeInfo::real();
let mut cps = [
cp(
"r",
real,
vec![bin("lo", vec![rr(0.0, 1.0)]), bin("hi", vec![rr(2.0, 3.0)])],
),
cp("n", TypeInfo::unsigned(2), vec![bin("n", vec![r(0, 3)])]),
];
let d = CrossDecl::new("c", &["r", "n"])
.bin(normal("near", binsof_in("r", vec![rr(0.5, 0.6)])))
.bin(normal(
"single",
binsof_in("r", vec![RangeItem::Single(Expr::real(3.0))]),
));
let mut x = cross(&d, &cps);
assert_eq!(tuples(&x, "near"), ["<lo,n>"]);
assert_eq!(tuples(&x, "single"), ["<hi,n>"]);
cps[0]
.sample_value(&Val::Real(2.5), &mut NoKernels, &[])
.unwrap();
cps[1]
.sample_value(&Val::Int(1), &mut NoKernels, &[])
.unwrap();
let refs: Vec<&Coverpoint> = cps.iter().collect();
x.sample(&refs, &mut NoKernels, &[]).unwrap();
assert_eq!(x.hits("single"), Some(1));
assert_eq!(x.hits("near"), Some(0));
}
#[test]
fn with_over_a_real_coverpoint_is_a_named_error() {
let real = TypeInfo::real();
let cps = [
cp("r", real, vec![bin("lo", vec![rr(0.0, 1.0)])]),
cp("n", TypeInfo::unsigned(2), vec![bin("n", vec![r(0, 3)])]),
];
let d =
CrossDecl::new("c", &["r", "n"]).bin(normal("w", with(SelectExpr::All, 1, Matches::one())));
let mut ev = Fn1(|_, _: &[Val]| b2i(true));
assert!(matches!(
try_cross(&d, &cps, &mut ev, &[]),
Err(CovError::Unsupported(_))
));
}
#[test]
fn wildcard_bins_intersect_through_their_expanded_values() {
let ty = TypeInfo::unsigned(2);
let cps = [
cp(
"w",
ty,
vec![
BinDecl::ranges(
"hi",
vec![RangeItem::Single(Expr::Const(Val::Pat {
bits: 0b10,
care: 0b10,
}))],
)
.wildcard(),
bin("lo", vec![r(0, 1)]),
],
),
cp("n", ty, vec![bin("n", vec![r(0, 3)])]),
];
let d = CrossDecl::new("c", &["w", "n"]).bin(normal("three", binsof_in("w", vec![s(3)])));
let x = cross(&d, &cps);
assert_eq!(tuples(&x, "three"), ["<hi,n>"]);
}
#[test]
fn bin_values_of_default_bins_are_rejected() {
let ty = TypeInfo::unsigned(2);
let c = cp("a", ty, vec![BinDecl::default_bin("d")]);
assert!(matches!(
c.bins[0].values(&ty),
Err(CovError::Unsupported(_))
));
let c = cp("a", ty, vec![bin("x", vec![s(1)])]);
assert!(matches!(c.bins[0].values(&ty), Ok(BinValues::Ints(_))));
}
#[test]
fn malformed_crosses_are_named_errors() {
let ty = TypeInfo::unsigned(2);
let cps = [cp("a", ty, vec![]), cp("b", ty, vec![])];
let mk = |d: CrossDecl| try_cross(&d, &cps, &mut NoKernels, &[]);
assert!(matches!(
mk(CrossDecl::new("c", &["a"])),
Err(CovError::CrossSelect { .. })
));
assert!(matches!(
mk(CrossDecl::new("c", &["a", "z"])),
Err(CovError::CrossSelect { .. })
));
let d = CrossDecl::new("c", &["a", "b"]).bin(normal("x", binsof("z")));
assert!(matches!(mk(d), Err(CovError::CrossSelect { .. })));
let d = CrossDecl::new("c", &["a", "b"]).bin(normal("x", binsof_bin("a", "nope")));
assert!(matches!(mk(d), Err(CovError::CrossSelect { .. })));
}
#[test]
fn a_cross_space_over_the_limit_is_a_named_error() {
let ty = TypeInfo::unsigned(12);
let cps = [
cp("a", ty, vec![open("a", vec![r(0, 4095)])]),
cp("b", ty, vec![open("b", vec![r(0, 4095)])]),
];
assert!(matches!(
try_cross(&CrossDecl::new("c", &["a", "b"]), &cps, &mut NoKernels, &[]),
Err(CovError::TooManyValues { .. })
));
}
#[test]
fn three_way_cross_numbers_tuples_first_item_most_significant() {
let ty = TypeInfo::unsigned(1);
let mut cps = [
cp("a", ty, vec![]),
cp("b", ty, vec![]),
cp("c", ty, vec![]),
];
let mut x = cross(&CrossDecl::new("x", &["a", "b", "c"]), &cps);
assert_eq!(x.space(), 8);
let names = auto_names(&x);
assert_eq!(names[0], "<auto[0],auto[0],auto[0]>");
assert_eq!(names[1], "<auto[0],auto[0],auto[1]>");
assert_eq!(names[2], "<auto[0],auto[1],auto[0]>");
assert_eq!(names[7], "<auto[1],auto[1],auto[1]>");
feed(&mut cps, &mut x, &[1, 0, 1]);
assert_eq!(x.hits("<auto[1],auto[0],auto[1]>"), Some(1));
assert_eq!(
x.coverage(),
Coverage {
covered: 1,
total: 8
}
);
}