mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-15 12:39:31 +03:00
Rollup merge of #153759 - Zalathar:chunk-tests, r=wesleywiser
Add a suite of ChunkedBitSet union/subtract/intersect test scenarios While working on https://github.com/rust-lang/rust/pull/153754, I found that these code paths are under-tested.
This commit is contained in:
@@ -362,6 +362,116 @@ fn chunked_bitset() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Additional helper methods for testing.
|
||||
impl ChunkedBitSet<usize> {
|
||||
/// Creates a new `ChunkedBitSet` containing all `i` for which `fill_fn(i)` is true.
|
||||
fn fill_with(domain_size: usize, fill_fn: impl Fn(usize) -> bool) -> Self {
|
||||
let mut this = ChunkedBitSet::new_empty(domain_size);
|
||||
for i in 0..domain_size {
|
||||
if fill_fn(i) {
|
||||
this.insert(i);
|
||||
}
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
/// Asserts that for each `i` in `0..self.domain_size()`, `self.contains(i) == expected_fn(i)`.
|
||||
#[track_caller]
|
||||
fn assert_filled_with(&self, expected_fn: impl Fn(usize) -> bool) {
|
||||
for i in 0..self.domain_size() {
|
||||
let expected = expected_fn(i);
|
||||
assert_eq!(self.contains(i), expected, "i = {i}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunked_bulk_ops() {
|
||||
struct ChunkedBulkOp {
|
||||
name: &'static str,
|
||||
op_fn: fn(&mut ChunkedBitSet<usize>, &ChunkedBitSet<usize>) -> bool,
|
||||
spec_fn: fn(fn(usize) -> bool, fn(usize) -> bool, usize) -> bool,
|
||||
}
|
||||
let ops = &[
|
||||
ChunkedBulkOp {
|
||||
name: "union",
|
||||
op_fn: ChunkedBitSet::union,
|
||||
spec_fn: |fizz, buzz, i| fizz(i) || buzz(i),
|
||||
},
|
||||
ChunkedBulkOp {
|
||||
name: "subtract",
|
||||
op_fn: ChunkedBitSet::subtract,
|
||||
spec_fn: |fizz, buzz, i| fizz(i) && !buzz(i),
|
||||
},
|
||||
ChunkedBulkOp {
|
||||
name: "intersect",
|
||||
op_fn: ChunkedBitSet::intersect,
|
||||
spec_fn: |fizz, buzz, i| fizz(i) && buzz(i),
|
||||
},
|
||||
];
|
||||
|
||||
let domain_sizes = [
|
||||
CHUNK_BITS / 7, // Smaller than a full chunk.
|
||||
CHUNK_BITS,
|
||||
(CHUNK_BITS + CHUNK_BITS / 7), // Larger than a full chunk.
|
||||
];
|
||||
|
||||
for ChunkedBulkOp { name, op_fn, spec_fn } in ops {
|
||||
for domain_size in domain_sizes {
|
||||
// If false, use different values for LHS and RHS, to test "fizz op buzz".
|
||||
// If true, use identical values, to test "fizz op fizz".
|
||||
for identical in [false, true] {
|
||||
// If false, make a clone of LHS before doing the op.
|
||||
// This covers optimizations that depend on whether chunk words are shared or not.
|
||||
for unique in [false, true] {
|
||||
// Print the current test case, so that we can see which one failed.
|
||||
println!(
|
||||
"Testing op={name}, domain_size={domain_size}, identical={identical}, unique={unique} ..."
|
||||
);
|
||||
|
||||
let fizz_fn = |i| i % 3 == 0;
|
||||
let buzz_fn = if identical { fizz_fn } else { |i| i % 5 == 0 };
|
||||
|
||||
// Check that `fizz op buzz` gives the expected results.
|
||||
chunked_bulk_ops_test_inner(
|
||||
domain_size,
|
||||
unique,
|
||||
fizz_fn,
|
||||
buzz_fn,
|
||||
op_fn,
|
||||
|i| spec_fn(fizz_fn, buzz_fn, i),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn chunked_bulk_ops_test_inner(
|
||||
domain_size: usize,
|
||||
unique: bool,
|
||||
fizz_fn: impl Fn(usize) -> bool + Copy,
|
||||
buzz_fn: impl Fn(usize) -> bool + Copy,
|
||||
op_fn: impl Fn(&mut ChunkedBitSet<usize>, &ChunkedBitSet<usize>) -> bool,
|
||||
expected_fn: impl Fn(usize) -> bool + Copy,
|
||||
) {
|
||||
// Create two bitsets, "fizz" (LHS) and "buzz" (RHS).
|
||||
let mut fizz = ChunkedBitSet::fill_with(domain_size, fizz_fn);
|
||||
let buzz = ChunkedBitSet::fill_with(domain_size, buzz_fn);
|
||||
|
||||
// If requested, clone `fizz` so that its word Rcs are not uniquely-owned.
|
||||
let _cloned = (!unique).then(|| fizz.clone());
|
||||
|
||||
// Perform the op (e.g. union/subtract/intersect), and verify that the
|
||||
// mutated LHS contains exactly the expected values.
|
||||
let changed = op_fn(&mut fizz, &buzz);
|
||||
fizz.assert_filled_with(expected_fn);
|
||||
|
||||
// Verify that the "changed" return value is correct.
|
||||
let should_change = (0..domain_size).any(|i| fizz_fn(i) != expected_fn(i));
|
||||
assert_eq!(changed, should_change);
|
||||
}
|
||||
|
||||
fn with_elements_chunked(elements: &[usize], domain_size: usize) -> ChunkedBitSet<usize> {
|
||||
let mut s = ChunkedBitSet::new_empty(domain_size);
|
||||
for &e in elements {
|
||||
|
||||
Reference in New Issue
Block a user