vcl/qa/cppunit/vclmaptest.cxx | 143 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+)
New commits: commit ebb5701a9acbd0912124250429022943bd311f45 Author: Christopher Sherlock <[email protected]> AuthorDate: Fri Dec 26 10:40:16 2025 +1100 Commit: Dan Williams <[email protected]> CommitDate: Tue Jan 13 07:11:01 2026 +0100 vcl: introduce some OutputDevice mapping tests specifically for fn3() Change-Id: I65a8aaa52fcb67416b2656faf3ff3e5e29d4fe92 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196463 Reviewed-by: Dan Williams <[email protected]> Tested-by: Jenkins diff --git a/vcl/qa/cppunit/vclmaptest.cxx b/vcl/qa/cppunit/vclmaptest.cxx index d06e6f34d0f3..04dfc34f2147 100644 --- a/vcl/qa/cppunit/vclmaptest.cxx +++ b/vcl/qa/cppunit/vclmaptest.cxx @@ -28,6 +28,13 @@ public: CPPUNIT_TEST(testFractionScaling); CPPUNIT_TEST(testIntermediateOverflow); CPPUNIT_TEST(testRoundingBehavior); + CPPUNIT_TEST(testOverflowProtectionPositive); + CPPUNIT_TEST(testOverflowProtectionNegative); + CPPUNIT_TEST(testRounding); + CPPUNIT_TEST(testTwipsTo100thMM); + CPPUNIT_TEST(testPointsToInch); + CPPUNIT_TEST(testZoomScaling); + CPPUNIT_TEST(testComplexFractions); CPPUNIT_TEST_SUITE_END(); public: @@ -121,6 +128,142 @@ public: CPPUNIT_ASSERT_EQUAL(tools::Long(-2), aResult2.X()); // -1.5 -> -2 CPPUNIT_ASSERT_EQUAL(tools::Long(-1), aResult2.Y()); // -1.4 -> -1 } + + void testOverflowProtectionPositive() + { + // Scenario: Convert Inch to MM (IsSimple() == true) + // Factor: 127 / 5 (1 inch = 25.4 mm = 254/10 = 127/5) + // + // We need an input N such that: + // 1. N * 127 > INT64_MAX (9.22e18) --> Triggers overflow flag in o3tl::convert + // 2. (N * 127) / 5 < INT64_MAX --> Final result fits in tools::Long + + constexpr tools::Long nInput = 100000000000000000LL; // 1e17 + constexpr tools::Long nExpected = 2540000000000000000LL; // 2.54e18 + + MapMode aSource(MapUnit::MapInch); + MapMode aDest(MapUnit::MapMM); + + // Sanity check: Ensure our MapModes are indeed "Simple" so we hit fn3 + CPPUNIT_ASSERT(aSource.IsSimple()); + CPPUNIT_ASSERT(aDest.IsSimple()); + + Point aPt(nInput, 0); + Point aResult = OutputDevice::LogicToLogic(aPt, aSource, aDest); + + CPPUNIT_ASSERT_EQUAL(nExpected, aResult.X()); + } + + void testOverflowProtectionNegative() + { + // Same as above, but testing negative handling in BigInt path + constexpr tools::Long nInput = -100000000000000000LL; // -1e17 + constexpr tools::Long nExpected = -2540000000000000000LL; + + MapMode aSource(MapUnit::MapInch); + MapMode aDest(MapUnit::MapMM); + + Point aPt(nInput, 0); + Point aResult = OutputDevice::LogicToLogic(aPt, aSource, aDest); + + CPPUNIT_ASSERT_EQUAL(nExpected, aResult.X()); + } + + void testRounding() + { + // Conversion: MapMM to MapInch + // Factor: 5 / 127 + + MapMode aSource(MapUnit::MapMM); + MapMode aDest(MapUnit::MapInch); + + // 13 * 5 / 127 = 65 / 127 ~= 0.5118 -> Rounds to 1 + Point aPt(13, 12); + // 12 * 5 / 127 = 60 / 127 ~= 0.47 -> Rounds to 0 + + Point aResult = OutputDevice::LogicToLogic(aPt, aSource, aDest); + + CPPUNIT_ASSERT_EQUAL(tools::Long(1), aResult.X()); + CPPUNIT_ASSERT_EQUAL(tools::Long(0), aResult.Y()); + } + + void testTwipsTo100thMM() + { + // 1 Twip = 1/1440 inch + // 1 inch = 2540 100thMM + // Factor = 2540 / 1440 = 127 / 72 ~= 1.7638 + + MapMode aSource(MapUnit::MapTwip); + MapMode aDest(MapUnit::Map100thMM); + + Point aPt(7200, 1440); // 5 inches, 1 inch + + Point aResult = OutputDevice::LogicToLogic(aPt, aSource, aDest); + + // 5 inches = 12700 100thMM + CPPUNIT_ASSERT_EQUAL(tools::Long(12700), aResult.X()); + // 1 inch = 2540 100thMM + CPPUNIT_ASSERT_EQUAL(tools::Long(2540), aResult.Y()); + } + + void testPointsToInch() + { + // 1 Point = 1/72 inch + MapMode aSource(MapUnit::MapPoint); + MapMode aDest(MapUnit::MapInch); + + Point aPt(72, 144); // 1 inch, 2 inches + + Point aResult = OutputDevice::LogicToLogic(aPt, aSource, aDest); + + CPPUNIT_ASSERT_EQUAL(tools::Long(1), aResult.X()); + CPPUNIT_ASSERT_EQUAL(tools::Long(2), aResult.Y()); + } + + void testZoomScaling() + { + // Simulating a View Zoom + // Source: 100thMM at 400% zoom (Scale 4:1) + // Dest: 100thMM at 100% zoom (Scale 1:1) + + MapMode aSource(MapUnit::Map100thMM); + aSource.SetScaleX(Fraction(4, 1)); + aSource.SetScaleY(Fraction(4, 1)); + + MapMode aDest(MapUnit::Map100thMM); + + Point aPt(100, 100); + // If I have 100 units at 400% zoom, that is physically 400 units. + // Converting to 100% zoom, it should remain 400 units. + + Point aResult = OutputDevice::LogicToLogic(aPt, aSource, aDest); + + CPPUNIT_ASSERT_EQUAL(tools::Long(400), aResult.X()); + CPPUNIT_ASSERT_EQUAL(tools::Long(400), aResult.Y()); + } + + void testComplexFractions() + { + // Use prime number fractions to ensure rational arithmetic + // is maintained without premature truncation. + + // Source Scale: 1/3 + // Dest Scale: 1/7 + // Conversion: Val * (1/3) / (1/7) = Val * 7/3 ~= Val * 2.333 + + MapMode aSource(MapUnit::MapMM); + aSource.SetScaleX(Fraction(1, 3)); + + MapMode aDest(MapUnit::MapMM); + aDest.SetScaleX(Fraction(1, 7)); + + Point aPt(300, 0); + // 300 * 7 / 3 = 100 * 7 = 700 + + Point aResult = OutputDevice::LogicToLogic(aPt, aSource, aDest); + + CPPUNIT_ASSERT_EQUAL(tools::Long(700), aResult.X()); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(VclMapTest);
