summaryrefslogtreecommitdiff
path: root/Source/UnitTests/Common/LazyMemoryRegionBitSetTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/UnitTests/Common/LazyMemoryRegionBitSetTest.cpp')
-rw-r--r--Source/UnitTests/Common/LazyMemoryRegionBitSetTest.cpp162
1 files changed, 162 insertions, 0 deletions
diff --git a/Source/UnitTests/Common/LazyMemoryRegionBitSetTest.cpp b/Source/UnitTests/Common/LazyMemoryRegionBitSetTest.cpp
new file mode 100644
index 0000000000..e9b9a12716
--- /dev/null
+++ b/Source/UnitTests/Common/LazyMemoryRegionBitSetTest.cpp
@@ -0,0 +1,162 @@
+// Copyright 2026 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <fmt/format.h>
+#include <gtest/gtest.h>
+
+#include "Common/LazyMemoryRegionBitSet.h"
+#include "Common/MemArena.h"
+
+// Intentionally not divisible by 8
+static constexpr size_t BIT_COUNT = 1024 + 512 + 1;
+
+// Two blocks for the test to write to, and one block that remains mapped to the default page so we
+// can check that we didn't accidentally write to the default page
+static constexpr size_t WINDOWS_BOUNDARY_BIT_COUNT =
+ Common::LazyMemoryRegion::WINDOWS_BLOCK_SIZE * 8 * 3;
+
+static u32 ExpectedContentAllZeroes(size_t)
+{
+ return 0;
+}
+
+static u32 ExpectedContentAllOnes(size_t)
+{
+ return 0xFFFFFFFF;
+}
+
+static u32 ExpectedContentAfterSetting(size_t index)
+{
+ switch (index)
+ {
+ case 32:
+ return 0x0000FF00;
+ case 34:
+ return 0x0FFFFFF0;
+ case 35:
+ return 0xFFFFFFF0;
+ case 36:
+ return 0x0FFFFFFF;
+ case 37:
+ return 0xFFFFFFF0;
+ case 39:
+ return 0x0FFFFFFF;
+ case 40:
+ return 0xFFFFFFF0;
+ case 47:
+ return 0x0FFFFFFF;
+ default:
+ if (index < 32)
+ return 1 << index;
+ if (index < 48)
+ return 0xFFFFFFFF;
+ return 0;
+ }
+}
+
+static u32 ExpectedContentAfterClearing(size_t index)
+{
+ return ~ExpectedContentAfterSetting(index);
+}
+
+static u32 ExpectedContentForWindowsBoundary(size_t index)
+{
+ switch (index)
+ {
+ case Common::LazyMemoryRegion::WINDOWS_BLOCK_SIZE / sizeof(u32) - 1:
+ return 0x80000000;
+ case Common::LazyMemoryRegion::WINDOWS_BLOCK_SIZE / sizeof(u32):
+ return 0x00000001;
+ default:
+ return 0;
+ }
+}
+
+template <typename F>
+static void VerifyContent(const u32* memory, size_t bits, F f)
+{
+ const size_t end_index = bits / 32;
+ for (size_t i = 0; i < end_index; ++i, ++memory)
+ {
+ const u32 expected = f(i);
+ ASSERT_EQ(*memory, expected) << "i=" << i;
+ }
+ if (bits % 32 != 0)
+ {
+ const u32 expected = f(end_index) & ~(0xFFFFFFFF << bits % 32);
+ ASSERT_EQ(*memory, expected) << "i=" << end_index;
+ }
+}
+
+TEST(LazyMemoryRegionBitSet, Unwritten)
+{
+ Common::LazyMemoryRegionBitSet bit_set(BIT_COUNT);
+ VerifyContent(bit_set.GetRawMemory(), BIT_COUNT, ExpectedContentAllZeroes);
+}
+
+TEST(LazyMemoryRegionBitSet, Set)
+{
+ Common::LazyMemoryRegionBitSet bit_set(BIT_COUNT);
+
+ // Set single bits in the range 0-1023
+ for (size_t i = 0; i < 32; ++i)
+ bit_set.SetBit(i * 33);
+
+ // Set ranges of bits in bits 1024 and higher
+ bit_set.SetBits(1024 + 8, 1024 + 16);
+ bit_set.SetBits(1024 + 32, 1024 + 64);
+ bit_set.SetBits(1024 + 68, 1024 + 92);
+ bit_set.SetBits(1024 + 100, 1024 + 128);
+ bit_set.SetBits(1024 + 128, 1024 + 156);
+ bit_set.SetBits(1024 + 164, 1024 + 252);
+ bit_set.SetBits(1024 + 260, 1024 + 384);
+ bit_set.SetBits(1024 + 384, 1024 + 508);
+
+ VerifyContent(bit_set.GetRawMemory(), BIT_COUNT, ExpectedContentAfterSetting);
+
+ bit_set.Clear();
+ VerifyContent(bit_set.GetRawMemory(), BIT_COUNT, ExpectedContentAllZeroes);
+}
+
+TEST(LazyMemoryRegionBitSet, Clear)
+{
+ Common::LazyMemoryRegionBitSet bit_set(BIT_COUNT);
+ bit_set.SetBits(0, BIT_COUNT);
+ VerifyContent(bit_set.GetRawMemory(), BIT_COUNT, ExpectedContentAllOnes);
+
+ // Clear single bits in the range 0-1023
+ for (size_t i = 0; i < 32; ++i)
+ bit_set.ClearBit(i * 33);
+
+ // Clear ranges of bits in bits 1024 and higher
+ bit_set.ClearBits(1024 + 8, 1024 + 16);
+ bit_set.ClearBits(1024 + 32, 1024 + 64);
+ bit_set.ClearBits(1024 + 68, 1024 + 92);
+ bit_set.ClearBits(1024 + 100, 1024 + 128);
+ bit_set.ClearBits(1024 + 128, 1024 + 156);
+ bit_set.ClearBits(1024 + 164, 1024 + 252);
+ bit_set.ClearBits(1024 + 260, 1024 + 384);
+ bit_set.ClearBits(1024 + 384, 1024 + 508);
+
+ VerifyContent(bit_set.GetRawMemory(), BIT_COUNT, ExpectedContentAfterClearing);
+
+ bit_set.Clear();
+ VerifyContent(bit_set.GetRawMemory(), BIT_COUNT, ExpectedContentAllZeroes);
+}
+
+TEST(LazyMemoryRegionBitSet, WindowsBoundary)
+{
+ // Unlike on other operating systems, LazyMemoryRegion's Windows implementation allocates one page
+ // containing only zeroes and maps it to every part of the region by default. If
+ // LazyMemoryRegionBitSet wants to write to any part of the region, it first needs to call
+ // LazyMemoryRegion::EnsureMemoryPageWritable. This test is checking that
+ // LazyMemoryRegion::EnsureMemoryPageWritable is being called with the correct parameters.
+
+ Common::LazyMemoryRegionBitSet bit_set(WINDOWS_BOUNDARY_BIT_COUNT);
+
+ bit_set.SetBits(Common::LazyMemoryRegion::WINDOWS_BLOCK_SIZE * 8 - 1,
+ Common::LazyMemoryRegion::WINDOWS_BLOCK_SIZE * 8 + 1);
+
+ VerifyContent(bit_set.GetRawMemory(), WINDOWS_BOUNDARY_BIT_COUNT,
+ ExpectedContentForWindowsBoundary);
+}