transmission/tests/libtransmission/bitfield-test.cc

497 lines
12 KiB
C++
Raw Normal View History

// This file Copyright (C) 2010-2022 Mnemosyne LLC.
2022-08-08 18:05:39 +00:00
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <algorithm>
#include <array>
2023-07-08 15:24:03 +00:00
#include <cstddef> // size_t
#include <cstdint> // uint8_t
#include <limits>
#include <vector>
#include <libtransmission/crypto-utils.h>
#include <libtransmission/bitfield.h>
#include "gtest/gtest.h"
TEST(Bitfield, count)
{
auto constexpr IterCount = size_t{ 10000U };
for (size_t i = 0; i < IterCount; ++i)
{
// generate a random bitfield
auto const bit_count = 100U + tr_rand_int(1000U);
auto bf = tr_bitfield{ bit_count };
for (size_t idx = 0U; idx < bit_count; ++idx)
{
bf.set(idx, tr_rand_int(2U) != 0U);
}
// pick arbitrary endpoints in the 1st and 2nd half of the bitfield
auto const midpt = bit_count / 2U;
auto const begin = tr_rand_int(midpt);
auto const end = midpt + tr_rand_int(midpt);
// test the bitfield
unsigned long count1 = {};
for (auto j = begin; j < end; ++j)
{
if (bf.test(j))
{
++count1;
}
}
auto const count2 = bf.count(begin, end);
EXPECT_EQ(count1, count2);
}
auto bf = tr_bitfield{ 0 };
fix: sonarcloud (#2865) * refactor: implement FileTreeItem::children_ with a std::vector * fix: std::move should not be called on forwarding reference * fix: uninitialized scalar variable * fix: unchecked return value from library * fix: dereference before null check * fix: unchecked return value from library * fix: unchecked return value from library * fixup! refactor: implement FileTreeItem::children_ with a std::vector * fix: signed-unsigned comparison in libtransmission tests * fix: avoid unnecessary copy by using const reference * fix: function should be declared const * refactor: use fmt::format to build log timestamps * fix: use init-statement to reduce variable scope * fixup! refactor: use fmt::format to build log timestamps * fix: remove tau_tracker destructor for rule-of-zero * fix: remove tr_peerIo destructor for rule-of-zero * Revert "fix: dereference before null check" This reverts commit cd789678156bb987a8dc6b2eb49cb1db4195d441. * fix: signed-unsigned comparison in libtransmission tests * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: signed-unsigned comparison in libtransmission tests * fixup! fix: extract nested code block into separate method * fix: mark possibly-unused as [[maybe_unused]] * fix: invalid stack memory reference in tr_found_file_t * fix: signed-unsigned comparison in libtransmission tests
2022-04-02 14:06:02 +00:00
EXPECT_EQ(0U, bf.count(0, 0));
EXPECT_EQ(0U, bf.count(0, 1));
bf = tr_bitfield{ 100 };
fix: sonarcloud (#2865) * refactor: implement FileTreeItem::children_ with a std::vector * fix: std::move should not be called on forwarding reference * fix: uninitialized scalar variable * fix: unchecked return value from library * fix: dereference before null check * fix: unchecked return value from library * fix: unchecked return value from library * fixup! refactor: implement FileTreeItem::children_ with a std::vector * fix: signed-unsigned comparison in libtransmission tests * fix: avoid unnecessary copy by using const reference * fix: function should be declared const * refactor: use fmt::format to build log timestamps * fix: use init-statement to reduce variable scope * fixup! refactor: use fmt::format to build log timestamps * fix: remove tau_tracker destructor for rule-of-zero * fix: remove tr_peerIo destructor for rule-of-zero * Revert "fix: dereference before null check" This reverts commit cd789678156bb987a8dc6b2eb49cb1db4195d441. * fix: signed-unsigned comparison in libtransmission tests * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: signed-unsigned comparison in libtransmission tests * fixup! fix: extract nested code block into separate method * fix: mark possibly-unused as [[maybe_unused]] * fix: invalid stack memory reference in tr_found_file_t * fix: signed-unsigned comparison in libtransmission tests
2022-04-02 14:06:02 +00:00
EXPECT_EQ(0U, bf.count(0, 0));
EXPECT_EQ(0U, bf.count(0, 100));
bf.set_has_all();
fix: sonarcloud (#2865) * refactor: implement FileTreeItem::children_ with a std::vector * fix: std::move should not be called on forwarding reference * fix: uninitialized scalar variable * fix: unchecked return value from library * fix: dereference before null check * fix: unchecked return value from library * fix: unchecked return value from library * fixup! refactor: implement FileTreeItem::children_ with a std::vector * fix: signed-unsigned comparison in libtransmission tests * fix: avoid unnecessary copy by using const reference * fix: function should be declared const * refactor: use fmt::format to build log timestamps * fix: use init-statement to reduce variable scope * fixup! refactor: use fmt::format to build log timestamps * fix: remove tau_tracker destructor for rule-of-zero * fix: remove tr_peerIo destructor for rule-of-zero * Revert "fix: dereference before null check" This reverts commit cd789678156bb987a8dc6b2eb49cb1db4195d441. * fix: signed-unsigned comparison in libtransmission tests * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: signed-unsigned comparison in libtransmission tests * fixup! fix: extract nested code block into separate method * fix: mark possibly-unused as [[maybe_unused]] * fix: invalid stack memory reference in tr_found_file_t * fix: signed-unsigned comparison in libtransmission tests
2022-04-02 14:06:02 +00:00
EXPECT_EQ(0U, bf.count(0, 0));
EXPECT_EQ(1U, bf.count(0, 1));
EXPECT_EQ(100U, bf.count(0, 100));
}
TEST(Bitfield, ctorFromFlagArray)
{
auto constexpr Tests = std::array<std::array<bool, 10>, 3>{ {
{ false, true, false, true, false, false, true, false, false, true }, // mixed
{ true, true, true, true, true, true, true, true, true, true }, // have all
{ false, false, false, false, false, false, false, false, false, false }, // have none
} };
for (auto const& flags : Tests)
{
size_t const true_count = std::count(std::begin(flags), std::end(flags), true);
size_t const n = std::size(flags);
bool const have_all = true_count == n;
bool const have_none = true_count == 0;
auto bf = tr_bitfield(n);
bf.set_from_bools(std::data(flags), std::size(flags));
EXPECT_EQ(n, bf.size());
EXPECT_EQ(have_all, bf.has_all());
EXPECT_EQ(have_none, bf.has_none());
EXPECT_EQ(true_count, bf.count());
for (size_t i = 0; i < std::size(flags); ++i)
{
EXPECT_EQ(flags[i], bf.test(i));
}
}
}
TEST(Bitfield, setRaw)
{
auto constexpr TestByte = uint8_t{ 10 };
auto constexpr TestByteTrueBits = 2;
auto raw = std::vector<uint8_t>(100, TestByte);
auto bf = tr_bitfield(std::size(raw) * 8);
bf.set_raw(std::data(raw), std::size(raw));
EXPECT_EQ(TestByteTrueBits * std::size(raw), bf.count());
// The first byte of the bitfield corresponds to indices 0 - 7
// from high bit to low bit, respectively. The next one 8-15, etc.
// Spare bits at the end are set to zero.
auto test = uint8_t{};
for (int i = 0; i < 8; ++i)
{
if (bf.test(i))
{
test |= (1 << (7 - i));
}
}
EXPECT_EQ(TestByte, test);
EXPECT_EQ(raw, bf.raw());
// check that has-all bitfield gets all-true
bf = tr_bitfield(std::size(raw) * 8);
bf.set_has_all();
raw = bf.raw();
EXPECT_EQ(std::size(bf) / 8, std::size(raw));
EXPECT_EQ(std::numeric_limits<unsigned char>::max(), raw[0]);
// check that the spare bits t the end are zero
bf = tr_bitfield{ 1 };
uint8_t const by = std::numeric_limits<uint8_t>::max();
bf.set_raw(&by, 1);
EXPECT_TRUE(bf.has_all());
EXPECT_FALSE(bf.has_none());
fix: sonarcloud (#2865) * refactor: implement FileTreeItem::children_ with a std::vector * fix: std::move should not be called on forwarding reference * fix: uninitialized scalar variable * fix: unchecked return value from library * fix: dereference before null check * fix: unchecked return value from library * fix: unchecked return value from library * fixup! refactor: implement FileTreeItem::children_ with a std::vector * fix: signed-unsigned comparison in libtransmission tests * fix: avoid unnecessary copy by using const reference * fix: function should be declared const * refactor: use fmt::format to build log timestamps * fix: use init-statement to reduce variable scope * fixup! refactor: use fmt::format to build log timestamps * fix: remove tau_tracker destructor for rule-of-zero * fix: remove tr_peerIo destructor for rule-of-zero * Revert "fix: dereference before null check" This reverts commit cd789678156bb987a8dc6b2eb49cb1db4195d441. * fix: signed-unsigned comparison in libtransmission tests * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: signed-unsigned comparison in libtransmission tests * fixup! fix: extract nested code block into separate method * fix: mark possibly-unused as [[maybe_unused]] * fix: invalid stack memory reference in tr_found_file_t * fix: signed-unsigned comparison in libtransmission tests
2022-04-02 14:06:02 +00:00
EXPECT_EQ(1U, bf.count());
raw = bf.raw();
fix: sonarcloud (#2865) * refactor: implement FileTreeItem::children_ with a std::vector * fix: std::move should not be called on forwarding reference * fix: uninitialized scalar variable * fix: unchecked return value from library * fix: dereference before null check * fix: unchecked return value from library * fix: unchecked return value from library * fixup! refactor: implement FileTreeItem::children_ with a std::vector * fix: signed-unsigned comparison in libtransmission tests * fix: avoid unnecessary copy by using const reference * fix: function should be declared const * refactor: use fmt::format to build log timestamps * fix: use init-statement to reduce variable scope * fixup! refactor: use fmt::format to build log timestamps * fix: remove tau_tracker destructor for rule-of-zero * fix: remove tr_peerIo destructor for rule-of-zero * Revert "fix: dereference before null check" This reverts commit cd789678156bb987a8dc6b2eb49cb1db4195d441. * fix: signed-unsigned comparison in libtransmission tests * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: signed-unsigned comparison in libtransmission tests * fixup! fix: extract nested code block into separate method * fix: mark possibly-unused as [[maybe_unused]] * fix: invalid stack memory reference in tr_found_file_t * fix: signed-unsigned comparison in libtransmission tests
2022-04-02 14:06:02 +00:00
EXPECT_EQ(1U, std::size(raw));
EXPECT_EQ(1 << 7, raw[0]);
}
TEST(Bitfield, bitfields)
{
unsigned int const bitcount = 500;
tr_bitfield field(bitcount);
// test tr_bitfield::set()
for (unsigned int i = 0; i < bitcount; i++)
{
if (i % 7 == 0)
{
field.set(i);
}
}
for (unsigned int i = 0; i < bitcount; i++)
{
EXPECT_EQ(field.test(i), (i % 7 == 0));
}
/* test tr_bitfield::setSpan */
field.set_span(0, bitcount);
for (unsigned int i = 0; i < bitcount; i++)
{
EXPECT_TRUE(field.test(i));
}
/* test tr_bitfield::clearBit */
for (unsigned int i = 0; i < bitcount; i++)
{
if (i % 7 != 0)
{
field.unset(i);
}
}
for (unsigned int i = 0; i < bitcount; i++)
{
EXPECT_EQ(field.test(i), (i % 7 == 0));
}
/* test tr_bitfield::clearBitRange in the middle of a boundary */
field.set_span(0, 64);
field.unset_span(4, 21);
for (unsigned int i = 0; i < 64; i++)
{
EXPECT_EQ(field.test(i), (i < 4 || i >= 21));
}
/* test tr_bitfield::clearBitRange on the boundaries */
field.set_span(0, 64);
field.unset_span(8, 24);
for (unsigned int i = 0; i < 64; i++)
{
EXPECT_EQ(field.test(i), (i < 8 || i >= 24));
}
/* test tr_bitfield::clearBitRange when begin & end is on the same word */
field.set_span(0, 64);
field.unset_span(4, 5);
for (unsigned int i = 0; i < 64; i++)
{
EXPECT_EQ(field.test(i), (i < 4 || i >= 5));
}
/* test tr_bitfield::setSpan */
field.unset_span(0, 64);
field.set_span(4, 21);
for (unsigned int i = 0; i < 64; i++)
{
EXPECT_EQ(field.test(i), (4 <= i && i < 21));
}
/* test tr_bitfield::setSpan on the boundaries */
field.unset_span(0, 64);
field.set_span(8, 24);
for (unsigned int i = 0; i < 64; i++)
{
EXPECT_EQ(field.test(i), (8 <= i && i < 24));
}
/* test tr_bitfield::setSpan when begin & end is on the same word */
field.unset_span(0, 64);
field.set_span(4, 5);
for (unsigned int i = 0; i < 64; i++)
{
EXPECT_EQ(field.test(i), (4 <= i && i < 5));
}
/* test tr_bitfield::setSpan when end runs beyond the end of the bitfield */
field.set_has_none();
field.set_span(100, 1000);
EXPECT_FALSE(field.has_none());
EXPECT_FALSE(field.has_all());
EXPECT_EQ(std::size(field) - 100, field.count());
/* test tr_bitfield::unsetSpan when it changes nothing */
field.set_has_none();
field.unset_span(0, 100);
EXPECT_TRUE(field.has_none());
EXPECT_FALSE(field.has_all());
fix: sonarcloud (#2865) * refactor: implement FileTreeItem::children_ with a std::vector * fix: std::move should not be called on forwarding reference * fix: uninitialized scalar variable * fix: unchecked return value from library * fix: dereference before null check * fix: unchecked return value from library * fix: unchecked return value from library * fixup! refactor: implement FileTreeItem::children_ with a std::vector * fix: signed-unsigned comparison in libtransmission tests * fix: avoid unnecessary copy by using const reference * fix: function should be declared const * refactor: use fmt::format to build log timestamps * fix: use init-statement to reduce variable scope * fixup! refactor: use fmt::format to build log timestamps * fix: remove tau_tracker destructor for rule-of-zero * fix: remove tr_peerIo destructor for rule-of-zero * Revert "fix: dereference before null check" This reverts commit cd789678156bb987a8dc6b2eb49cb1db4195d441. * fix: signed-unsigned comparison in libtransmission tests * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: extract nested code block into separate method * fix: use init-statement to reduce variable scope * fix: extract nested code block into separate method * fix: signed-unsigned comparison in libtransmission tests * fixup! fix: extract nested code block into separate method * fix: mark possibly-unused as [[maybe_unused]] * fix: invalid stack memory reference in tr_found_file_t * fix: signed-unsigned comparison in libtransmission tests
2022-04-02 14:06:02 +00:00
EXPECT_EQ(0U, field.count());
/* test tr_bitfield::setSpan when it changes nothing */
field.set_has_all();
field.set_span(0, 100);
EXPECT_FALSE(field.has_none());
EXPECT_TRUE(field.has_all());
EXPECT_EQ(std::size(field), field.count());
/* test tr_bitfield::setSpan with an invalid span doesn't crash */
field.set_has_all();
field.set_span(0, 0);
EXPECT_TRUE(field.has_all());
}
TEST(Bitfield, hasAllNone)
{
{
tr_bitfield field(3);
EXPECT_TRUE(!field.has_all());
EXPECT_TRUE(field.has_none());
field.set(0);
EXPECT_TRUE(!field.has_all());
EXPECT_TRUE(!field.has_none());
field.unset(0);
field.set(1);
EXPECT_TRUE(!field.has_all());
EXPECT_TRUE(!field.has_none());
field.unset(1);
field.set(2);
EXPECT_TRUE(!field.has_all());
EXPECT_TRUE(!field.has_none());
field.set(0);
field.set(1);
EXPECT_TRUE(field.has_all());
EXPECT_TRUE(!field.has_none());
field.set_has_none();
EXPECT_TRUE(!field.has_all());
EXPECT_TRUE(field.has_none());
field.set_has_all();
EXPECT_TRUE(field.has_all());
EXPECT_TRUE(!field.has_none());
}
{
tr_bitfield field(0);
EXPECT_TRUE(!field.has_all());
EXPECT_TRUE(!field.has_none());
field.set_has_none();
EXPECT_TRUE(!field.has_all());
EXPECT_TRUE(field.has_none());
field.set_has_all();
EXPECT_TRUE(field.has_all());
EXPECT_TRUE(!field.has_none());
}
}
TEST(Bitfield, percent)
{
auto field = tr_bitfield{ 100 };
field.set_has_all();
EXPECT_NEAR(1.0F, field.percent(), 0.01);
field.set_has_none();
EXPECT_NEAR(0.0F, field.percent(), 0.01);
field.set_span(0, std::size(field) / 2U);
EXPECT_NEAR(0.5F, field.percent(), 0.01);
field.set_has_none();
field.set_span(0, std::size(field) / 4U);
EXPECT_NEAR(0.25F, field.percent(), 0.01);
}
TEST(Bitfield, bitwiseOr)
{
auto a = tr_bitfield{ 100 };
auto b = tr_bitfield{ 100 };
a.set_has_all();
b.set_has_none();
a |= b;
EXPECT_TRUE(a.has_all());
a.set_has_none();
b.set_has_all();
a |= b;
EXPECT_TRUE(a.has_all());
a.set_has_none();
b.set_has_none();
a |= b;
EXPECT_TRUE(a.has_none());
a.set_has_none();
b.set_has_none();
a.set_span(0, std::size(a) / 2U);
b.set_span(std::size(a) / 2U, std::size(a));
EXPECT_EQ(0.5, a.percent());
EXPECT_EQ(0.5, b.percent());
a |= b;
EXPECT_EQ(1.0, a.percent());
EXPECT_TRUE(a.has_all());
a.set_has_none();
b.set_has_none();
for (size_t i = 0; i < std::size(a); ++i)
{
if ((i % 2U) != 0U)
{
a.set(i);
}
else
{
b.set(i);
}
}
EXPECT_NEAR(0.5F, a.percent(), 0.01);
EXPECT_NEAR(0.5F, b.percent(), 0.01);
a |= b;
EXPECT_TRUE(a.has_all());
}
TEST(Bitfield, bitwiseAnd)
{
auto a = tr_bitfield{ 100 };
auto b = tr_bitfield{ 100 };
a.set_has_all();
b.set_has_none();
a &= b;
EXPECT_TRUE(a.has_none());
a.set_has_none();
b.set_has_all();
a &= b;
EXPECT_TRUE(a.has_none());
a.set_has_all();
b.set_has_all();
a &= b;
EXPECT_TRUE(a.has_all());
a.set_has_none();
b.set_has_none();
a.set_span(0, std::size(a) / 2U);
b.set_span(std::size(a) / 2U, std::size(a));
EXPECT_EQ(0.5, a.percent());
EXPECT_EQ(0.5, b.percent());
a &= b;
EXPECT_TRUE(a.has_none());
a.set_has_none();
b.set_has_none();
for (size_t i = 0; i < std::size(a); ++i)
{
if ((i % 2U) != 0U)
{
a.set(i);
}
else
{
b.set(i);
}
}
a &= b;
EXPECT_TRUE(a.has_none());
a.set_has_none();
a.set_span(0U, std::size(a) / 10U);
b.set_has_none();
b.set_span(0U, std::size(a) / 20U);
a &= b;
EXPECT_NEAR(0.05F, a.percent(), 0.01);
a.set_has_none();
a.set_span(0U, std::size(a) / 10U);
b.set_has_none();
b.set_span(0U, std::size(a) / 20U);
b &= a;
EXPECT_NEAR(0.1F, a.percent(), 0.01);
}
TEST(Bitfield, intersects)
{
auto a = tr_bitfield{ 100 };
auto b = tr_bitfield{ 100 };
a.set_has_all();
b.set_has_none();
EXPECT_FALSE(a.intersects(b));
EXPECT_FALSE(b.intersects(a));
a.set_has_all();
b.set_has_all();
EXPECT_TRUE(a.intersects(b));
EXPECT_TRUE(b.intersects(a));
a.set_has_none();
b.set_has_none();
EXPECT_FALSE(a.intersects(b));
EXPECT_FALSE(b.intersects(a));
a.set_has_none();
b.set_has_none();
a.set_span(0, std::size(a) / 2U);
b.set_span(std::size(a) / 2U, std::size(a));
EXPECT_EQ(0.5, a.percent());
EXPECT_EQ(0.5, b.percent());
EXPECT_FALSE(a.intersects(b));
EXPECT_FALSE(b.intersects(a));
a.set_has_none();
b.set_has_none();
for (size_t i = 0; i < std::size(a); ++i)
{
if ((i % 2U) != 0U)
{
a.set(i);
}
else
{
b.set(i);
}
}
EXPECT_FALSE(a.intersects(b));
EXPECT_FALSE(b.intersects(a));
a.set_has_none();
a.set_span(0U, std::size(a) / 10U);
b.set_has_none();
b.set_span(0U, std::size(a) / 20U);
EXPECT_TRUE(a.intersects(b));
EXPECT_TRUE(b.intersects(a));
a.set_has_none();
a.set_span(0U, std::size(a) / 10U);
b.set_has_none();
b.set_span(0U, std::size(a) / 20U);
EXPECT_TRUE(a.intersects(b));
EXPECT_TRUE(b.intersects(a));
}