use std::collections::BTreeSet;
use maplit::btreeset;
use crate::Node;
use crate::QuorumTree;
use crate::quorum::QuorumIntersection;
use crate::quorum::verify_intersection;
const UNIVERSE_SIZE: u64 = 5;
pub(super) fn all_configs() -> Vec<BTreeSet<u64>> {
(1..1u64 << UNIVERSE_SIZE)
.map(|mask| (0..UNIVERSE_SIZE).filter(|id| mask & (1 << id) != 0).collect())
.collect()
}
pub(super) fn all_joints() -> Vec<Vec<BTreeSet<u64>>> {
let configs = all_configs();
let singles = configs.iter().map(|config| vec![config.clone()]);
let pairs =
configs.iter().flat_map(|a| configs.iter().map(move |b| vec![a.clone(), b.clone()]));
singles.chain(pairs).collect()
}
#[test]
fn test_intersects_with() -> anyhow::Result<()> {
let s123 = || btreeset! {1,2,3};
let s345 = || btreeset! {3,4,5};
let s789 = || btreeset! {7,8,9};
let j123 = vec![s123()];
let j345 = vec![s345()];
let j123_345 = vec![s123(), s345()];
let j345_789 = vec![s345(), s789()];
assert_eq!(Some(true), j123.intersects_with(&j123));
assert_eq!(Some(false), j123.intersects_with(&j345));
assert_eq!(Some(true), j123.intersects_with(&j123_345));
assert_eq!(None, j123.intersects_with(&j345_789));
assert_eq!(Some(false), j345.intersects_with(&j123));
assert_eq!(Some(true), j345.intersects_with(&j345));
assert_eq!(Some(true), j345.intersects_with(&j123_345));
assert_eq!(Some(true), j345.intersects_with(&j345_789));
assert_eq!(Some(true), j123_345.intersects_with(&j123));
assert_eq!(Some(true), j123_345.intersects_with(&j345));
assert_eq!(Some(true), j123_345.intersects_with(&j123_345));
assert_eq!(Some(true), j123_345.intersects_with(&j345_789));
assert_eq!(None, j345_789.intersects_with(&j123));
assert_eq!(Some(true), j345_789.intersects_with(&j345));
assert_eq!(Some(true), j345_789.intersects_with(&j123_345));
assert_eq!(Some(true), j345_789.intersects_with(&j345_789));
Ok(())
}
#[test]
fn test_intersects_with_empty_joint() -> anyhow::Result<()> {
let empty: Vec<BTreeSet<u64>> = vec![];
let j123 = vec![btreeset! {1,2,3}];
let no_quorum = vec![BTreeSet::new(), btreeset! {1,2,3}];
assert_eq!(Some(false), empty.intersects_with(&j123));
assert_eq!(Some(false), j123.intersects_with(&empty));
assert_eq!(Some(false), empty.intersects_with(&empty));
let empty_with_no_quorum = empty.intersects_with(&no_quorum);
assert_eq!(Some(true), empty_with_no_quorum);
let no_quorum_with_empty = no_quorum.intersects_with(&empty);
assert_eq!(Some(true), no_quorum_with_empty);
Ok(())
}
#[test]
fn test_verify_intersection() {
let s123 = btreeset! {1, 2, 3};
let s12 = btreeset! {1, 2};
let s45 = btreeset! {4, 5};
assert!(verify_intersection(&s123, &s123));
assert!(verify_intersection(&s12, &s123));
assert!(verify_intersection(&s123, &s12));
assert!(!verify_intersection(&s123, &s45));
}
#[test]
fn test_verify_intersection_empty_joint() {
let empty: Vec<BTreeSet<u64>> = vec![];
let j123 = vec![btreeset! {1, 2, 3}];
assert!(!verify_intersection(&empty, &j123));
assert!(!verify_intersection(&empty, &empty));
}
#[test]
fn test_verify_intersection_exact_where_intersects_with_is_conservative() {
let j123_145 = vec![btreeset! {1, 2, 3}, btreeset! {1, 4, 5}];
let j2345 = vec![btreeset! {2, 3, 4, 5}];
let exact = verify_intersection(&j123_145, &j2345);
assert!(exact);
let intersects = j123_145.intersects_with(&j2345);
assert_eq!(None, intersects);
}
#[test]
fn test_intersects_with_agrees_with_verify_intersection() {
let mut joints: Vec<Vec<BTreeSet<u64>>> = vec![
vec![],
vec![BTreeSet::new()],
vec![btreeset! {3, 4, 5}],
vec![btreeset! {1, 2, 3}, btreeset! {3, 4, 5}],
vec![btreeset! {3, 4, 5}, btreeset! {7, 8, 9}],
];
joints.extend(all_joints());
for a in &joints {
for b in &joints {
let Some(intersects) = a.intersects_with(b) else {
continue;
};
let exact = verify_intersection(a, b);
assert_eq!(exact, intersects, "a: {a:?}, b: {b:?}");
}
}
}
#[test]
fn test_verify_intersection_tree_read_write() -> anyhow::Result<()> {
let write = btreeset! {1u64, 2, 3};
let read = QuorumTree::new(1, [
Node::Subtree(QuorumTree::new(2, [Node::Id(2), Node::Id(3)])?),
Node::Subtree(QuorumTree::new(2, [Node::Id(8), Node::Id(9)])?),
])?;
assert!(!verify_intersection(&read, &write));
let read = QuorumTree::new(2, [Node::Id(2), Node::Id(3)])?;
assert!(verify_intersection(&read, &write));
Ok(())
}
#[test]
fn test_btree_set_intersects_with() {
let s123 = btreeset! {1, 2, 3};
let s124 = btreeset! {1, 2, 4};
let s1234 = btreeset! {1, 2, 3, 4};
let s1235 = btreeset! {1, 2, 3, 5};
let added = s123.intersects_with(&s1234);
assert_eq!(Some(true), added);
let replaced_of_four = s1234.intersects_with(&s1235);
assert_eq!(Some(true), replaced_of_four);
let replaced_of_three = s123.intersects_with(&s124);
assert_eq!(Some(false), replaced_of_three);
}
#[test]
fn test_btree_set_intersects_with_agrees_with_verify_intersection() {
let mut configs = all_configs();
configs.push(BTreeSet::new());
for a in &configs {
for b in &configs {
let intersects = a.intersects_with(b);
let exact = verify_intersection(a, b);
assert_eq!(Some(exact), intersects, "a: {a:?}, b: {b:?}");
}
}
}
#[test]
fn test_joint_intersects_with_config() {
let s123 = btreeset! {1, 2, 3};
let s124 = btreeset! {1, 2, 4};
let s1234 = btreeset! {1, 2, 3, 4};
let s2345 = btreeset! {2, 3, 4, 5};
let empty_config = BTreeSet::new();
let j123 = vec![s123.clone()];
let j123_145 = vec![s123.clone(), btreeset! {1, 4, 5}];
let empty_joint: Vec<BTreeSet<u64>> = vec![];
let added = j123.intersects_with(&s1234);
assert_eq!(Some(true), added);
let replaced = j123.intersects_with(&s124);
assert_eq!(Some(false), replaced);
let one_config_intersects = j123_145.intersects_with(&s1234);
assert_eq!(Some(true), one_config_intersects);
let no_config_intersects = j123_145.intersects_with(&s2345);
assert_eq!(None, no_config_intersects);
let empty_joint_with_config = empty_joint.intersects_with(&s123);
assert_eq!(Some(false), empty_joint_with_config);
let empty_joint_with_empty_config = empty_joint.intersects_with(&empty_config);
assert_eq!(Some(true), empty_joint_with_empty_config);
let config_with_joint = s124.intersects_with(&j123);
assert_eq!(Some(false), config_with_joint);
}
#[test]
fn test_joint_intersects_with_config_agrees_with_verify_intersection() {
let mut joints = all_joints();
joints.push(vec![]);
let mut configs = all_configs();
configs.push(BTreeSet::new());
for joint in &joints {
for config in &configs {
let intersects = joint.intersects_with(config);
let reversed = config.intersects_with(joint);
assert_eq!(intersects, reversed, "joint: {joint:?}, config: {config:?}");
let exact = verify_intersection(joint, config);
let at_most_one_config = joint.len() <= 1;
if at_most_one_config {
assert_eq!(
Some(exact),
intersects,
"joint: {joint:?}, config: {config:?}"
);
continue;
}
let Some(intersects) = intersects else {
continue;
};
assert_eq!(exact, intersects, "joint: {joint:?}, config: {config:?}");
}
}
}