NazaraEngine/tests/Engine/Core/BitsetTest.cpp

374 lines
9.4 KiB
C++

#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/Bitset.hpp>
#include <catch2/catch.hpp>
#include <array>
#include <string>
#include <iostream>
template<typename Block> void Check(const char* title);
template<typename Block> void CheckAppend(const char* title);
template<typename Block> void CheckBitOps(const char* title);
template<typename Block> void CheckBitOpsMultipleBlocks(const char* title);
template<typename Block> void CheckConstructor(const char* title);
template<typename Block> void CheckCopyMoveSwap(const char* title);
template<typename Block> void CheckRead(const char* title);
template<typename Block> void CheckReverse(const char* title);
SCENARIO("Bitset", "[CORE][BITSET]")
{
Check<Nz::UInt8>("Bitset made of 8bits blocks");
Check<Nz::UInt16>("Bitset made of 16bits blocks");
Check<Nz::UInt32>("Bitset made of 32bits blocks");
Check<Nz::UInt64>("Bitset made of 64bits blocks");
}
template<typename Block>
void Check(const char* title)
{
CheckConstructor<Block>(title);
CheckCopyMoveSwap<Block>(title);
CheckBitOps<Block>(title);
CheckBitOpsMultipleBlocks<Block>(title);
CheckAppend<Block>(title);
CheckRead<Block>(title);
CheckReverse<Block>(title);
}
template<typename Block>
void CheckAppend(const char* title)
{
SECTION(title)
{
GIVEN("An empty bitset filled by bytes")
{
#define BitVal1 00110111
#define BitVal2 11011110
#define BitVal3 01000010
std::array<Nz::UInt8, 3> data = {{NazaraPrefixMacro(BitVal1, 0b), NazaraPrefixMacro(BitVal2, 0b), NazaraPrefixMacro(BitVal3, 0b)}};
const char result[] = NazaraStringifyMacro(BitVal3) NazaraStringifyMacro(BitVal2) NazaraStringifyMacro(BitVal1);
std::size_t bitCount = data.size() * 8;
#undef BitVal1
#undef BitVal2
#undef BitVal3
std::array<std::pair<const char*, std::size_t>, 7> tests = {
{
{"We append bits one by one", 1},
{"We append bits two by two", 2},
{"We append bits three by three", 3},
{"We append bits four by four", 4},
{"We append bits six by six", 6},
{"We append bits byte by byte", 8},
{"We append bits twelve by twelve", 12}
}
};
for (auto& pair : tests)
{
WHEN(pair.first)
{
Nz::Bitset<Block> bitset;
for (std::size_t i = 0; i < bitCount; i += pair.second)
{
Nz::UInt16 value = data[i / 8] >> (i % 8);
if ((i % 8) + pair.second > 8 && i/8 != data.size()-1)
value |= static_cast<Nz::UInt16>(data[i / 8 + 1]) << (8 - (i % 8));
bitset.AppendBits(value, pair.second);
}
REQUIRE(bitset.GetSize() == bitCount);
Nz::Bitset<Block> expectedBitset(result);
CHECK(bitset == expectedBitset);
CHECK(bitset.GetBlockCount() == (bitCount / bitset.bitsPerBlock + std::min<std::size_t>(1, bitCount % bitset.bitsPerBlock)));
}
}
}
}
}
template<typename Block>
void CheckBitOps(const char* title)
{
SECTION(title)
{
GIVEN("Two bitsets")
{
Nz::Bitset<Block> first("01001");
Nz::Bitset<Block> second("10111");
WHEN("We perform operators")
{
Nz::Bitset<Block> andBitset = first & second;
Nz::Bitset<Block> orBitset = first | second;
Nz::Bitset<Block> xorBitset = first ^ second;
THEN("They should operate as logical operators")
{
CHECK(andBitset == Nz::Bitset<Block>("00001"));
CHECK(orBitset == Nz::Bitset<Block>("11111"));
CHECK(orBitset.TestAll());
CHECK(xorBitset == Nz::Bitset<Block>("11110"));
CHECK(!xorBitset.TestAll());
CHECK((~orBitset).TestNone());
}
}
WHEN("We perform bit shifts")
{
first.ShiftLeft(1);
second.ShiftRight(2);
THEN("We should obtain these")
{
CHECK(first == Nz::Bitset<Block>("10010"));
CHECK(second == Nz::Bitset<Block>("101"));
}
}
}
}
}
template<typename Block>
void CheckBitOpsMultipleBlocks(const char* title)
{
SECTION(title)
{
GIVEN("Two bitsets")
{
Nz::Bitset<Block> first("01001011010010101001010011010101001");
Nz::Bitset<Block> second("10111111101101110110111101101110110");
WHEN("We perform operators")
{
Nz::Bitset<Block> andBitset = first & second;
Nz::Bitset<Block> orBitset = first | second;
Nz::Bitset<Block> xorBitset = first ^ second;
THEN("They should operate as logical operators")
{
CHECK(andBitset == Nz::Bitset<Block>("00001011000000100000010001000100000"));
CHECK(orBitset == Nz::Bitset<Block>("11111111111111111111111111111111111"));
CHECK(orBitset.TestAll());
CHECK(xorBitset == Nz::Bitset<Block>("11110100111111011111101110111011111"));
CHECK(!xorBitset.TestAll());
CHECK((~orBitset).TestNone());
}
}
WHEN("We perform bit shifts")
{
first.ShiftLeft(16);
second.ShiftRight(16);
THEN("We should obtain these")
{
CHECK(first == Nz::Bitset<Block>("10010100110101010010000000000000000"));
first.ShiftLeft(1);
CHECK(first == Nz::Bitset<Block>("00101001101010100100000000000000000"));
CHECK(second == Nz::Bitset<Block>("1011111110110111011"));
second.ShiftRight(1);
CHECK(second == Nz::Bitset<Block>("101111111011011101"));
}
}
}
}
}
template<typename Block>
void CheckConstructor(const char* title)
{
SECTION(title)
{
GIVEN("Allocate and constructor")
{
Nz::Bitset<Block> bitset(3, false);
THEN("Capacity is 3 and size is 3")
{
CHECK(bitset.GetSize() == 3);
CHECK(bitset.GetCapacity() >= 3);
}
}
GIVEN("Iterator and default constructor")
{
std::string anotherDataString("0101");
Nz::Bitset<Block> defaultByte;
Nz::Bitset<Block> anotherData(anotherDataString.c_str());
WHEN("We assign 'anotherData'")
{
defaultByte = anotherDataString;
CHECK(anotherData == defaultByte);
CHECK(defaultByte.GetSize() == 4);
CHECK(defaultByte.GetCapacity() >= 4);
CHECK(anotherData.GetSize() == 4);
CHECK(anotherData.GetCapacity() >= 4);
}
}
}
}
template<typename Block>
void CheckCopyMoveSwap(const char* title)
{
SECTION(title)
{
GIVEN("Copy and Move constructor")
{
Nz::Bitset<Block> originalArray(3, true);
WHEN("We copy")
{
Nz::Bitset<Block> copyBitset(originalArray);
THEN("We get a copy")
{
CHECK(copyBitset == originalArray);
AND_WHEN("We modify one")
{
for (std::size_t i = 0; i < copyBitset.GetSize(); ++i)
copyBitset[i] = false;
THEN("They are no more equal")
{
CHECK(copyBitset != originalArray);
CHECK(copyBitset == Nz::Bitset<Block>(3, false));
}
}
}
}
WHEN("We move")
{
Nz::Bitset<Block> moveBitset(std::move(originalArray));
THEN("These results are expected")
{
CHECK(moveBitset == Nz::Bitset<Block>(3, true));
CHECK(originalArray.GetCapacity() == 0);
}
}
}
GIVEN("Three bitsets")
{
Nz::Bitset<Block> first("01001");
Nz::Bitset<Block> second("10110");
Nz::Bitset<Block> third;
WHEN("We swap first and third, then second and third and finally third and first")
{
Nz::Bitset<Block> oldFirst(first);
Nz::Bitset<Block> oldSecond(second);
first.Swap(third);
std::swap(second, third);
third.Swap(first);
THEN("First and second have been swapped and third is still empty.")
{
CHECK(oldFirst == second);
CHECK(oldSecond == first);
CHECK(third.GetSize() == 0);
}
}
}
}
}
template<typename Block>
void CheckRead(const char* title)
{
SECTION(title)
{
GIVEN("An empty bitset filled by reading")
{
#define BitVal1 10010101
#define BitVal2 11010010
#define BitVal3 01101010
std::array<Nz::UInt8, 3> data = {{NazaraPrefixMacro(BitVal1, 0b), NazaraPrefixMacro(BitVal2, 0b), NazaraPrefixMacro(BitVal3, 0b)}};
const char result[] = NazaraStringifyMacro(BitVal3) NazaraStringifyMacro(BitVal2) NazaraStringifyMacro(BitVal1);
std::size_t resultLength = Nz::CountOf(result) - 1;
std::size_t bitCount = data.size() * 8;
#undef BitVal1
#undef BitVal2
#undef BitVal3
std::array<std::pair<const char*, std::size_t>, 8> tests = {
{
{"We read bits one by one", 1},
{"We read bits two by two", 2},
{"We read bits three by three", 3},
{"We read bits four by four", 4},
{"We read bits six by six", 6},
{"We read bits byte by byte", 8},
{"We read bits twelve by twelve", 12},
{"We read bits all at once", 24}
}
};
for (auto& pair : tests)
{
WHEN(pair.first)
{
Nz::Bitset<Block> bitset;
auto seq = bitset.Write(data.data(), pair.second);
for (std::size_t i = pair.second; i < bitCount; i += pair.second)
seq = bitset.Write(seq, pair.second);
REQUIRE(bitset.GetSize() == bitCount);
Nz::Bitset<Block> expectedBitset(result);
CHECK(bitset == expectedBitset);
CHECK(bitset.GetBlockCount() == (bitCount / bitset.bitsPerBlock + std::min<std::size_t>(1, bitCount % bitset.bitsPerBlock)));
}
}
}
}
}
template<typename Block>
void CheckReverse(const char* title)
{
SECTION(title)
{
GIVEN("A bitset")
{
std::string bits = "010011100010001101001111";
Nz::Bitset<Block> expected(bits);
WHEN("We reverse the order of bits")
{
Nz::Bitset<Block> bitset(bits);
bitset.Reverse();
THEN("The order of bits should be reversed")
{
std::string reversedBits = bits;
std::reverse(reversedBits.begin(), reversedBits.end());
CHECK(bitset == Nz::Bitset<Block>(reversedBits));
}
AND_WHEN("We reverse the bit order again")
{
bitset.Reverse();
THEN("It should be back to normal")
{
CHECK(bitset == expected);
}
}
}
}
}
}