Skip to content

Commit

Permalink
Randomize bitvector tests
Browse files Browse the repository at this point in the history
  • Loading branch information
FAlbertDev committed Oct 29, 2024
1 parent 083e153 commit c2caebb
Showing 1 changed file with 115 additions and 54 deletions.
169 changes: 115 additions & 54 deletions src/tests/test_utils_bitvector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,45 @@
#include <botan/internal/fmt.h>

#include <algorithm>
#include <numeric>

namespace Botan_Tests {

namespace {

std::vector<Test::Result> test_bitvector_bitwise_accessors() {
/// Returns a random number in the range [min, max)
size_t rand_in_range(Botan::RandomNumberGenerator& rng, size_t min, size_t max) {
size_t val = Botan::load_le<size_t>(rng.random_array<sizeof(size_t)>());
return min + (val % (max - min));
}

/// Returns @p n integers smaller than @p upper_bound in random order
std::vector<size_t> rand_indices(Botan::RandomNumberGenerator& rng, size_t n, size_t upper_bound) {
auto shuffle = [&](std::vector<size_t>& v, Botan::RandomNumberGenerator& rng) {
// Fisher-Yates shuffle
for(size_t i = 0; i < v.size() - 1; ++i) {
auto j = rand_in_range(rng, i, v.size());
std::swap(v[i], v[j]);
}
};

std::vector<size_t> indices(upper_bound);
std::iota(indices.begin(), indices.end(), 0);
shuffle(indices, rng);
indices.resize(n);
return indices;
}

/// Create an empty bitvector of random size and chose a random number of points of interests
std::pair<Botan::bitvector, std::set<size_t>> rnd_bitvector_with_rnd_pois(Botan::RandomNumberGenerator& rng) {
Botan::bitvector bv(rand_in_range(rng, 0, 65));
size_t no_poi = rand_in_range(rng, 0, bv.size());
auto points_of_interest = rand_indices(rng, no_poi, bv.size());

return {bv, {points_of_interest.begin(), points_of_interest.end()}};
}

std::vector<Test::Result> test_bitvector_bitwise_accessors(Botan::RandomNumberGenerator& rng) {
return {
CHECK("default constructed bitvector",
[](auto& result) {
Expand All @@ -36,42 +69,53 @@ std::vector<Test::Result> test_bitvector_bitwise_accessors() {
}),

CHECK("setting bits",
[](auto& result) {
Botan::bitvector bv(32);
bv.set(1);
bv.at(2) = true;
bv.set(3);

result.confirm("0", !bv.at(0));
result.confirm("1", bv.at(1));
result.confirm("2", bv.at(2));
result.confirm("3", bv.at(3));
result.confirm("4", !bv.at(4));
[&](auto& result) {
auto [bv, ones] = rnd_bitvector_with_rnd_pois(rng);

for(size_t i : ones) {
if(rng.next_byte() % 2 == 0) {
bv.set(i);
} else {
bv.at(i) = true;
}
}
for(size_t i = 0; i < bv.size(); ++i) {
result.confirm(Botan::fmt("bit {} in expected state", i), bv.at(i) == ones.contains(i));
}
}),

CHECK("unsetting bits",
[](auto& result) {
Botan::bitvector bv(32);
bv.set(1).set(2).set(3).unset(2);
bv.at(3) = false;

result.confirm("0", !bv.at(0));
result.confirm("1", bv.at(1));
result.confirm("2", !bv.at(2));
result.confirm("3", !bv.at(3));
result.confirm("4", !bv.at(4));
[&](auto& result) {
auto [bv, zeros] = rnd_bitvector_with_rnd_pois(rng);
for(auto b : bv) {
b.set();
}

for(size_t i : zeros) {
if(rng.next_byte() % 2 == 0) {
bv.unset(i);
} else {
bv.at(i) = false;
}
}
for(size_t i = 0; i < bv.size(); ++i) {
result.confirm(Botan::fmt("bit {} in expected state", i), bv.at(i) == !zeros.contains(i));
}
}),

CHECK("flipping bits",
[](auto& result) {
Botan::bitvector bv(32);
bv.set(1).set(2).set(3).flip(2).flip(4);

result.confirm("0", !bv.at(0));
result.confirm("1", bv.at(1));
result.confirm("2", !bv.at(2));
result.confirm("3", bv.at(3));
result.confirm("4", bv.at(4));
[&](auto& result) {
auto [bv, ones] = rnd_bitvector_with_rnd_pois(rng);

for(size_t i = 0; i < bv.size(); ++i) {
if(std::find(ones.begin(), ones.end(), i) == ones.end()) {
bv.set(i);
}
bv.flip(i);
}
for(size_t i = 0; i < bv.size(); ++i) {
result.confirm(Botan::fmt("bit {} in expected state", i), bv.at(i) == ones.contains(i));
}
}),

CHECK("accessors validate offsets",
Expand Down Expand Up @@ -159,7 +203,7 @@ std::vector<Test::Result> test_bitvector_bitwise_accessors() {
};
}

std::vector<Test::Result> test_bitvector_capacity() {
std::vector<Test::Result> test_bitvector_capacity(Botan::RandomNumberGenerator&) {
return {
CHECK("default constructed bitvector",
[](auto& result) {
Expand Down Expand Up @@ -268,7 +312,7 @@ std::vector<Test::Result> test_bitvector_capacity() {
};
}

std::vector<Test::Result> test_bitvector_subvector() {
std::vector<Test::Result> test_bitvector_subvector(Botan::RandomNumberGenerator&) {
auto make_bitpattern = []<typename T>(T& bitvector, size_t pattern_offset = 0) {
if constexpr(std::unsigned_integral<T>) {
for(size_t i = pattern_offset; i - pattern_offset < sizeof(T) * 8; ++i) {
Expand Down Expand Up @@ -556,7 +600,7 @@ std::vector<Test::Result> test_bitvector_subvector() {
};
}

std::vector<Test::Result> test_bitvector_global_modifiers_and_predicates() {
std::vector<Test::Result> test_bitvector_global_modifiers_and_predicates(Botan::RandomNumberGenerator&) {
auto make_bitpattern = [](auto& bitvector) {
for(size_t i = 0; i < bitvector.size(); ++i) {
bitvector[i] = (i % 5);
Expand Down Expand Up @@ -719,7 +763,7 @@ std::vector<Test::Result> test_bitvector_global_modifiers_and_predicates() {
};
}

std::vector<Test::Result> test_bitvector_binary_operators() {
std::vector<Test::Result> test_bitvector_binary_operators(Botan::RandomNumberGenerator&) {
auto check_set = [](auto& result, auto bits, std::vector<size_t> set_bits) {
for(size_t i = 0; i < bits.size(); ++i) {
const auto should_be_set = std::find(set_bits.begin(), set_bits.end(), i) != set_bits.end();
Expand Down Expand Up @@ -846,7 +890,7 @@ std::vector<Test::Result> test_bitvector_binary_operators() {
};
}

std::vector<Test::Result> test_bitvector_serialization() {
std::vector<Test::Result> test_bitvector_serialization(Botan::RandomNumberGenerator&) {
constexpr uint8_t outlen = 64;
const auto bytearray = [] {
std::array<uint8_t, outlen> out;
Expand Down Expand Up @@ -936,7 +980,7 @@ std::vector<Test::Result> test_bitvector_serialization() {
};
}

std::vector<Test::Result> test_bitvector_constant_time_operations() {
std::vector<Test::Result> test_bitvector_constant_time_operations(Botan::RandomNumberGenerator&) {
constexpr Botan::CT::Choice yes = Botan::CT::Choice::yes();
constexpr Botan::CT::Choice no = Botan::CT::Choice::no();

Expand Down Expand Up @@ -990,7 +1034,7 @@ std::vector<Test::Result> test_bitvector_constant_time_operations() {
};
}

Test::Result test_bitvector_conditional_xor_workload() {
std::vector<Test::Result> test_bitvector_conditional_xor_workload(Botan::RandomNumberGenerator&) {
Test::Result res("Conditional XOR, Gauss Workload");

auto rng = Test::new_rng("Conditional XOR, Gauss Workload");
Expand All @@ -1015,10 +1059,10 @@ Test::Result test_bitvector_conditional_xor_workload() {

res.confirm("Prevent compiler from optimizing away",
bitvec_vec.at(0).any_vartime() || bitvec_vec.at(0).none_vartime());
return res;
return {res};
}

std::vector<Test::Result> test_bitvector_iterators() {
std::vector<Test::Result> test_bitvector_iterators(Botan::RandomNumberGenerator&) {
return {
CHECK("Iterators: range-based for loop",
[](auto& result) {
Expand Down Expand Up @@ -1139,7 +1183,7 @@ using TestBitvector = Botan::Strong<Botan::bitvector, struct TestBitvector_>;
using TestSecureBitvector = Botan::Strong<Botan::secure_bitvector, struct TestBitvector_>;
using TestUInt32 = Botan::Strong<uint32_t, struct TestUInt32_>;

Test::Result test_bitvector_strongtype_adapter() {
std::vector<Test::Result> test_bitvector_strongtype_adapter(Botan::RandomNumberGenerator&) {
Test::Result result("Bitvector in strong type");

TestBitvector bv1(33);
Expand Down Expand Up @@ -1193,22 +1237,39 @@ Test::Result test_bitvector_strongtype_adapter() {
const auto str = bv4.to_string();
result.test_eq("bv4 to_string", str, "00010");

return result;
return {result};
}

} // namespace

BOTAN_REGISTER_TEST_FN("utils",
"bitvector",
test_bitvector_bitwise_accessors,
test_bitvector_capacity,
test_bitvector_subvector,
test_bitvector_global_modifiers_and_predicates,
test_bitvector_binary_operators,
test_bitvector_serialization,
test_bitvector_constant_time_operations,
test_bitvector_conditional_xor_workload,
test_bitvector_iterators,
test_bitvector_strongtype_adapter);
class BitVector_Tests final : public Test {
public:
std::vector<Test::Result> run() override {
std::vector<Test::Result> results;
auto& rng = Test::rng();

std::vector<std::function<std::vector<Test::Result>(Botan::RandomNumberGenerator&)>> funcs{
test_bitvector_bitwise_accessors,
test_bitvector_capacity,
test_bitvector_subvector,
test_bitvector_global_modifiers_and_predicates,
test_bitvector_binary_operators,
test_bitvector_serialization,
test_bitvector_constant_time_operations,
test_bitvector_conditional_xor_workload,
test_bitvector_iterators,
test_bitvector_strongtype_adapter,
};

for(const auto& test_fn : funcs) {
auto fn_results = test_fn(rng);
results.insert(results.end(), fn_results.begin(), fn_results.end());
}

return results;
}
};

BOTAN_REGISTER_TEST("utils", "bitvector", BitVector_Tests);

} // namespace Botan_Tests

0 comments on commit c2caebb

Please sign in to comment.