/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <sal/config.h>

#include "helper/qahelper.hxx"

#include <comphelper/propertyvalue.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/request.hxx>
#include <svl/intitem.hxx>
#include <svx/svdoashp.hxx>
#include <svx/svdomeas.hxx>
#include <svx/svdorect.hxx>
#include <svx/svdouno.hxx>
#include <svx/svdpage.hxx>
#include <vcl/keycodes.hxx>

#include <drwlayer.hxx>
#include <fuconcustomshape.hxx>
#include <fuconuno.hxx>
#include <postit.hxx>
#include <tabvwsh.hxx>
#include <userdat.hxx>

#include <sc.hrc> // defines of slot-IDs

using namespace css;

class ScShapeTest : public ScModelTestBase
{
public:
    ScShapeTest()
        : ScModelTestBase(u"sc/qa/unit/data"_ustr)
    {
    }
};

static SdrPage* lcl_getSdrPageWithAssert(ScDocument& rDoc)
{
    ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
    CPPUNIT_ASSERT_MESSAGE("No ScDrawLayer", pDrawLayer);
    SdrPage* pPage = pDrawLayer->GetPage(0);
    CPPUNIT_ASSERT_MESSAGE("No draw page", pPage);
    return pPage;
}

static SdrObject* lcl_getSdrObjectWithAssert(ScDocument& rDoc, sal_uInt16 nObjNumber,
                                             sal_uInt16 nPageIndex)
{
    ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
    CPPUNIT_ASSERT_MESSAGE("No ScDrawLayer", pDrawLayer);
    const SdrPage* pPage = pDrawLayer->GetPage(nPageIndex);
    OString sMsg1 = "No draw page " + OString::number(nPageIndex);
    CPPUNIT_ASSERT_MESSAGE(sMsg1.getStr(), pPage);
    SdrObject* pObj = pPage->GetObj(nObjNumber);
    OString sMsg2
        = "No Object " + OString::number(nObjNumber) + " on page " + OString::number(nPageIndex);
    CPPUNIT_ASSERT_MESSAGE(sMsg2.getStr(), pObj);
    return pObj;
}

static SdrObject* lcl_getSdrObjectWithAssert(ScDocument& rDoc, sal_uInt16 nObjNumber)
{
    return lcl_getSdrObjectWithAssert(rDoc, nObjNumber, 0);
}

static SdrObject* lcl_getSdrObjectbyName(ScDocument& rDoc, std::u16string_view rName)
{
    ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
    const SdrPage* pPage = pDrawLayer->GetPage(0);
    SdrObject* pObj = pPage->GetObjByName(rName);
    return pObj;
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf144242_OpenBezier_noSwapWH)
{
    // Shapes, which have rotation incorporated in their points, got erroneously width-height
    // swapped, because they report a rotation. (Rotation was introduced to align text with curve.)

    // Create a spreadsheet document with default row height and col width
    createScDoc();

    // Insert default open Bezier curve
    ScTabViewShell* pTabViewShell = getViewShell();
    SfxRequest aReq(pTabViewShell->GetViewFrame(), SID_DRAW_BEZIER_NOFILL);
    aReq.SetModifier(KEY_MOD1); // Ctrl
    pTabViewShell->ExecDraw(aReq);
    pTabViewShell->SetDrawShell(false);

    // Get document and newly created object
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Rotate object by 300deg
    pObj->Rotate(pObj->GetSnapRect().Center(), 30000_deg100, sin(toRadians(30000_deg100)),
                 cos(toRadians(30000_deg100)));
    tools::Rectangle aExpectRect(pObj->GetSnapRect());

    // Save, reload and compare
    saveAndReload(TestFilter::XLSX);
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    tools::Rectangle aSnapRect(pObj->GetSnapRect());
    // Without fix in place width and height were swapped
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectRect, aSnapRect, 40);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf144242_Line_noSwapWH)
{
    // Shapes, which have rotation incorporated in their points, got erroneously width-height
    // swapped, because they report a rotation. (Rotation was introduced to align text with line.)

    // Create a spreadsheet document with default row height and col width
    createScDoc();

    // Insert default line
    ScTabViewShell* pTabViewShell = getViewShell();
    SfxRequest aReq(pTabViewShell->GetViewFrame(), SID_DRAW_LINE);
    aReq.SetModifier(KEY_MOD1); // Ctrl
    pTabViewShell->ExecDraw(aReq);
    pTabViewShell->SetDrawShell(false);

    // Get document and newly created object
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Rotate object by 300deg
    pObj->Rotate(pObj->GetSnapRect().Center(), 30000_deg100, sin(toRadians(30000_deg100)),
                 cos(toRadians(30000_deg100)));
    tools::Rectangle aExpectRect(pObj->GetSnapRect());

    // Save, reload and compare
    saveAndReload(TestFilter::XLSX);
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    tools::Rectangle aSnapRect(pObj->GetSnapRect());
    // Without fix in place width and height were swapped
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectRect, aSnapRect, 40);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf143619_validation_circle_pos)
{
    // Load a document, which has validation circle around cell E6.
    createScDoc("ods/tdf143619_validationCirclePos.ods");

    // Get document
    ScDocument* pDoc = getScDoc();

    // Get shape. That is the validation circle.
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Error was, that deleting row and col before E6 does not move circle to D5, but to B3.
    // Delete first row and first column.
    goToCell(u"$A$1"_ustr);
    dispatchCommand(mxComponent, u".uno:DeleteRows"_ustr, {});
    goToCell(u"$A$1"_ustr);
    dispatchCommand(mxComponent, u".uno:DeleteColumns"_ustr, {});

    // Without fix in place the position was (2007, 833)
    Point aPos = pObj->GetSnapRect().TopLeft();
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(Point(6523, 1736), aPos, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf140252_DragCreateFormControl)
{
    // Error was, that drag-created form controls were initially not on layer 'controls' and thus
    // other shapes could be placed in front of form controls.
    // Load an empty document.
    createScDoc("ods/ManualColWidthRowHeight.ods");

    // Get ScTabViewShell
    ScTabViewShell* pTabViewShell = getViewShell();

    // drag-create a push button as example of form control
    SfxUInt16Item aIdentifierItem(SID_FM_CONTROL_IDENTIFIER,
                                  static_cast<sal_uInt16>(SdrObjKind::FormButton));
    SfxUInt32Item aInventorItem(SID_FM_CONTROL_INVENTOR, sal_uInt32(SdrInventor::FmForm));
    const SfxPoolItem* pArgs[] = { &aIdentifierItem, &aInventorItem, nullptr };
    pTabViewShell->GetViewData().GetDispatcher().Execute(SID_FM_CREATE_CONTROL,
                                                         SfxCallMode::SYNCHRON, pArgs);
    // above includes creation of FuConstUnoControl and call of its Activate() method

    // get FuConstUnoControl
    ScTabView* pTabView = pTabViewShell->GetViewData().GetView();
    CPPUNIT_ASSERT(pTabView);
    FuConstUnoControl* pFuConstUC = static_cast<FuConstUnoControl*>(pTabView->GetDrawFuncPtr());
    CPPUNIT_ASSERT(pFuConstUC);

    // drag-create shape, points are in pixel
    MouseEvent aMouseEvent(Point(50, 100), 1, MouseEventModifiers::NONE, MOUSE_LEFT, 0);
    pFuConstUC->MouseButtonDown(aMouseEvent);
    aMouseEvent = MouseEvent(Point(200, 250), 1, MouseEventModifiers::DRAGMOVE, MOUSE_LEFT, 0);
    pFuConstUC->MouseMove(aMouseEvent);
    aMouseEvent = MouseEvent(Point(200, 250), 1, MouseEventModifiers::NONE, MOUSE_LEFT, 0);
    pFuConstUC->MouseButtonUp(aMouseEvent);
    pFuConstUC->Deactivate();
    pTabViewShell->SetDrawShell(false);

    // Get document and newly created push button.
    ScDocument* pDoc = getScDoc();
    SdrUnoObj* pObj = static_cast<SdrUnoObj*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    // Without the fix in place, the shape would be on layer SC_LAYER_FRONT (0)
    sal_Int16 nExpectedID = SC_LAYER_CONTROLS.get();
    sal_Int16 nActualID = pObj->GetLayer().get();
    CPPUNIT_ASSERT_EQUAL(nExpectedID, nActualID);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf134355_DragCreateCustomShape)
{
    // Error was, that drag-created custom shapes were initially on layer "controls", although that
    // layer is exclusively for form controls. Effect was, that other shapes could not be brought in
    // front of custom shapes.
    // Load an empty document.
    createScDoc("ods/ManualColWidthRowHeight.ods");

    // Get ScTabView
    ScTabViewShell* pTabViewShell = getViewShell();
    ScTabView* pTabView = pTabViewShell->GetViewData().GetView();

    // drag-create custom shape
    cpo::uno::Sequence<beans::PropertyValue> aPropertyValues = {
        comphelper::makePropertyValue(u"SymbolShapes"_ustr, u"smiley"_ustr),
    };
    dispatchCommand(mxComponent, u".uno:SymbolShapes"_ustr, aPropertyValues);
    // above includes creation of FuConstCustomShape and call of its Activate() method
    FuConstCustomShape* pFuConstCS = static_cast<FuConstCustomShape*>(pTabView->GetDrawFuncPtr());
    CPPUNIT_ASSERT(pFuConstCS);
    // points are in pixel
    MouseEvent aMouseEvent(Point(50, 100), 1, MouseEventModifiers::NONE, MOUSE_LEFT, 0);
    pFuConstCS->MouseButtonDown(aMouseEvent);
    aMouseEvent = MouseEvent(Point(200, 250), 1, MouseEventModifiers::DRAGMOVE, MOUSE_LEFT, 0);
    pFuConstCS->MouseMove(aMouseEvent);
    aMouseEvent = MouseEvent(Point(200, 250), 1, MouseEventModifiers::NONE, MOUSE_LEFT, 0);
    pFuConstCS->MouseButtonUp(aMouseEvent);
    pFuConstCS->Deactivate();
    pTabViewShell->SetDrawShell(false);

    // Get document and newly created custom shape.
    ScDocument* pDoc = getScDoc();
    SdrObjCustomShape* pObj = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    // Without the fix in place, the shape would be on layer SC_LAYER_CONTROLS (3)
    sal_Int16 nExpectedID = SC_LAYER_FRONT.get();
    sal_Int16 nActualID = pObj->GetLayer().get();
    CPPUNIT_ASSERT_EQUAL(nExpectedID, nActualID);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf140252_LayerOfControl)
{
    // Error was, that a newly inserted control shape was put on layer
    // "vorne" instead of layer "control".
    // Load an empty document.
    createScDoc("ods/ManualColWidthRowHeight.ods");

    // Create default push button
    SfxUInt16Item aIdentifierItem(SID_FM_CONTROL_IDENTIFIER,
                                  static_cast<sal_uInt16>(SdrObjKind::FormButton));
    SfxUInt32Item aInventorItem(SID_FM_CONTROL_INVENTOR, sal_uInt32(SdrInventor::FmForm));
    const SfxPoolItem* pArgs[] = { &aIdentifierItem, &aInventorItem, nullptr };
    const SfxPoolItem* pInternalArgs[] = { nullptr };
    ScTabViewShell* pTabViewShell = getViewShell();
    pTabViewShell->GetViewData().GetDispatcher().Execute(
        SID_FM_CREATE_CONTROL, SfxCallMode::SYNCHRON, pArgs, KEY_MOD1, pInternalArgs);

    // Get document and newly created push button.
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Check LayerID of object. Without the fix in place it was 0.
    sal_Int16 nExpectedID = SC_LAYER_CONTROLS.get();
    sal_Int16 nActualID = pObj->GetLayer().get();
    CPPUNIT_ASSERT_EQUAL(nExpectedID, nActualID);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137082_LTR_to_RTL)
{
    // Before the fix for tdf137081 and tdf137082, when flipping sheet from LTR to RTL, page anchored
    // shapes were mirrored, but cell anchored shapes not. This was changed so, that shapes are always
    // mirrored. Graphics are still not mirrored but shifted. This test makes sure a shape is mirrored
    // and an image is not mirrored.

    createScDoc("ods/tdf137082_LTR_arrow_image.ods");

    // Get document
    ScDocument* pDoc = getScDoc();

    // Get objects and their transformation angles
    SdrObject* pObjCS = lcl_getSdrObjectWithAssert(*pDoc, 0);
    const Degree100 nRotateLTR = pObjCS->GetRotateAngle();
    SdrObject* pObjImage = lcl_getSdrObjectWithAssert(*pDoc, 1);
    const Degree100 nShearLTR = pObjImage->GetShearAngle();

    // Switch to RTL
    ScTabViewShell* pViewShell = getViewShell();
    pViewShell->GetViewData().GetDispatcher().Execute(FID_TAB_RTL);

    // Check custom shape is mirrored, image not.
    const Degree100 nShearRTLActual = pObjImage->GetShearAngle();
    CPPUNIT_ASSERT_EQUAL_MESSAGE("image should not be mirrored", nShearLTR.get(),
                                 nShearRTLActual.get());
    const Degree100 nRotateRTLExpected = 36000_deg100 - nRotateLTR;
    const Degree100 nRotateRTLActual = pObjCS->GetRotateAngle();
    CPPUNIT_ASSERT_EQUAL_MESSAGE("custom shape should be mirrored", nRotateRTLExpected.get(),
                                 nRotateRTLActual.get());
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137082_RTL_cell_anchored)
{
    // Error was, that cell anchored custom shapes wrote wrong offsets to file and thus were wrong on
    // reloading. The file contains one custom shape with "resize" and another one without.
    createScDoc("ods/tdf137082_RTL_cell_anchored.ods");

    // Get document
    ScDocument* pDoc = getScDoc();

    // Expected values.
    const Point aTopLeftA(-20500, 3500); // shape A without "resize"
    const Point aTopLeftB(-9500, 3500); // shape B with "resize"
    const Size aSize(2278, 5545); // both
    const tools::Rectangle aSnapRectA(aTopLeftA, aSize);
    const tools::Rectangle aSnapRectB(aTopLeftB, aSize);

    // Test reading was correct
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aSnapRectA, pObj->GetSnapRect(), 1);
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 1);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aSnapRectB, pObj->GetSnapRect(), 1);

    // Save and reload.
    saveAndReload(TestFilter::ODS);

    // Get document
    pDoc = getScDoc();

    // And test again
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aSnapRectA, pObj->GetSnapRect(), 1);
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 1);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aSnapRectB, pObj->GetSnapRect(), 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137081_RTL_page_anchored)
{
    // Error was, that page anchored lines and custom shapes were mirrored on opening. The document
    // contains measure line, polyline and transformed custom shape.
    createScDoc("ods/tdf137081_RTL_page_anchored.ods");

    // Get document
    ScDocument* pDoc = getScDoc();

    // Expected values.
    // Measure line
    const Point aStart(-3998, 2490);
    const Point aEnd(-8488, 5490);
    // Polyline
    const Point aFirst(-10010, 2500);
    const Point aSecond(-14032, 5543);
    const Point aThird(-14500, 3500);
    // Custom shape
    const Point aTopLeft(-20500, 4583);

    // Test reading was correct
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    // Measure line
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aStart, pObj->GetPoint(0), 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aEnd, pObj->GetPoint(1), 1);
    // Polyline
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aFirst, pObj->GetPoint(0), 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aSecond, pObj->GetPoint(1), 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aThird, pObj->GetPoint(2), 1);
    //Custom shape
    SdrObjCustomShape* pObjCS
        = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 2));
    CPPUNIT_ASSERT(!pObjCS->IsMirroredX());
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aTopLeft, pObjCS->GetLogicRect().TopLeft(), 1);

    // Save and reload.
    saveAndReload(TestFilter::ODS);

    // Get document
    pDoc = getScDoc();

    // And test again
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    // Measure line
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aStart, pObj->GetPoint(0), 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aEnd, pObj->GetPoint(1), 1);
    // Polyline
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aFirst, pObj->GetPoint(0), 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aSecond, pObj->GetPoint(1), 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aThird, pObj->GetPoint(2), 1);
    //Custom shape
    pObjCS = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 2));
    CPPUNIT_ASSERT(!pObjCS->IsMirroredX());
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aTopLeft, pObjCS->GetLogicRect().TopLeft(), 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf139583_Rotate180deg)
{
    // Load an empty document.
    createScDoc("ods/ManualColWidthRowHeight.ods");

    // Get document and draw page
    ScDocument* pDoc = getScDoc();
    SdrPage* pPage = lcl_getSdrPageWithAssert(*pDoc);

    // Insert Shape
    const tools::Rectangle aRect(Point(3000, 4000), Size(5000, 2000));
    ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
    CPPUNIT_ASSERT_MESSAGE("No ScDrawLayer", pDrawLayer);
    rtl::Reference<SdrRectObj> pObj = new SdrRectObj(*pDrawLayer, aRect);
    CPPUNIT_ASSERT_MESSAGE("Could not create rectangle", pObj);
    pPage->InsertObject(pObj.get());

    // Anchor "to cell (resize with cell)" and then rotate it by 180deg around center
    // The order is important here.
    ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/);
    pObj->Rotate(aRect.Center(), Degree100(18000), 0.0, -1.0);
    pObj.clear();

    // Save and reload.
    saveAndReload(TestFilter::ODS);

    // Get document and object
    pDoc = getScDoc();
    pObj = static_cast<SdrRectObj*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    //  Without the fix in place, the shape would have nearly zero size.
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aRect, pObj->GetSnapRect(), 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137033_FlipHori_Resize)
{
    // Load a document, which has a rotated custom shape, which is horizontal flipped. Error was, that
    // if such shape was anchored "resize with cell", then after save and reload it was distorted.
    createScDoc("ods/tdf137033_FlipHoriRotCustomShape.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrObjCustomShape* pObj = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    // Verify shape is correctly loaded. Then set shape to "resize with cell".
    tools::Rectangle aSnapRect(pObj->GetSnapRect());
    const tools::Rectangle aExpectRect(Point(4998, 7000), Size(9644, 6723));
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectRect, aSnapRect, 1);
    ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/);

    // Save and reload.
    saveAndReload(TestFilter::ODS);

    // Get document and shape
    pDoc = getScDoc();
    pObj = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    // Check shape has the original geometry, besides rounding and unit conversion errors
    aSnapRect = pObj->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectRect, aSnapRect, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137033_RotShear_ResizeHide)
{
    // For rotated or sheared shapes anchored "To Cell (resize with cell) hiding rows or columns will
    // not only change size but rotation and shear angle too. Error was, that not the original angles
    // of the full sized shape were written to file but the changed one.

    // Load a document, which has a rotated and sheared shape, anchored to cell with resize.
    createScDoc("ods/tdf137033_RotShearResizeAnchor.ods");

    // Get document
    ScDocument* pDoc = getScDoc();

    // Hide rows 4 and 5 (UI number), which are inside the shape and thus change shape geometry
    pDoc->SetRowHidden(3, 4, 0, true);
    pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not changed

    // Get shape
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Verify hiding has changed shape geometry as expected
    tools::Rectangle aSnapRect(pObj->GetSnapRect());
    Degree100 aRotateAngle(pObj->GetRotateAngle());
    Degree100 aShearAngle(pObj->GetShearAngle());
    // mathematical exact would be Point(3868, 4795), Size(9763, 1909)
    // current values as of LO 7.2
    const tools::Rectangle aExpectRect(Point(3875, 4796), Size(9760, 1911));
    const Degree100 aExpectRotateAngle(20925_deg100);
    const Degree100 aExpectShearAngle(-6570_deg100);
    CPPUNIT_ASSERT_MESSAGE("Hide rows, shear angle: ",
                           abs(aShearAngle - aExpectShearAngle) <= 1_deg100);
    CPPUNIT_ASSERT_MESSAGE("Hide rows, rotate angle: ",
                           abs(aRotateAngle - aExpectRotateAngle) <= 1_deg100);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectRect, aSnapRect, 1);

    // Save and reload.
    saveAndReload(TestFilter::ODS);

    // Get document and shape
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Check shape has the original geometry, besides heavy rounding and unit conversion errors
    aSnapRect = pObj->GetSnapRect();
    aRotateAngle = pObj->GetRotateAngle();
    aShearAngle = pObj->GetShearAngle();
    CPPUNIT_ASSERT_MESSAGE("Reload, shear angle: ",
                           abs(aShearAngle - aExpectShearAngle) <= 3_deg100);
    CPPUNIT_ASSERT_MESSAGE("Reload, rotate angle: ",
                           abs(aRotateAngle - aExpectRotateAngle) <= 3_deg100);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectRect, aSnapRect, 7);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137033_RotShear_Hide)
{
    // Hiding row or columns affect cell anchored shape based on their snap rectangle. The first
    // attempt to fix lost position has used the logic rect instead. For rotated or sheared shape it
    // makes a difference.

    // Load a document, which has a rotated and sheared shape, anchored to cell, without resize.
    createScDoc("ods/tdf137033_RotShearCellAnchor.ods");

    // Get document
    ScDocument* pDoc = getScDoc();

    // Hide column C, which is left from logic rect, but right from left edge of snap rect
    pDoc->SetColHidden(2, 2, 0, true);
    pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not changed

    // Save and reload.
    saveAndReload(TestFilter::ODS);

    // Get document and shape
    pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Check shape is visible. With the old version, the shape was moved to column C and
    // thus hidden on reload.
    CPPUNIT_ASSERT_MESSAGE("Reload: Shape has to be visible", pObj->IsVisible());
    // Verify position and size are unchanged besides rounding and unit conversion errors
    // Values are manually taken from shape before hiding column C.
    const tools::Rectangle aExpectRect(Point(4500, 3500), Size(15143, 5187));
    const tools::Rectangle aSnapRect = pObj->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectRect, aSnapRect, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137576_LogicRectInDefaultMeasureline)
{
    // Error was, that the empty logical rectangle of a default measure line (Ctrl+Click)
    // resulted in zeros in NonRotatedAnchor and a wrong position when reloading.

    // Load an empty document.
    createScDoc("ods/ManualColWidthRowHeight.ods");

    // Create default measureline by SfxRequest that corresponds to Ctrl+Click
    ScTabViewShell* pTabViewShell = getViewShell();
    SfxRequest aReq(pTabViewShell->GetViewFrame(), SID_DRAW_MEASURELINE);
    aReq.SetModifier(KEY_MOD1); // Ctrl
    pTabViewShell->ExecDraw(aReq);

    // Get document and newly created measure line.
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Anchor "to Cell (resize with cell)"
    ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/);
    // Deselect shape and switch to object selection type "Cell".
    pTabViewShell->SetDrawShell(false);

    // Hide column A.
    goToCell(u"$A$1"_ustr);
    dispatchCommand(mxComponent, u".uno:HideColumn"_ustr, {});

    // Get current position. I will not use absolute values for comparison, because document is loaded
    // in full screen mode of unknown size and default object is placed in center of window.
    Point aOldPos = pObj->GetRelativePos();

    // Save and reload, get ScDocShell
    saveAndReload(TestFilter::ODS);

    // Get document and object
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Assert object position is unchanged, besides Twips<->Hmm inaccuracy.
    Point aNewPos = pObj->GetRelativePos();
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aOldPos, aNewPos, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137576_LogicRectInNewMeasureline)
{
    // Error was, that a new measure line had no logical rectangle. This resulted in zeros in
    // NonRotatedAnchor. As a result the position was wrong when reloading.

    // Load an empty document
    createScDoc("ods/ManualColWidthRowHeight.ods");

    // Get document and draw page
    ScDocument* pDoc = getScDoc();
    SdrPage* pPage = lcl_getSdrPageWithAssert(*pDoc);

    // Create a new measure line and insert it
    Point aStartPoint(5000, 5500);
    Point aEndPoint(13000, 8000);
    ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
    CPPUNIT_ASSERT_MESSAGE("No ScDrawLayer", pDrawLayer);
    rtl::Reference<SdrMeasureObj> pObj = new SdrMeasureObj(*pDrawLayer, aStartPoint, aEndPoint);
    CPPUNIT_ASSERT_MESSAGE("Could not create measure line", pObj);
    pPage->InsertObject(pObj.get());

    // Anchor "to cell (resize with cell)" and examine NonRotatedAnchor
    ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/);
    ScDrawObjData* pNData = ScDrawLayer::GetNonRotatedObjData(pObj.get());
    CPPUNIT_ASSERT_MESSAGE("Failed to get NonRotatedAnchor", pNData);
    // Without the fix all four values would be zero.
    CPPUNIT_ASSERT_EQUAL(SCCOL(1), pNData->maStart.Col());
    CPPUNIT_ASSERT_EQUAL(SCROW(2), pNData->maStart.Row());
    CPPUNIT_ASSERT_EQUAL(SCCOL(7), pNData->maEnd.Col());
    CPPUNIT_ASSERT_EQUAL(SCROW(2), pNData->maEnd.Row());

    pObj.clear();
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testMeasurelineHideColSave)
{
    // The document contains a SdrMeasureObj anchored "To Cell (resize with cell)" with start in cell
    // D11 and end in cell I5. Error was, that after hiding col A and saving, start and end point
    // position were lost.
    createScDoc("ods/measurelineHideColSave.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Make sure loading is correct
    Point aStartPoint(7500, 15000); // according UI
    Point aEndPoint(17500, 8000);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aStartPoint, pObj->GetPoint(0), 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aEndPoint, pObj->GetPoint(1), 1);

    // Hide column A
    pDoc->SetColHidden(0, 0, 0, true);
    pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not changed
    // Shape should move by column width, here 3000
    aStartPoint.Move(-3000, 0);
    aEndPoint.Move(-3000, 0);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aStartPoint, pObj->GetPoint(0), 1);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aEndPoint, pObj->GetPoint(1), 1);

    // save and reload
    saveAndReload(TestFilter::ODS);

    // Get document and shape
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Check that start and end point are unchanged besides rounding and unit conversion errors
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aStartPoint, pObj->GetPoint(0), 2);
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aEndPoint, pObj->GetPoint(1), 2);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testHideColsShow)
{
    // The document contains a shape anchored "To Cell (resize with cell)" with starts in cell C3 and
    //ends in cell D5. Error was, that hiding cols C and D and then show them again extends the shape
    // to column E

    createScDoc("ods/hideColsShow.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrObjCustomShape* pObj = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    CPPUNIT_ASSERT_MESSAGE("Load: Object should be visible", pObj->IsVisible());
    tools::Rectangle aSnapRectOrig(pObj->GetSnapRect());

    // Hide cols C and D.
    goToCell(u"$C$1:$D$1"_ustr);

    ScTabViewShell* pViewShell = getViewShell();
    pViewShell->GetViewData().GetDispatcher().Execute(FID_COL_HIDE);

    // Check object is invisible
    CPPUNIT_ASSERT_MESSAGE("Hide: Object should be invisible", !pObj->IsVisible());

    // Show cols C and D
    goToCell(u"$C$1:$D$1"_ustr);
    pViewShell->GetViewData().GetDispatcher().Execute(FID_COL_SHOW);

    // Check object is visible and has old size
    CPPUNIT_ASSERT_MESSAGE("Show: Object should be visible", pObj->IsVisible());
    tools::Rectangle aSnapRectShow(pObj->GetSnapRect());
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aSnapRectOrig, aSnapRectShow, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testFormSizeWithHiddenCol)
{
    // The document contains a form (Listbox) shape anchored "To Cell (resize with cell)" with starts in cell B5 and
    // ends in cell D5. The error was the form shape was resized if there was hidden col/row.

    createScDoc("ods/tdf154005.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrUnoObj* pObj = static_cast<SdrUnoObj*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    // Check Position and Size
    pDoc->SetDrawPageSize(0); // trigger recalcpos
    tools::Rectangle aRect(2432, 3981, 4932, 4631); // expected snap rect from values in file
    const tools::Rectangle& rShapeRect(pObj->GetSnapRect());
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aRect, rShapeRect, 1);

    // Check anchor
    ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj);
    CPPUNIT_ASSERT_MESSAGE("expected object meta data", pData);

    const OUString sActual("start col " + OUString::number(pData->maStart.Col()) + " row "
                           + OUString::number(pData->maStart.Row()) + " end col "
                           + OUString::number(pData->maEnd.Col()) + " row "
                           + OUString::number(pData->maEnd.Row()));
    CPPUNIT_ASSERT_EQUAL(u"start col 1 row 4 end col 3 row 4"_ustr, sActual);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf138138_MoveCellWithRotatedShape)
{
    // The document contains a 90deg rotated, cell-anchored rectangle in column D. Insert 2 columns
    // after column B, save and reload. The shape was not correctly moved to column F.
    createScDoc("ods/tdf138138_MoveCellWithRotatedShape.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Check anchor and position of shape. The expected values are taken from UI.
    tools::Rectangle aSnapRect = pObj->GetSnapRect();
    tools::Rectangle aExpectedRect(Point(10000, 3000), Size(1000, 7500));
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect, aSnapRect, 1);

    // Insert two columns after column B
    goToCell(u"$A$1:$B$1"_ustr);

    ScTabViewShell* pViewShell = getViewShell();
    pViewShell->GetViewData().GetDispatcher().Execute(FID_INS_COLUMNS_AFTER);
    aExpectedRect = tools::Rectangle(Point(16000, 3000), Size(1000, 7500)); // col width 3000
    aSnapRect = pObj->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect, aSnapRect, 1);

    // Save and reload
    saveAndReload(TestFilter::ODS);

    // Get document and shape
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Assert objects size is unchanged, position is shifted.
    aSnapRect = pObj->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect, aSnapRect, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testLoadVerticalFlip)
{
    // The document has a cell anchored custom shape with vertical flip. Error was, that the
    // flip was lost on loading.
    createScDoc("ods/loadVerticalFlip.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrObjCustomShape* pObj = static_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    // Check that shape is flipped
    CPPUNIT_ASSERT_MESSAGE("Load: Object should be vertically flipped", pObj->IsMirroredY());
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf117948_CollapseBeforeShape)
{
    // The document contains a column group left from the image. The group is expanded. Collapse the
    // group, save and reload. The original error was, that the line was on wrong position after reload.
    // After the fix for 'resize with cell', the custom shape had wrong position and size too.
    createScDoc("ods/tdf117948_CollapseBeforeShape.ods");

    // Get document and objects
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj0 = lcl_getSdrObjectWithAssert(*pDoc, 0);
    SdrObject* pObj1 = lcl_getSdrObjectWithAssert(*pDoc, 1);

    // Collapse the group
    ScTabViewShell* pViewShell = getViewShell();
    pViewShell->GetViewData().SetCurX(1);
    pViewShell->GetViewData().SetCurY(0);
    pViewShell->GetViewData().GetDispatcher().Execute(SID_OUTLINE_HIDE);

    // Check anchor and position of shape. The expected values are taken from UI before saving.
    tools::Rectangle aSnapRect0Collapse = pObj0->GetSnapRect();
    tools::Rectangle aExpectedRect0(Point(4672, 1334), Size(1787, 1723));
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect0, aSnapRect0Collapse, 1);
    tools::Rectangle aSnapRect1Collapse = pObj1->GetSnapRect();
    tools::Rectangle aExpectedRect1(Point(5647, 4172), Size(21, 3441));
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect1, aSnapRect1Collapse, 1);

    // Save and reload
    saveAndReload(TestFilter::ODS);

    // Get document and objects
    pDoc = getScDoc();
    pObj0 = lcl_getSdrObjectWithAssert(*pDoc, 0);
    pObj1 = lcl_getSdrObjectWithAssert(*pDoc, 1);

    // Assert objects size and position are not changed. Actual values differ a little bit
    // because of cumulated Twips-Hmm conversion errors.
    tools::Rectangle aSnapRect0Reload = pObj0->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect0, aSnapRect0Reload, 2);

    tools::Rectangle aSnapRect1Reload = pObj1->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect1, aSnapRect1Reload, 2);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf137355_UndoHideRows)
{
    // The document contains a shape anchored "To Cell" with start in cell C3 and end in cell D6.
    // Error was, that hiding rows 3 to 6 and undo that action "lost" the shape.
    // Actually it was not lost but hidden.
    createScDoc("ods/tdf137355_UndoHideRows.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    CPPUNIT_ASSERT_MESSAGE("Load: Object should be visible", pObj->IsVisible());
    tools::Rectangle aSnapRectOrig(pObj->GetSnapRect());

    // Hide rows 3 to 6 in UI. [Note: Simple pDoc->SetRowHidden(2,5,0,true) does not work, because it
    // does not produce the needed undo items.]
    goToCell(u"$A$3:$A$6"_ustr);
    ScTabViewShell* pViewShell = getViewShell();
    pViewShell->GetViewData().GetDispatcher().Execute(FID_ROW_HIDE);

    // Check object is invisible
    CPPUNIT_ASSERT_MESSAGE("Hide: Object should be invisible", !pObj->IsVisible());

    // Undo
    pViewShell->GetViewData().GetDispatcher().Execute(SID_UNDO);

    // Check object is visible and has old size
    CPPUNIT_ASSERT_MESSAGE("Undo: Object should exist", pObj);
    CPPUNIT_ASSERT_MESSAGE("Undo: Object should be visible", pObj->IsVisible());
    tools::Rectangle aSnapRectUndo(pObj->GetSnapRect());
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aSnapRectOrig, aSnapRectUndo, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf152081_UndoHideColsWithNotes)
{
    createScDoc("ods/tdf152081_UndoHideColsWithNotes.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    // A note gets its caption only when something asks for it, and with the Kit the sheet never
    // shows one, so ask for it before looking for it on the drawing page.
    std::vector<sc::NoteEntry> aNotes;
    pDoc->GetAllNoteEntries(aNotes);
    CPPUNIT_ASSERT_EQUAL(size_t(1), aNotes.size());
    aNotes[0].mpNote->GetOrCreateCaption(aNotes[0].maPos);

    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    CPPUNIT_ASSERT_MESSAGE("Load: Note object should be visible", pObj->IsVisible());

    // Hide B column
    goToCell(u"$B$2:$B$2"_ustr);
    ScTabViewShell* pViewShell = getViewShell();
    pViewShell->GetViewData().GetDispatcher().Execute(FID_COL_HIDE);

    // Check object is invisible
    CPPUNIT_ASSERT_MESSAGE("Hide: Note object should be invisible", !pObj->IsVisible());

    // Undo
    pViewShell->GetViewData().GetDispatcher().Execute(SID_UNDO);

    // Check object is visible
    CPPUNIT_ASSERT_MESSAGE("Undo: Note object should exist", pObj);
    CPPUNIT_ASSERT_MESSAGE("Undo: Note object should be visible", pObj->IsVisible());
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf115655_HideDetail)
{
    // The document contains an image inside a cell anchored "To Cell (resize with cell)". The cell
    // belongs to a group. On loading the group is expanded.
    // Error was, that after collapsing the group, save and reload, and expanding the group, the image
    // was "lost". Actually is was resized to zero height.
    createScDoc("ods/tdf115655_HideDetail.ods");

    // Get document and image
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Get image size
    tools::Rectangle aSnapRectOrig = pObj->GetSnapRect();

    // Collapse the group
    ScTabViewShell* pViewShell = getViewShell();
    pViewShell->GetViewData().SetCurX(0);
    pViewShell->GetViewData().SetCurY(1);
    pViewShell->GetViewData().GetDispatcher().Execute(SID_OUTLINE_HIDE);
    CPPUNIT_ASSERT_MESSAGE("Collapse: Image should not be visible", !pObj->IsVisible());

    // Save and reload
    saveAndReload(TestFilter::ODS);

    // Get document and image
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Expand the group
    pViewShell = getViewShell();
    pViewShell->GetViewData().SetCurX(0);
    pViewShell->GetViewData().SetCurY(1);
    pViewShell->GetViewData().GetDispatcher().Execute(SID_OUTLINE_SHOW);
    CPPUNIT_ASSERT_MESSAGE("Expand: Image should be visible", pObj->IsVisible());

    // Assert image size is not changed
    tools::Rectangle aSnapRectReload = pObj->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aSnapRectOrig, aSnapRectReload, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testFitToCellSize)
{
    // The document has a cell anchored custom shape. Applying
    // FitToCellSize should resize and position the shape so,
    // that it fits into its anchor cell. That did not happened.
    createScDoc("ods/tdf119191_FitToCellSize.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrObjCustomShape* pObj
        = dynamic_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    // Get the draw view of the document
    ScTabViewShell* pViewShell = getViewShell();
    ScDrawView* pDrawView = pViewShell->GetViewData().GetScDrawView();
    CPPUNIT_ASSERT(pDrawView);

    // Select the shape
    pDrawView->MarkNextObj();
    CPPUNIT_ASSERT(pDrawView->GetMarkedObjectList().GetMarkCount() != 0);

    // Fit selected shape into cell
    pViewShell->GetViewData().GetDispatcher().Execute(SID_FITCELLSIZE);

    const tools::Rectangle& rShapeRect(pObj->GetSnapRect());
    const tools::Rectangle aCellRect = pDoc->GetMMRect(1, 1, 1, 1, 0);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aCellRect, rShapeRect, 2);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testCustomShapeCellAnchoredRotatedShape)
{
    // The example doc contains a cell anchored custom shape that is rotated
    // and sheared. Error was, that the shape lost position and size on
    // loading.
    createScDoc("ods/tdf119191_transformedShape.ods");

    // Get document and shape
    ScDocument* pDoc = getScDoc();
    SdrObjCustomShape* pObj
        = dynamic_cast<SdrObjCustomShape*>(lcl_getSdrObjectWithAssert(*pDoc, 0));

    // Check Position and Size
    pDoc->SetDrawPageSize(0); // trigger recalcpos
    tools::Rectangle aRect(2400, 751, 5772, 3694); // expected snap rect from values in file
    const tools::Rectangle& rShapeRect(pObj->GetSnapRect());
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aRect, rShapeRect, 1);

    // Check anchor
    ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj);
    CPPUNIT_ASSERT_MESSAGE("expected object meta data", pData);

    const OUString sActual("start col " + OUString::number(pData->maStart.Col()) + " row "
                           + OUString::number(pData->maStart.Row()) + " end col "
                           + OUString::number(pData->maEnd.Col()) + " row "
                           + OUString::number(pData->maEnd.Row()));
    CPPUNIT_ASSERT_EQUAL(u"start col 1 row 1 end col 2 row 8"_ustr, sActual);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testLargeAnchorOffset)
{
    // The example doc contains a resize-with-cell-anchored measure line
    // with a large vertical offset that shifts the start point onto the
    // next cell below.
    createScDoc("ods/LargeAnchorOffset.ods");

    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    const Point aOldPos = pObj->GetRelativePos();
    // Just to check that it imports correctly
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(Point(9504, 9089), aOldPos, 1);

    saveAndReload(TestFilter::ODS);

    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Without the fix, this would fail:
    //   Test name: sc_apitest::ScShapeTest::testLargeAnchorOffset
    //   assertion failed
    //   - Expression: std::abs(rExpected.Y() - rActual.Y()) <= nTolerance
    //   - after reload Y expected 9089 actual 9643 Tolerance 1
    const Point aNewPos = pObj->GetRelativePos();
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aOldPos, aNewPos, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf139083_copy_without_resize)
{
    // Load a document, which has a shape anchored to cell B2, but without 'resize with cell'.
    // When the range B2:B3 is copied and pasted to D5, then the copied shape should keep its size.
    createScDoc("ods/tdf139083_copy_without_resize.ods");

    // Get document
    ScDocument* pDoc = getScDoc();

    // Copy cells B2:B3. They have row height 2cm and column width 3cm.
    goToCell(u"$B$2:$B$3"_ustr);
    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});

    // Paste to D5. There are row height 0.5cm and column width 1cm.
    goToCell(u"$D$5"_ustr);
    dispatchCommand(mxComponent, u".uno:Paste"_ustr, {});

    // Make sure original and pasted shape have the same size.
    // Size of original shape is 2001x3002, without fix size of pasted shape was 668x750.
    SdrObject* pObjOrig = lcl_getSdrObjectWithAssert(*pDoc, 0); // original shape
    SdrObject* pObjPasted = lcl_getSdrObjectWithAssert(*pDoc, 1); // pasted shape
    CPPUNIT_ASSERT_DOUBLES_EQUAL(2001, pObjOrig->GetSnapRect().GetWidth(), 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3002, pObjOrig->GetSnapRect().GetHeight(), 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(2001, pObjPasted->GetSnapRect().GetWidth(), 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(3002, pObjPasted->GetSnapRect().GetHeight(), 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf155093_double_names)
{
    // Load a document, which has a shape in range B6:C14 with name "myArrow". When the range was
    // copied and pasted, then the copied shape got the same name and thus was not accessible with
    // Navigator.
    createScDoc("ods/tdf155093_double_names.ods");
    ScDocument* pDoc = getScDoc();

    // Copy and paste
    goToCell(u"$B$6:$C$14"_ustr);
    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
    goToCell(u"$D$16"_ustr);
    dispatchCommand(mxComponent, u".uno:Paste"_ustr, {});

    // Make sure original and pasted shape have different names.
    SdrObject* pObjOrig = lcl_getSdrObjectWithAssert(*pDoc, 0); // original shape
    SdrObject* pObjPasted = lcl_getSdrObjectWithAssert(*pDoc, 1); // pasted shape
    CPPUNIT_ASSERT(pObjOrig->GetName() != pObjPasted->GetName());
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf155095_shape_collapsed_group)
{
    // Load a document, which has a shape in range C9:C16, anchored 'To cell (resize with cell)'.
    // The rows 11 to 14 are in a collapsed group. So the shape effectively spans 4 rows. When
    // copying the range B5:C19 and pasting it to B22, the group is expanded and the shape should
    // increase its height so that it spans 8 rows.
    createScDoc("ods/tdf155095_shape_over_collapsed_group.ods");
    ScDocument* pDoc = getScDoc();

    // Copy and paste
    goToCell(u"$B$5:$C$19"_ustr);
    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
    goToCell(u"$B$22"_ustr);
    dispatchCommand(mxComponent, u".uno:Paste"_ustr, {});

    // Make sure the shape has the correct size and spans C26:C33
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 1); // pasted shape
    // Without fix the shape had position(6708,11564) and size(407,2013).
    tools::Rectangle aExpectedRect(tools::Rectangle(Point(6708, 10743), Size(407, 3473)));
    tools::Rectangle aSnapRect(pObj->GetSnapRect());
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect, aSnapRect, 1);

    // Without fix the shape spans C28:C32
    ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
    ScAddress aExpectedStart(SCCOL(2), SCROW(25), SCTAB(0)); // zero based
    ScAddress aExpectedEnd(SCCOL(2), SCROW(32), SCTAB(0));
    CPPUNIT_ASSERT_EQUAL(aExpectedStart, (*pObjData).maStart);
    CPPUNIT_ASSERT_EQUAL(aExpectedEnd, (*pObjData).maEnd);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf155094_paste_transposed)
{
    // Load a document, which has a page anchored shape "Red" in C4, a cell anchored shape "Green" in
    // D4 and a cell anchored shape "Blue" with 'resize with cell' in E4. The range C3:E5 is copied
    // and pasted with 'Transpose all' to cell K6. The pasted content had these errors:
    // Pasted shape "Red" was missing.
    // Pasted shape "Green" was resized although 'resize with cell' was not set.
    // Pasted shape "Blue" was in cell K5 instead of L8.
    // The behavior of paste transposed is changed since LO 7.6 so that no shape is resized.
    createScDoc("ods/tdf155094_paste_transposed.ods");
    ScDocument* pDoc = getScDoc();

    // Copy and paste
    goToCell(u"$C$3:$E$5"_ustr);
    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
    goToCell(u"$K$6"_ustr);
    cpo::uno::Sequence<beans::PropertyValue> aPropertyValues
        = { comphelper::makePropertyValue(u"Flags"_ustr, u"A"_ustr),
            comphelper::makePropertyValue(u"FormulaCommand"_ustr, sal_uInt16(0)),
            comphelper::makePropertyValue(u"SkipEmptyCells"_ustr, false),
            comphelper::makePropertyValue(u"Transpose"_ustr, true),
            comphelper::makePropertyValue(u"AsLink"_ustr, false),
            comphelper::makePropertyValue(u"MoveMode"_ustr, sal_uInt16(4)) };
    dispatchCommand(mxComponent, u".uno:InsertContents"_ustr, aPropertyValues);

    // Without fix there had been only 5 object.
    ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
    CPPUNIT_ASSERT_MESSAGE("No ScDrawLayer", pDrawLayer);
    const SdrPage* pPage = pDrawLayer->GetPage(0);
    CPPUNIT_ASSERT_MESSAGE("No draw page", pPage);
    CPPUNIT_ASSERT_EQUAL(size_t(6), pPage->GetObjCount());

    // Without fix pasted object had position(7972, 8616) and size(1805×801).
    SdrObject* pObjGreen = lcl_getSdrObjectWithAssert(*pDoc, 4); // pasted shape "Green"
    tools::Rectangle aExpectedRect(tools::Rectangle(Point(12489, 12609), Size(800, 800)));
    tools::Rectangle aSnapRect(pObjGreen->GetSnapRect());
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect, aSnapRect, 1);

    // Without fix the pasted object was at wrong position(10230,8616).
    // Since LO 7.6 the pasted object has the same size as the original shape "Blue".
    SdrObject* pObjBlue = lcl_getSdrObjectWithAssert(*pDoc, 5);
    aExpectedRect = tools::Rectangle(Point(12489, 14612), Size(800, 800));
    aSnapRect = pObjBlue->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpectedRect, aSnapRect, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf155091_paste_duplicates)
{
    // Load a document, which has a shape in range C6:C16, anchored 'To cell (resize with cell)'.
    // The rows 6 to 9 are filtered. When copying the range B5:C19 and paste it to B23, the
    // shape was pasted twice.
    createScDoc("ods/tdf155091_paste_duplicates.ods");
    ScDocument* pDoc = getScDoc();

    // Copy and paste
    goToCell(u"$B$5:$C$19"_ustr);
    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
    goToCell(u"$B$23"_ustr);
    dispatchCommand(mxComponent, u".uno:Paste"_ustr, {});

    // Make sure there is no third object but only original and pasted one.
    ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
    CPPUNIT_ASSERT_MESSAGE("No ScDrawLayer", pDrawLayer);
    const SdrPage* pPage = pDrawLayer->GetPage(0);
    CPPUNIT_ASSERT_MESSAGE("No draw page", pPage);
    CPPUNIT_ASSERT_EQUAL(size_t(2), pPage->GetObjCount());
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf125938_anchor_after_copy_paste)
{
    // Load a document, which has an image in cell $sheet1.$B$3, anchored to cell. When the range
    // A3:C3 was copied and pasted to D9:D11 in sheet2, the image was displayed in cell D10, but
    // its anchor was in B3.
    createScDoc("ods/tdf125938_anchor_after_copy_paste.ods");
    ScDocument* pDoc = getScDoc();

    // Copy and paste
    goToCell(u"$Sheet1.$A$3:$C$3"_ustr);
    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
    goToCell(u"$Sheet2.$D$9"_ustr);
    dispatchCommand(mxComponent, u".uno:Paste"_ustr, {});

    // Get pasted shape
    ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
    const SdrPage* pPage = pDrawLayer->GetPage(1);
    SdrObject* pObj = pPage->GetObj(0);

    // Make sure object is anchored to E9
    ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
    ScAddress aExpectedAddress(SCCOL(4), SCROW(8), SCTAB(1)); // zero based
    CPPUNIT_ASSERT_EQUAL(aExpectedAddress, (*pObjData).maStart);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf154821_shape_in_group)
{
    // The document contains a shape in A7, a group spanning rows 2 to 4 and a second group spanning
    // rows 6 to 10. Error was, that when the document was saved with collapsed groups, the shape
    // lost its position.
    createScDoc("ods/tdf154821_shape_in_group.ods");

    // Get snap rectangle before collapse and save
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    tools::Rectangle aRectOrig = pObj->GetSnapRect();

    // Collapse the lower group
    ScTabViewShell* pViewShell = getViewShell();
    pViewShell->GetViewData().SetCurX(0);
    pViewShell->GetViewData().SetCurY(5);
    pViewShell->GetViewData().GetDispatcher().Execute(SID_OUTLINE_HIDE);
    // Collapse the upper group
    pViewShell->GetViewData().SetCurX(0);
    pViewShell->GetViewData().SetCurY(1);
    pViewShell->GetViewData().GetDispatcher().Execute(SID_OUTLINE_HIDE);

    saveAndReload(TestFilter::ODS);

    // Expand the lower group
    pViewShell = getViewShell();
    pViewShell->GetViewData().SetCurX(0);
    pViewShell->GetViewData().SetCurY(5);
    pViewShell->GetViewData().GetDispatcher().Execute(SID_OUTLINE_SHOW);
    // Expand the upper group
    pViewShell = getViewShell();
    pViewShell->GetViewData().SetCurX(0);
    pViewShell->GetViewData().SetCurY(1);
    pViewShell->GetViewData().GetDispatcher().Execute(SID_OUTLINE_SHOW);

    // Verify shape position is not changed besides rounding errors from twips<->mm
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    tools::Rectangle aRectReload = pObj->GetSnapRect();
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aRectOrig, aRectReload, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf160003_copy_page_anchored)
{
    // Load a document, which has a chart anchored to page on sheet2. Copy&paste to other document
    // had lost the chart object.
    createScDoc("ods/tdf160003_page_anchored_object.ods");

    // copy range with chart
    goToCell(u"$Sheet2.$A$1:$L$24"_ustr);
    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});

    // close document and create new one
    createScDoc();

    // paste clipboard
    goToCell(u"$Sheet1.$A$1"_ustr);
    dispatchCommand(mxComponent, u".uno:Paste"_ustr, {});

    // Make sure the chart object exists.
    ScDocument* pDoc = getScDoc();
    ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
    const SdrPage* pPage = pDrawLayer->GetPage(0);
    CPPUNIT_ASSERT_EQUAL(size_t(1), pPage->GetObjCount());
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf160369_groupshape)
{
    // The document contains a group spanning range C5:F12. It is currently anchored to page to
    // make sure its position does not change. When the group was anchored 'To Cell' and rows or
    // columns were hidden before the group, saving changed the anchor position and anchor
    // offset. This happened both with using 'resize with cell' and not.
    createScDoc("ods/tdf160369_groupshape.ods");

    // Get document and group object
    ScDocument* pDoc = getScDoc();
    SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);

    // Anchor group 'To Cell (resize with cell)' to prepare the test.
    ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/);

    // Hide rows 3 and 4 (UI number), which are before the group
    // Hide column D, which is inside the group
    pDoc->SetRowHidden(2, 3, 0, true);
    pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not changed
    pDoc->SetColHidden(3, 3, 0, true);
    pDoc->SetDrawPageSize(0);

    // Get geometry of the group
    ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
    ScAddress aOrigStart = (*pObjData).maStart;
    ScAddress aOrigEnd = (*pObjData).maEnd;
    tools::Rectangle aOrigRect = pObj->GetSnapRect();

    // Save document but do not reload. Saving alone had already caused the error.
    save(TestFilter::ODS);

    // Get geometry of the group again
    ScDrawObjData* pAfterObjData = ScDrawLayer::GetObjData(pObj);
    ScAddress aAfterStart = (*pAfterObjData).maStart;
    ScAddress aAfterEnd = (*pAfterObjData).maEnd;
    tools::Rectangle aAfterRect = pObj->GetSnapRect();

    // verify Orig equals After
    CPPUNIT_ASSERT_EQUAL(aOrigStart, aAfterStart);
    CPPUNIT_ASSERT_EQUAL(aOrigEnd, aAfterEnd);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aOrigRect, aAfterRect, 1);

    // The same but with saveAndReload.
    createScDoc("ods/tdf160369_groupshape.ods");
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/);
    pDoc->SetRowHidden(2, 3, 0, true);
    pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not changed
    pDoc->SetColHidden(3, 3, 0, true);
    pDoc->SetDrawPageSize(0);

    saveAndReload(TestFilter::ODS);

    // Verify geometry is same as before save
    pDoc = getScDoc();
    pObj = lcl_getSdrObjectWithAssert(*pDoc, 0);
    pAfterObjData = ScDrawLayer::GetObjData(pObj);
    aAfterStart = (*pAfterObjData).maStart;
    aAfterEnd = (*pAfterObjData).maEnd;
    aAfterRect = pObj->GetSnapRect();

    // verify Orig equals After
    CPPUNIT_ASSERT_EQUAL(aOrigStart, aAfterStart);
    CPPUNIT_ASSERT_EQUAL(aOrigEnd, aAfterEnd);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aOrigRect, aAfterRect, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf160329_sortWithHiddenRows)
{
    // Load a document, which has images anchored to cell and rows hidden
    createScDoc("ods/tdf160329_sortWithHiddenRows.ods");
    ScDocument* pDoc = getScDoc();

    // Sort the rows
    cpo::uno::Sequence<beans::PropertyValue> aArgs1
        = { comphelper::makePropertyValue(u"DbName"_ustr, u"myRange"_ustr) };
    dispatchCommand(mxComponent, u".uno:SelectDB"_ustr, aArgs1);
    cpo::uno::Sequence<beans::PropertyValue> aArgs2
        = { comphelper::makePropertyValue(u"ByRows"_ustr, true),
            comphelper::makePropertyValue(u"HasHeader"_ustr, true),
            comphelper::makePropertyValue(u"Col1"_ustr, sal_Int32(1)),
            comphelper::makePropertyValue(u"Ascending1"_ustr, false),
            comphelper::makePropertyValue(u"IncludeImages"_ustr, true) };
    dispatchCommand(mxComponent, u".uno:DataSort"_ustr, aArgs2);

    // Make sure objects are on correct position
    SdrObject* pObj = lcl_getSdrObjectbyName(*pDoc, std::u16string_view(u"ImageD"));
    Point aPos = pObj->GetSnapRect().TopLeft();
    // The position was (3000|2899) without fix.
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(Point(3000, 5898), aPos, 1);
    pObj = lcl_getSdrObjectbyName(*pDoc, std::u16string_view(u"ImageE"));
    aPos = pObj->GetSnapRect().TopLeft();
    // The position was (2600|2499) without fix.
    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(Point(2600, 4399), aPos, 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf167450_copySheet)
{
    // Copy a sheet that contains an image from one document to another. Make sure the image has the
    // correct position after save and reload of the target document.

    // Open source and target document
    mxComponent = loadFromDesktop(createFileURL(u"ods/tdf167450_target.ods"));
    ScDocument* pDocTarget = getScDoc();
    mxComponent2 = loadFromDesktop(createFileURL(u"ods/tdf167450_source.ods"));
    ScDocument* pDocSource = getScDoc2();

    // Copy sheet to target document. 32767 means 'to end position'
    cpo::uno::Sequence<beans::PropertyValue> aArgs
        = { comphelper::makePropertyValue(u"DocName"_ustr, u"tdf167450_target"_ustr),
            comphelper::makePropertyValue(u"Index"_ustr, sal_Int32(32767)),
            comphelper::makePropertyValue(u"Copy"_ustr, true) };
    dispatchCommand(mxComponent2, u".uno:Move"_ustr, aArgs);

    // Get object in source document. It is on first sheet.
    SdrObject* pObjSource = lcl_getSdrObjectWithAssert(*pDocSource, 0, 0);
    tools::Rectangle aRectSource = pObjSource->GetLogicRect();

    // verify sheet inclusive image is copied
    SdrObject* pObjTarget = lcl_getSdrObjectWithAssert(*pDocTarget, 0, 1);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aRectSource, pObjTarget->GetLogicRect(), 1);

    // Compare positions after save and reload. Without fix the position was wrong after reload.
    saveAndReload(TestFilter::ODS);
    pDocTarget = getScDoc();
    pObjTarget = lcl_getSdrObjectWithAssert(*pDocTarget, 0, 1);
    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aRectSource, pObjTarget->GetLogicRect(), 1);
}

CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf140866)
{
    // Load a document, which has a comment in cell $sheet2.$A$1, and a custom shape in cell
    // $sheet2.$B$11. When the shape from $sheet2.$B$11 was copied and pasted to $sheet2.$D$9,
    // the anchor position of comment is changed and after saved to ods the comment was gone.
    createScDoc("ods/tdf140866.ods");
    ScDocument* pDoc = getScDoc();

    // Check that we have the comment on A1
    ScPostIt* pNote = pDoc->GetNote(ScAddress(0, 0, 0));
    CPPUNIT_ASSERT(pNote);
    CPPUNIT_ASSERT_EQUAL(u"Test 1"_ustr, pNote->GetText());

    goToCell(u"$Sheet2.$B$11"_ustr);
    selectObjectByName(u"Shape 1");

    // Copy and paste
    dispatchCommand(mxComponent, u".uno:Copy"_ustr, {});
    goToCell(u"$Sheet2.$D$9"_ustr);
    dispatchCommand(mxComponent, u".uno:Paste"_ustr, {});

    // Check that we still have the comment on A1
    pNote = pDoc->GetNote(ScAddress(0, 0, 0));
    CPPUNIT_ASSERT(pNote);
    CPPUNIT_ASSERT_EQUAL(u"Test 1"_ustr, pNote->GetText());

    // Save, reload
    saveAndReload(TestFilter::ODS);
    pDoc = getScDoc();
    // Check that we still have the comment on A1 after save&reload
    pNote = pDoc->GetNote(ScAddress(0, 0, 0));
    // Without the fix in place the comment was gone and test would have failed with:
    // assertion failed
    // - Expression : pNote
    CPPUNIT_ASSERT(pNote);
    CPPUNIT_ASSERT_EQUAL(u"Test 1"_ustr, pNote->GetText());
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
