diff --git a/Utilities/address_range.h b/Utilities/address_range.h index 71fbd4a382..e1e48bb00c 100644 --- a/Utilities/address_range.h +++ b/Utilities/address_range.h @@ -529,6 +529,20 @@ namespace utils return !cur.valid() || cur.inside(range); }); } + + // Count valid entries + usz valid_count() const + { + usz count = 0; + for (const auto& e : data) + { + if (e.valid()) + { + count++; + } + } + return count; + } }; diff --git a/rpcs3/tests/rpcs3_test.vcxproj b/rpcs3/tests/rpcs3_test.vcxproj index 3351d9d8b8..d59172a231 100644 --- a/rpcs3/tests/rpcs3_test.vcxproj +++ b/rpcs3/tests/rpcs3_test.vcxproj @@ -89,6 +89,7 @@ + diff --git a/rpcs3/tests/test_address_range.cpp b/rpcs3/tests/test_address_range.cpp new file mode 100644 index 0000000000..292f8c11bf --- /dev/null +++ b/rpcs3/tests/test_address_range.cpp @@ -0,0 +1,450 @@ +#include + +#define private public +#include "Utilities/address_range.h" +#undef private + +using namespace utils; + +namespace utils +{ + TEST(AddressRange, Constructors) + { + // Default constructor + address_range empty; + EXPECT_FALSE(empty.valid()); + EXPECT_EQ(empty.start, umax); + EXPECT_EQ(empty.end, 0); + + // Static factory methods + address_range r1 = address_range::start_length(0x1000, 0x1000); + EXPECT_EQ(r1.start, 0x1000); + EXPECT_EQ(r1.end, 0x1FFF); + EXPECT_EQ(r1.length(), 0x1000); + EXPECT_TRUE(r1.valid()); + + address_range r2 = address_range::start_end(0x2000, 0x2FFF); + EXPECT_EQ(r2.start, 0x2000); + EXPECT_EQ(r2.end, 0x2FFF); + EXPECT_EQ(r2.length(), 0x1000); + EXPECT_TRUE(r2.valid()); + + // Edge cases + address_range zero_length = address_range::start_length(0x1000, 0); + EXPECT_FALSE(zero_length.valid()); + + address_range single_byte = address_range::start_length(0x1000, 1); + EXPECT_TRUE(single_byte.valid()); + EXPECT_EQ(single_byte.start, 0x1000); + EXPECT_EQ(single_byte.end, 0x1000); + EXPECT_EQ(single_byte.length(), 1); + } + + TEST(AddressRange, LengthAndBoundaries) + { + address_range r = address_range::start_length(0x1000, 0x1000); + + // Test length + EXPECT_EQ(r.length(), 0x1000); + + // Test set_length + r.set_length(0x2000); + EXPECT_EQ(r.start, 0x1000); + EXPECT_EQ(r.end, 0x2FFF); + EXPECT_EQ(r.length(), 0x2000); + + // Test next_address and prev_address + EXPECT_EQ(r.next_address(), 0x3000); + EXPECT_EQ(r.prev_address(), 0xFFF); + } + + TEST(AddressRange, Overlapping) + { + address_range r1 = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + + // Complete overlap + address_range r2 = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + EXPECT_TRUE(r1.overlaps(r2)); + EXPECT_TRUE(r2.overlaps(r1)); + + // Partial overlap at start + address_range r3 = address_range::start_length(0x800, 0x1000); // 0x800-0x17FF + EXPECT_TRUE(r1.overlaps(r3)); + EXPECT_TRUE(r3.overlaps(r1)); + + // Partial overlap at end + address_range r4 = address_range::start_length(0x1800, 0x1000); // 0x1800-0x27FF + EXPECT_TRUE(r1.overlaps(r4)); + EXPECT_TRUE(r4.overlaps(r1)); + + // No overlap, before + address_range r5 = address_range::start_length(0x0, 0x1000); // 0x0-0xFFF + EXPECT_FALSE(r1.overlaps(r5)); + EXPECT_FALSE(r5.overlaps(r1)); + + // No overlap, after + address_range r6 = address_range::start_length(0x2000, 0x1000); // 0x2000-0x2FFF + EXPECT_FALSE(r1.overlaps(r6)); + EXPECT_FALSE(r6.overlaps(r1)); + + // Single address overlap at start + address_range r7 = address_range::start_length(0x800, 0x801); // 0x800-0x1000 + EXPECT_TRUE(r1.overlaps(r7)); + EXPECT_TRUE(r7.overlaps(r1)); + + // Single address overlap at end + address_range r8 = address_range::start_length(0x1FFF, 0x1000); // 0x1FFF-0x2FFE + EXPECT_TRUE(r1.overlaps(r8)); + EXPECT_TRUE(r8.overlaps(r1)); + + // Address overlap test + EXPECT_TRUE(r1.overlaps(0x1000)); // Start boundary + EXPECT_TRUE(r1.overlaps(0x1FFF)); // End boundary + EXPECT_TRUE(r1.overlaps(0x1800)); // Middle + EXPECT_FALSE(r1.overlaps(0xFFF)); // Just before + EXPECT_FALSE(r1.overlaps(0x2000)); // Just after + } + + TEST(AddressRange, Inside) + { + address_range r1 = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + + // Same range + address_range r2 = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + EXPECT_TRUE(r1.inside(r2)); + EXPECT_TRUE(r2.inside(r1)); + + // Smaller range inside + address_range r3 = address_range::start_length(0x1200, 0x800); // 0x1200-0x19FF + EXPECT_TRUE(r3.inside(r1)); + EXPECT_FALSE(r1.inside(r3)); + + // Larger range outside + address_range r4 = address_range::start_length(0x800, 0x2000); // 0x800-0x27FF + EXPECT_TRUE(r1.inside(r4)); + EXPECT_FALSE(r4.inside(r1)); + + // Partially overlapping + address_range r5 = address_range::start_length(0x1800, 0x1000); // 0x1800-0x27FF + EXPECT_FALSE(r1.inside(r5)); + EXPECT_FALSE(r5.inside(r1)); + + // No overlap + address_range r6 = address_range::start_length(0x3000, 0x1000); // 0x3000-0x3FFF + EXPECT_FALSE(r1.inside(r6)); + EXPECT_FALSE(r6.inside(r1)); + } + + TEST(AddressRange, Touches) + { + address_range r1 = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + + // Same range (overlaps) + address_range r2 = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + EXPECT_TRUE(r1.touches(r2)); + + // Overlapping ranges + address_range r3 = address_range::start_length(0x1800, 0x1000); // 0x1800-0x27FF + EXPECT_TRUE(r1.touches(r3)); + + // Adjacent at end of r1 + address_range r4 = address_range::start_length(0x2000, 0x1000); // 0x2000-0x2FFF + EXPECT_TRUE(r1.touches(r4)); + EXPECT_TRUE(r4.touches(r1)); + + // Adjacent at start of r1 + address_range r5 = address_range::start_length(0x0, 0x1000); // 0x0-0xFFF + EXPECT_TRUE(r1.touches(r5)); + EXPECT_TRUE(r5.touches(r1)); + + // Not touching + address_range r6 = address_range::start_length(0x3000, 0x1000); // 0x3000-0x3FFF + EXPECT_FALSE(r1.touches(r6)); + EXPECT_FALSE(r6.touches(r1)); + } + + TEST(AddressRange, Distance) + { + address_range r1 = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + + // Touching ranges + address_range r2 = address_range::start_length(0x2000, 0x1000); // 0x2000-0x2FFF + EXPECT_EQ(r1.distance(r2), 0); + EXPECT_EQ(r2.distance(r1), 0); + EXPECT_EQ(r1.signed_distance(r2), 0); + EXPECT_EQ(r2.signed_distance(r1), 0); + + // Gap of 0x1000 (r3 after r1) + address_range r3 = address_range::start_length(0x3000, 0x1000); // 0x3000-0x3FFF + EXPECT_EQ(r1.distance(r3), 0x1000); + EXPECT_EQ(r3.distance(r1), 0x1000); + EXPECT_EQ(r1.signed_distance(r3), 0x1000); + EXPECT_EQ(r3.signed_distance(r1), -0x1000); + + // Gap of 0x1000 (r4 before r1) + address_range r4 = address_range::start_end(0, 0xEFF); // 0x0-0xEFF + EXPECT_EQ(r1.distance(r4), 0x100); + EXPECT_EQ(r4.distance(r1), 0x100); + EXPECT_EQ(r1.signed_distance(r4), -0x100); + EXPECT_EQ(r4.signed_distance(r1), 0x100); + + // Overlapping ranges + address_range r5 = address_range::start_length(0x1800, 0x1000); // 0x1800-0x27FF + EXPECT_EQ(r1.distance(r5), 0); + EXPECT_EQ(r5.distance(r1), 0); + EXPECT_EQ(r1.signed_distance(r5), 0); + EXPECT_EQ(r5.signed_distance(r1), 0); + } + + TEST(AddressRange, MinMax) + { + address_range r1 = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + address_range r2 = address_range::start_length(0x1800, 0x1000); // 0x1800-0x27FF + + // Get min-max + address_range min_max = r1.get_min_max(r2); + EXPECT_EQ(min_max.start, 0x1000); + EXPECT_EQ(min_max.end, 0x27FF); + + // Set min-max + address_range r3 = address_range::start_length(0x2000, 0x1000); // 0x2000-0x2FFF + r3.set_min_max(r1); + EXPECT_EQ(r3.start, 0x1000); + EXPECT_EQ(r3.end, 0x2FFF); + + // Test with invalid ranges + address_range empty; + address_range min_max2 = r1.get_min_max(empty); + EXPECT_EQ(min_max2.start, r1.start); + EXPECT_EQ(min_max2.end, r1.end); + + address_range min_max3 = empty.get_min_max(r1); + EXPECT_EQ(min_max3.start, r1.start); + EXPECT_EQ(min_max3.end, r1.end); + + address_range min_max4 = empty.get_min_max(empty); + EXPECT_EQ(min_max4.start, umax); + EXPECT_EQ(min_max4.end, 0); + } + + TEST(AddressRange, Intersect) + { + address_range r1 = address_range::start_length(0x1000, 0x2000); // 0x1000-0x2FFF + + // Complete overlap + address_range r2 = address_range::start_length(0x0, 0x4000); // 0x0-0x3FFF + address_range i1 = r1.get_intersect(r2); + EXPECT_EQ(i1.start, 0x1000); + EXPECT_EQ(i1.end, 0x2FFF); + + // Partial overlap at start + address_range r3 = address_range::start_length(0x0, 0x2000); // 0x0-0x1FFF + address_range i2 = r1.get_intersect(r3); + EXPECT_EQ(i2.start, 0x1000); + EXPECT_EQ(i2.end, 0x1FFF); + + // Partial overlap at end + address_range r4 = address_range::start_length(0x2000, 0x2000); // 0x2000-0x3FFF + address_range i3 = r1.get_intersect(r4); + EXPECT_EQ(i3.start, 0x2000); + EXPECT_EQ(i3.end, 0x2FFF); + + // No overlap + address_range r5 = address_range::start_length(0x4000, 0x1000); // 0x4000-0x4FFF + address_range i4 = r1.get_intersect(r5); + EXPECT_FALSE(i4.valid()); + + // Test intersect method + address_range r6 = address_range::start_length(0x1000, 0x2000); // 0x1000-0x2FFF + r6.intersect(r3); + EXPECT_EQ(r6.start, 0x1000); + EXPECT_EQ(r6.end, 0x1FFF); + } + + TEST(AddressRange, Validity) + { + // Valid range + address_range r1 = address_range::start_length(0x1000, 0x1000); + EXPECT_TRUE(r1.valid()); + + // Invalid range (default constructor) + address_range r2; + EXPECT_FALSE(r2.valid()); + + // Invalid range (start > end) + address_range r3 = address_range::start_end(0x2000, 0x1000); + EXPECT_FALSE(r3.valid()); + + // Invalidate + r1.invalidate(); + EXPECT_FALSE(r1.valid()); + EXPECT_EQ(r1.start, umax); + EXPECT_EQ(r1.end, 0); + } + + TEST(AddressRange, Comparison) + { + address_range r1 = address_range::start_length(0x1000, 0x1000); + address_range r2 = address_range::start_length(0x1000, 0x1000); + address_range r3 = address_range::start_length(0x2000, 0x1000); + + EXPECT_TRUE(r1 == r2); + EXPECT_FALSE(r1 == r3); + } + + TEST(AddressRange, StringRepresentation) + { + address_range r1 = address_range::start_length(0x1000, 0x1000); + std::string str = r1.str(); + + // The exact format may vary, but it should contain the start and end addresses + EXPECT_NE(str.find("1000"), std::string::npos); + EXPECT_NE(str.find("1fff"), std::string::npos); + } + + // Tests for address_range_vector + TEST(AddressRangeVector, BasicOperations) + { + address_range_vector vec; + EXPECT_TRUE(vec.empty()); + EXPECT_EQ(vec.size(), 0); + + // Add a range + vec.merge(address_range::start_length(0x1000, 0x1000)); + EXPECT_FALSE(vec.empty()); + EXPECT_EQ(vec.size(), 1); + + // Clear + vec.clear(); + EXPECT_TRUE(vec.empty()); + EXPECT_EQ(vec.size(), 0); + } + + TEST(AddressRangeVector, MergeOperations) + { + address_range_vector vec; + + // Add non-touching ranges + vec.merge(address_range::start_length(0x1000, 0x1000)); // 0x1000-0x1FFF + vec.merge(address_range::start_length(0x3000, 0x1000)); // 0x3000-0x3FFF + EXPECT_EQ(vec.valid_count(), 2); + + // Add a range that touches the first range + vec.merge(address_range::start_length(0x2000, 0x1000)); // 0x2000-0x2FFF + // Should merge all three ranges + EXPECT_EQ(vec.valid_count(), 1); + EXPECT_TRUE(vec.contains(address_range::start_end(0x1000, 0x3FFF))); + + // Add a non-touching range + vec.merge(address_range::start_length(0x5000, 0x1000)); // 0x5000-0x5FFF + EXPECT_EQ(vec.valid_count(), 2); + + // Add an overlapping range + vec.merge(address_range::start_length(0x4000, 0x2000)); // 0x4000-0x5FFF + EXPECT_EQ(vec.valid_count(), 1); + EXPECT_TRUE(vec.contains(address_range::start_end(0x1000, 0x5FFF))); + } + + TEST(AddressRangeVector, ExcludeOperations) + { + address_range_vector vec; + vec.merge(address_range::start_length(0x1000, 0x4000)); // 0x1000-0x4FFF + + // Exclude from the middle + vec.exclude(address_range::start_length(0x2000, 0x1000)); // 0x2000-0x2FFF + EXPECT_EQ(vec.valid_count(), 2); + + auto it = vec.begin(); + EXPECT_EQ(it->start, 0x1000); + EXPECT_EQ(it->end, 0x1FFF); + ++it; + EXPECT_EQ(it->start, 0x3000); + EXPECT_EQ(it->end, 0x4FFF); + + // Exclude from the start + vec.exclude(address_range::start_length(0x1000, 0x1000)); // 0x1000-0x1FFF + EXPECT_EQ(vec.valid_count(), 1); + EXPECT_TRUE(vec.contains(address_range::start_end(0x3000, 0x4FFF))); + + // Exclude from the end + vec.exclude(address_range::start_length(0x4000, 0x1000)); // 0x4000-0x4FFF + EXPECT_EQ(vec.valid_count(), 1); + EXPECT_TRUE(vec.contains(address_range::start_end(0x3000, 0x3FFF))); + + // Exclude entire range + vec.exclude(address_range::start_length(0x3000, 0x1000)); // 0x3000-0x3FFF + EXPECT_EQ(vec.valid_count(), 0); + + // Test excluding with another vector + vec.merge(address_range::start_length(0x1000, 0x4000)); // 0x1000-0x4FFF + + address_range_vector vec2; + vec2.merge(address_range::start_length(0x2000, 0x1000)); // 0x2000-0x2FFF + vec2.merge(address_range::start_length(0x4000, 0x1000)); // 0x4000-0x4FFF + + vec.exclude(vec2); + EXPECT_EQ(vec.valid_count(), 2); + + EXPECT_TRUE(vec.contains(address_range::start_end(0x1000, 0x1FFF))); + EXPECT_TRUE(vec.contains(address_range::start_end(0x3000, 0x3FFF))); + } + + TEST(AddressRangeVector, ConsistencyCheck) + { + address_range_vector vec; + vec.merge(address_range::start_length(0x1000, 0x1000)); // 0x1000-0x1FFF + vec.merge(address_range::start_length(0x3000, 0x1000)); // 0x3000-0x3FFF + + EXPECT_TRUE(vec.check_consistency()); + + // This would cause inconsistency, but merge should handle it + vec.merge(address_range::start_length(0x2000, 0x1000)); // 0x2000-0x2FFF + EXPECT_TRUE(vec.check_consistency()); + EXPECT_EQ(vec.valid_count(), 1); + } + + TEST(AddressRangeVector, OverlapsAndContains) + { + address_range_vector vec; + vec.merge(address_range::start_length(0x1000, 0x1000)); // 0x1000-0x1FFF + vec.merge(address_range::start_length(0x3000, 0x1000)); // 0x3000-0x3FFF + + // Test overlaps with range + EXPECT_TRUE(vec.overlaps(address_range::start_length(0x1500, 0x1000))); // 0x1500-0x24FF + EXPECT_TRUE(vec.overlaps(address_range::start_length(0x3500, 0x1000))); // 0x3500-0x44FF + EXPECT_FALSE(vec.overlaps(address_range::start_length(0x2000, 0x1000))); // 0x2000-0x2FFF + + // Test contains + EXPECT_TRUE(vec.contains(address_range::start_length(0x1000, 0x1000))); // 0x1000-0x1FFF + EXPECT_TRUE(vec.contains(address_range::start_length(0x3000, 0x1000))); // 0x3000-0x3FFF + EXPECT_FALSE(vec.contains(address_range::start_length(0x1500, 0x1000))); // 0x1500-0x24FF + + // Test overlaps with another vector + address_range_vector vec2; + vec2.merge(address_range::start_length(0x1500, 0x1000)); // 0x1500-0x24FF + EXPECT_TRUE(vec.overlaps(vec2)); + + address_range_vector vec3; + vec3.merge(address_range::start_length(0x2000, 0x1000)); // 0x2000-0x2FFF + EXPECT_FALSE(vec.overlaps(vec3)); + + // Test inside + address_range big_range = address_range::start_length(0x0, 0x5000); // 0x0-0x4FFF + EXPECT_TRUE(vec.inside(big_range)); + + address_range small_range = address_range::start_length(0x1000, 0x1000); // 0x1000-0x1FFF + EXPECT_FALSE(vec.inside(small_range)); + } + + // Test the std::hash implementation for address_range + TEST(AddressRange, Hash) + { + address_range r1 = address_range::start_length(0x1000, 0x1000); + address_range r2 = address_range::start_length(0x1000, 0x1000); + address_range r3 = address_range::start_length(0x2000, 0x1000); + + std::hash hasher; + EXPECT_EQ(hasher(r1), hasher(r2)); + EXPECT_NE(hasher(r1), hasher(r3)); + } +}