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);

Reply via email to