1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/config.h>
11 #include <unotest/filters-test.hxx>
12 #include <test/bootstrapfixture.hxx>
14 #include "helper/qahelper.hxx"
17 #include <inputopt.hxx>
19 #include <document.hxx>
20 #include <drwlayer.hxx>
21 #include <userdat.hxx>
22 #include <formulacell.hxx>
23 #include <tabprotection.hxx>
24 #include <testlotus.hxx>
25 #include <dbdocfun.hxx>
26 #include <globalnames.hxx>
28 #include <sortparam.hxx>
29 #include <scopetools.hxx>
32 #include <svx/svdocapt.hxx>
33 #include <svx/svdpage.hxx>
35 using namespace ::com::sun::star
;
36 using namespace ::com::sun::star::uno
;
38 /* Implementation of Filters test */
41 : public test::FiltersTest
42 , public ScBootstrapFixture
47 virtual void setUp() override
;
48 virtual void tearDown() override
;
50 virtual bool load( const OUString
&rFilter
, const OUString
&rURL
,
51 const OUString
&rUserData
, SfxFilterFlags nFilterFlags
,
52 SotClipboardFormatId nClipboardID
, unsigned int nFilterVersion
) override
;
54 * Ensure CVEs remain unbroken
58 //ods, xls, xlsx filter tests
59 void testRangeNameODS(); // only test ods here, xls and xlsx in subsequent_filters-test
60 void testContentODS();
61 void testContentXLS();
62 void testContentXLSX();
63 void testContentXLSXStrict(); // strict OOXML
64 void testContentLotus123();
65 void testContentofz9704();
66 void testContentDIF();
67 void testContentXLSB();
68 void testContentXLS_XML();
69 void testContentGnumeric();
70 void testSharedFormulaXLS();
71 void testSharedFormulaXLSX();
72 void testSharedFormulaRefUpdateXLSX();
73 void testSheetNamesXLSX();
75 void testCommentSize();
76 void testLegacyCellAnchoredRotatedShape();
77 void testEnhancedProtectionXLS();
78 void testEnhancedProtectionXLSX();
79 void testSortWithSharedFormulasODS();
80 void testSortWithSheetExternalReferencesODS();
81 void testSortWithSheetExternalReferencesODS_Impl( ScDocShellRef
const & xDocShRef
, SCROW nRow1
, SCROW nRow2
,
82 bool bCheckRelativeInSheet
);
83 void testSortWithFormattingXLS();
85 CPPUNIT_TEST_SUITE(ScFiltersTest
);
86 CPPUNIT_TEST(testCVEs
);
87 CPPUNIT_TEST(testRangeNameODS
);
88 CPPUNIT_TEST(testContentODS
);
89 CPPUNIT_TEST(testContentXLS
);
90 CPPUNIT_TEST(testContentXLSX
);
91 CPPUNIT_TEST(testContentXLSXStrict
);
92 CPPUNIT_TEST(testContentLotus123
);
93 CPPUNIT_TEST(testContentofz9704
);
94 CPPUNIT_TEST(testContentDIF
);
95 CPPUNIT_TEST(testContentXLSB
);
96 CPPUNIT_TEST(testContentXLS_XML
);
97 CPPUNIT_TEST(testContentGnumeric
);
98 CPPUNIT_TEST(testSharedFormulaXLS
);
99 CPPUNIT_TEST(testSharedFormulaXLSX
);
100 CPPUNIT_TEST(testSharedFormulaRefUpdateXLSX
);
101 CPPUNIT_TEST(testSheetNamesXLSX
);
102 CPPUNIT_TEST(testTdf79998
);
103 CPPUNIT_TEST(testCommentSize
);
104 CPPUNIT_TEST(testLegacyCellAnchoredRotatedShape
);
105 CPPUNIT_TEST(testEnhancedProtectionXLS
);
106 CPPUNIT_TEST(testEnhancedProtectionXLSX
);
107 CPPUNIT_TEST(testSortWithSharedFormulasODS
);
108 CPPUNIT_TEST(testSortWithSheetExternalReferencesODS
);
109 CPPUNIT_TEST(testSortWithFormattingXLS
);
111 CPPUNIT_TEST_SUITE_END();
114 uno::Reference
<uno::XInterface
> m_xCalcComponent
;
115 bool mbUpdateReferenceOnSort
; ///< Remember the configuration option so that we can set it back.
118 bool ScFiltersTest::load(const OUString
&rFilter
, const OUString
&rURL
,
119 const OUString
&rUserData
, SfxFilterFlags nFilterFlags
,
120 SotClipboardFormatId nClipboardID
, unsigned int nFilterVersion
)
122 ScDocShellRef xDocShRef
= ScBootstrapFixture::load(rURL
, rFilter
, rUserData
,
123 OUString(), nFilterFlags
, nClipboardID
, nFilterVersion
);
124 bool bLoaded
= xDocShRef
.is();
125 //reference counting of ScDocShellRef is very confused.
127 xDocShRef
->DoClose();
131 void ScFiltersTest::testCVEs()
133 #ifndef DISABLE_CVE_TESTS
134 testDir("Quattro Pro 6.0",
135 m_directories
.getURLFromSrc("/sc/qa/unit/data/qpro/"));
137 //warning, the current "sylk filter" in sc (docsh.cxx) automatically
138 //chains on failure on trying as csv, rtf, etc. so "success" may
139 //not indicate that it imported as .slk.
141 m_directories
.getURLFromSrc("/sc/qa/unit/data/slk/"));
143 testDir("MS Excel 97",
144 m_directories
.getURLFromSrc("/sc/qa/unit/data/xls/"));
146 testDir("Calc Office Open XML",
147 m_directories
.getURLFromSrc("/sc/qa/unit/data/xlsx/"), OUString(), XLSX_FORMAT_TYPE
);
149 testDir("Calc Office Open XML",
150 m_directories
.getURLFromSrc("/sc/qa/unit/data/xlsm/"), OUString(), XLSX_FORMAT_TYPE
);
153 m_directories
.getURLFromSrc("/sc/qa/unit/data/dbf/"));
156 m_directories
.getURLFromSrc("/sc/qa/unit/data/wks/"));
163 void testRangeNameImpl(const ScDocument
& rDoc
)
165 //check one range data per sheet and one global more detailed
166 //add some more checks here
167 ScRangeData
* pRangeData
= rDoc
.GetRangeName()->findByUpperName(OUString("GLOBAL1"));
168 CPPUNIT_ASSERT_MESSAGE("range name Global1 not found", pRangeData
);
170 rDoc
.GetValue(1,0,0,aValue
);
171 ASSERT_DOUBLES_EQUAL_MESSAGE("range name Global1 should reference Sheet1.A1", 1.0, aValue
);
172 pRangeData
= rDoc
.GetRangeName(0)->findByUpperName(OUString("LOCAL1"));
173 CPPUNIT_ASSERT_MESSAGE("range name Sheet1.Local1 not found", pRangeData
);
174 rDoc
.GetValue(1,2,0,aValue
);
175 ASSERT_DOUBLES_EQUAL_MESSAGE("range name Sheet1.Local1 should reference Sheet1.A3", 3.0, aValue
);
176 pRangeData
= rDoc
.GetRangeName(1)->findByUpperName(OUString("LOCAL2"));
177 CPPUNIT_ASSERT_MESSAGE("range name Sheet2.Local2 not found", pRangeData
);
178 //check for correct results for the remaining formulas
179 rDoc
.GetValue(1,1,0, aValue
);
180 ASSERT_DOUBLES_EQUAL_MESSAGE("=global2 should be 2", 2.0, aValue
);
181 rDoc
.GetValue(1,3,0, aValue
);
182 ASSERT_DOUBLES_EQUAL_MESSAGE("=local2 should be 4", 4.0, aValue
);
183 rDoc
.GetValue(2,0,0, aValue
);
184 ASSERT_DOUBLES_EQUAL_MESSAGE("=SUM(global3) should be 10", 10.0, aValue
);
189 void ScFiltersTest::testRangeNameODS()
191 ScDocShellRef xDocSh
= loadDoc("named-ranges-global.", FORMAT_ODS
);
193 CPPUNIT_ASSERT_MESSAGE("Failed to load named-ranges-global.*", xDocSh
.is());
195 xDocSh
->DoHardRecalc();
197 ScDocument
& rDoc
= xDocSh
->GetDocument();
198 testRangeNameImpl(rDoc
);
201 createCSVPath( "rangeExp_Sheet2.", aCSVPath
);
202 testFile( aCSVPath
, rDoc
, 1);
208 void testContentImpl(ScDocument
& rDoc
, sal_Int32 nFormat
) //same code for ods, xls, xlsx
212 rDoc
.GetValue(0,0,0,fValue
);
213 ASSERT_DOUBLES_EQUAL_MESSAGE("value not imported correctly", 1.0, fValue
);
214 rDoc
.GetValue(0,1,0,fValue
);
215 ASSERT_DOUBLES_EQUAL_MESSAGE("value not imported correctly", 2.0, fValue
);
216 OUString aString
= rDoc
.GetString(1, 0, 0);
218 //check string import
219 CPPUNIT_ASSERT_EQUAL_MESSAGE("string imported not correctly", OUString("String1"), aString
);
220 aString
= rDoc
.GetString(1, 1, 0);
221 CPPUNIT_ASSERT_EQUAL_MESSAGE("string not imported correctly", OUString("String2"), aString
);
223 //check basic formula import
224 // in case of DIF it just contains values
225 rDoc
.GetValue(2,0,0,fValue
);
226 ASSERT_DOUBLES_EQUAL_MESSAGE("=2*3", 6.0, fValue
);
227 rDoc
.GetValue(2,1,0,fValue
);
228 ASSERT_DOUBLES_EQUAL_MESSAGE("=2+3", 5.0, fValue
);
229 rDoc
.GetValue(2,2,0,fValue
);
230 ASSERT_DOUBLES_EQUAL_MESSAGE("=2-3", -1.0, fValue
);
231 rDoc
.GetValue(2,3,0,fValue
);
232 ASSERT_DOUBLES_EQUAL_MESSAGE("=C1+C2", 11.0, fValue
);
234 //check merged cells import
235 if (nFormat
!= FORMAT_LOTUS123
&& nFormat
!= FORMAT_DIF
&& nFormat
!= FORMAT_XLS_XML
236 && nFormat
!= FORMAT_GNUMERIC
)
240 rDoc
.ExtendMerge(4, 1, nCol
, nRow
, 0);
241 CPPUNIT_ASSERT_MESSAGE("merged cells are not imported", nCol
== 5 && nRow
== 2);
244 ScAddress
aAddress(7, 2, 0);
245 ScPostIt
* pNote
= rDoc
.GetNote(aAddress
);
246 CPPUNIT_ASSERT_MESSAGE("note not imported", pNote
);
247 CPPUNIT_ASSERT_EQUAL_MESSAGE("note text not imported correctly", OUString("Test"), pNote
->GetText() );
250 //add additional checks here
255 void ScFiltersTest::testContentODS()
257 ScDocShellRef xDocSh
= loadDoc("universal-content.", FORMAT_ODS
);
258 xDocSh
->DoHardRecalc();
260 ScDocument
& rDoc
= xDocSh
->GetDocument();
261 testContentImpl(rDoc
, FORMAT_ODS
);
265 void ScFiltersTest::testContentXLS()
267 ScDocShellRef xDocSh
= loadDoc("universal-content.", FORMAT_XLS
);
268 xDocSh
->DoHardRecalc();
270 ScDocument
& rDoc
= xDocSh
->GetDocument();
271 testContentImpl(rDoc
, FORMAT_XLS
);
275 void ScFiltersTest::testContentXLSX()
277 ScDocShellRef xDocSh
= loadDoc("universal-content.", FORMAT_XLSX
);
278 xDocSh
->DoHardRecalc();
280 ScDocument
& rDoc
= xDocSh
->GetDocument();
281 testContentImpl(rDoc
, FORMAT_XLSX
);
285 void ScFiltersTest::testContentXLSXStrict()
287 ScDocShellRef xDocSh
= loadDoc("universal-content-strict.", FORMAT_XLSX
);
288 xDocSh
->DoHardRecalc();
290 ScDocument
& rDoc
= xDocSh
->GetDocument();
291 testContentImpl(rDoc
, FORMAT_XLSX
);
295 void ScFiltersTest::testContentLotus123()
297 ScDocShellRef xDocSh
= loadDoc("universal-content.", FORMAT_LOTUS123
);
298 xDocSh
->DoHardRecalc();
300 ScDocument
& rDoc
= xDocSh
->GetDocument();
301 testContentImpl(rDoc
, FORMAT_LOTUS123
);
305 void ScFiltersTest::testContentofz9704()
308 createFileURL("ofz9704.", "123", aFileName
);
309 SvFileStream
aFileStream(aFileName
, StreamMode::READ
);
310 TestImportWKS(aFileStream
);
313 void ScFiltersTest::testContentDIF()
315 ScDocShellRef xDocSh
= loadDoc("universal-content.", FORMAT_DIF
);
317 CPPUNIT_ASSERT_MESSAGE("Failed to load universal-content.dif", xDocSh
.is());
322 void ScFiltersTest::testContentXLSB()
324 ScDocShellRef xDocSh
= loadDoc("universal-content.", FORMAT_XLSB
);
325 xDocSh
->DoHardRecalc();
327 ScDocument
& rDoc
= xDocSh
->GetDocument();
328 testContentImpl(rDoc
, FORMAT_XLSB
);
332 void ScFiltersTest::testContentXLS_XML()
334 ScDocShellRef xDocSh
= loadDoc("universal-content.", FORMAT_XLS_XML
);
335 CPPUNIT_ASSERT(xDocSh
.is());
337 ScDocument
& rDoc
= xDocSh
->GetDocument();
338 testContentImpl(rDoc
, FORMAT_XLS_XML
);
342 void ScFiltersTest::testContentGnumeric()
344 ScDocShellRef xDocSh
= loadDoc("universal-content.", FORMAT_GNUMERIC
);
345 CPPUNIT_ASSERT(xDocSh
.is());
347 ScDocument
& rDoc
= xDocSh
->GetDocument();
348 testContentImpl(rDoc
, FORMAT_GNUMERIC
);
352 void ScFiltersTest::testSharedFormulaXLS()
354 ScDocShellRef xDocSh
= loadDoc("shared-formula/basic.", FORMAT_XLS
);
355 CPPUNIT_ASSERT(xDocSh
.is());
356 ScDocument
& rDoc
= xDocSh
->GetDocument();
357 xDocSh
->DoHardRecalc();
358 // Check the results of formula cells in the shared formula range.
359 for (SCROW i
= 1; i
<= 18; ++i
)
361 double fVal
= rDoc
.GetValue(ScAddress(1,i
,0));
362 double fCheck
= i
*10.0;
363 CPPUNIT_ASSERT_EQUAL(fCheck
, fVal
);
366 ScFormulaCell
* pCell
= rDoc
.GetFormulaCell(ScAddress(1,18,0));
367 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pCell
);
368 ScFormulaCellGroupRef xGroup
= pCell
->GetCellGroup();
369 CPPUNIT_ASSERT_MESSAGE("This cell should be a part of a cell group.", xGroup
);
370 CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup
->mpTopCell
->aPos
.Row() == 1 && xGroup
->mnLength
== 18);
374 // The following file contains shared formula whose range is inaccurate.
375 // Excel can easily mess up shared formula ranges, so we need to be able
376 // to handle these wrong ranges that Excel stores.
378 xDocSh
= loadDoc("shared-formula/gap.", FORMAT_XLS
);
379 CPPUNIT_ASSERT(xDocSh
.is());
380 ScDocument
& rDoc2
= xDocSh
->GetDocument();
383 ASSERT_FORMULA_EQUAL(rDoc2
, ScAddress(1,0,0), "A1*20", "Wrong formula.");
384 ASSERT_FORMULA_EQUAL(rDoc2
, ScAddress(1,1,0), "A2*20", "Wrong formula.");
385 ASSERT_FORMULA_EQUAL(rDoc2
, ScAddress(1,2,0), "A3*20", "Wrong formula.");
387 // There is an intentional gap at row 4.
389 ASSERT_FORMULA_EQUAL(rDoc2
, ScAddress(1,4,0), "A5*20", "Wrong formula.");
390 ASSERT_FORMULA_EQUAL(rDoc2
, ScAddress(1,5,0), "A6*20", "Wrong formula.");
391 ASSERT_FORMULA_EQUAL(rDoc2
, ScAddress(1,6,0), "A7*20", "Wrong formula.");
392 ASSERT_FORMULA_EQUAL(rDoc2
, ScAddress(1,7,0), "A8*20", "Wrong formula.");
394 // We re-group formula cells on load. Let's check that as well.
396 ScFormulaCell
* pFC
= rDoc2
.GetFormulaCell(ScAddress(1,0,0));
397 CPPUNIT_ASSERT_MESSAGE("Failed to fetch formula cell.", pFC
);
398 CPPUNIT_ASSERT_MESSAGE("This should be the top cell in formula group.", pFC
->IsSharedTop());
399 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(3), pFC
->GetSharedLength());
401 pFC
= rDoc2
.GetFormulaCell(ScAddress(1,4,0));
402 CPPUNIT_ASSERT_MESSAGE("Failed to fetch formula cell.", pFC
);
403 CPPUNIT_ASSERT_MESSAGE("This should be the top cell in formula group.", pFC
->IsSharedTop());
404 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(4), pFC
->GetSharedLength());
409 void ScFiltersTest::testSharedFormulaXLSX()
411 ScDocShellRef xDocSh
= loadDoc("shared-formula/basic.", FORMAT_XLSX
);
412 ScDocument
& rDoc
= xDocSh
->GetDocument();
413 xDocSh
->DoHardRecalc();
414 // Check the results of formula cells in the shared formula range.
415 for (SCROW i
= 1; i
<= 18; ++i
)
417 double fVal
= rDoc
.GetValue(ScAddress(1,i
,0));
418 double fCheck
= i
*10.0;
419 CPPUNIT_ASSERT_EQUAL(fCheck
, fVal
);
422 ScFormulaCell
* pCell
= rDoc
.GetFormulaCell(ScAddress(1,18,0));
423 CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pCell
);
424 ScFormulaCellGroupRef xGroup
= pCell
->GetCellGroup();
425 CPPUNIT_ASSERT_MESSAGE("This cell should be a part of a cell group.", xGroup
);
426 CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup
->mpTopCell
->aPos
.Row() == 1 && xGroup
->mnLength
== 18);
431 void ScFiltersTest::testSharedFormulaRefUpdateXLSX()
433 ScDocShellRef xDocSh
= loadDoc("shared-formula/refupdate.", FORMAT_XLSX
);
434 ScDocument
& rDoc
= xDocSh
->GetDocument();
435 sc::AutoCalcSwitch
aACSwitch(rDoc
, true); // turn auto calc on.
436 rDoc
.DeleteRow(ScRange(0, 4, 0, rDoc
.MaxCol(), 4, 0)); // delete row 5.
440 const char* pExpectedFormula
;
441 const char* pErrorMsg
;
444 TestCase aCases
[4] = {
445 { ScAddress(1, 0, 0), "B29+1", "Wrong formula in B1" },
446 { ScAddress(2, 0, 0), "C29+1", "Wrong formula in C1" },
447 { ScAddress(3, 0, 0), "D29+1", "Wrong formula in D1" },
448 { ScAddress(4, 0, 0), "E29+1", "Wrong formula in E1" },
451 for (size_t nIdx
= 0; nIdx
< 4; ++nIdx
)
453 TestCase
& rCase
= aCases
[nIdx
];
454 ASSERT_FORMULA_EQUAL(rDoc
, rCase
.aPos
, rCase
.pExpectedFormula
, rCase
.pErrorMsg
);
460 void ScFiltersTest::testSheetNamesXLSX()
462 ScDocShellRef xDocSh
= loadDoc("sheet-names.", FORMAT_XLSX
);
463 ScDocument
& rDoc
= xDocSh
->GetDocument();
465 std::vector
<OUString
> aTabNames
= rDoc
.GetAllTableNames();
466 CPPUNIT_ASSERT_EQUAL_MESSAGE("The document should have 5 sheets in total.", size_t(5), aTabNames
.size());
467 CPPUNIT_ASSERT_EQUAL(OUString("S&P"), aTabNames
[0]);
468 CPPUNIT_ASSERT_EQUAL(OUString("Sam's Club"), aTabNames
[1]);
469 CPPUNIT_ASSERT_EQUAL(OUString("\"The Sheet\""), aTabNames
[2]);
470 CPPUNIT_ASSERT_EQUAL(OUString("A<B"), aTabNames
[3]);
471 CPPUNIT_ASSERT_EQUAL(OUString("C>D"), aTabNames
[4]);
476 // FILESAVE: XLSX export with long sheet names (length > 31 characters)
477 void ScFiltersTest::testTdf79998()
479 // check: original document has tab name > 31 characters
480 ScDocShellRef xDocSh
= loadDoc("tdf79998.", FORMAT_ODS
);
481 ScDocument
& rDoc1
= xDocSh
->GetDocument();
482 const std::vector
<OUString
> aTabNames1
= rDoc1
.GetAllTableNames();
483 CPPUNIT_ASSERT_EQUAL(OUString("Utilities (FX Kurse, Kreditkarten etc)"), aTabNames1
[1]);
485 // check: saved XLSX document has truncated tab name
486 xDocSh
= saveAndReload( &(*xDocSh
), FORMAT_XLSX
);
487 ScDocument
& rDoc2
= xDocSh
->GetDocument();
488 const std::vector
<OUString
> aTabNames2
= rDoc2
.GetAllTableNames();
489 CPPUNIT_ASSERT_EQUAL(OUString("Utilities (FX Kurse, Kreditkart"), aTabNames2
[1]);
494 void ScFiltersTest::testCommentSize()
496 ScDocShellRef xDocSh
= loadDoc("comment.", FORMAT_ODS
);
497 ScDocument
& rDoc
= xDocSh
->GetDocument();
499 ScAddress
aPos(0,0,0);
500 ScPostIt
*pNote
= rDoc
.GetNote(aPos
);
501 CPPUNIT_ASSERT(pNote
);
503 pNote
->ShowCaption(aPos
, true);
504 CPPUNIT_ASSERT(pNote
->IsCaptionShown());
506 SdrCaptionObj
* pCaption
= pNote
->GetCaption();
507 CPPUNIT_ASSERT(pCaption
);
509 const tools::Rectangle
& rOldRect
= pCaption
->GetLogicRect();
510 CPPUNIT_ASSERT_EQUAL(tools::Long(2899), rOldRect
.getWidth());
511 CPPUNIT_ASSERT_EQUAL(tools::Long(939), rOldRect
.getHeight());
513 pNote
->SetText(aPos
, "first\nsecond\nthird");
515 const tools::Rectangle
& rNewRect
= pCaption
->GetLogicRect();
516 CPPUNIT_ASSERT_EQUAL(rOldRect
.getWidth(), rNewRect
.getWidth());
517 CPPUNIT_ASSERT_EQUAL(tools::Long(1605), rNewRect
.getHeight());
519 rDoc
.GetUndoManager()->Undo();
521 CPPUNIT_ASSERT_EQUAL(rOldRect
.getWidth(), pCaption
->GetLogicRect().getWidth());
522 CPPUNIT_ASSERT_EQUAL(rOldRect
.getHeight(), pCaption
->GetLogicRect().getHeight());
527 static void impl_testLegacyCellAnchoredRotatedShape( ScDocument
& rDoc
, const tools::Rectangle
& aRect
, const ScDrawObjData
& aAnchor
, tools::Long TOLERANCE
= 30 /* 30 hmm */ )
529 ScDrawLayer
* pDrawLayer
= rDoc
.GetDrawLayer();
530 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer
);
531 SdrPage
* pPage
= pDrawLayer
->GetPage(0);
532 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage
);
533 CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(1), pPage
->GetObjCount() );
535 SdrObject
* pObj
= pPage
->GetObj(0);
536 const tools::Rectangle
& aSnap
= pObj
->GetSnapRect();
537 printf("expected height %" SAL_PRIdINT64
" actual %" SAL_PRIdINT64
"\n", sal_Int64(aRect
.GetHeight()), sal_Int64(aSnap
.GetHeight()) );
538 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect
.GetHeight(), aSnap
.GetHeight(), TOLERANCE
) );
539 printf("expected width %" SAL_PRIdINT64
" actual %" SAL_PRIdINT64
"\n", sal_Int64(aRect
.GetWidth()), sal_Int64(aSnap
.GetWidth()) );
540 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect
.GetWidth(), aSnap
.GetWidth(), TOLERANCE
) );
541 printf("expected left %" SAL_PRIdINT64
" actual %" SAL_PRIdINT64
"\n", sal_Int64(aRect
.Left()), sal_Int64(aSnap
.Left()) );
542 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect
.Left(), aSnap
.Left(), TOLERANCE
) );
543 printf("expected right %" SAL_PRIdINT64
" actual %" SAL_PRIdINT64
"\n", sal_Int64(aRect
.Top()), sal_Int64(aSnap
.Top()) );
544 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect
.Top(), aSnap
.Top(), TOLERANCE
) );
546 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pObj
);
547 CPPUNIT_ASSERT_MESSAGE("expected object meta data", pData
);
548 printf("expected startrow %" SAL_PRIdINT32
" actual %" SAL_PRIdINT32
"\n", aAnchor
.maStart
.Row(), pData
->maStart
.Row() );
549 CPPUNIT_ASSERT_EQUAL( aAnchor
.maStart
.Row(), pData
->maStart
.Row() );
550 printf("expected startcol %d actual %d\n", aAnchor
.maStart
.Col(), pData
->maStart
.Col() );
551 CPPUNIT_ASSERT_EQUAL( aAnchor
.maStart
.Col(), pData
->maStart
.Col() );
552 printf("expected endrow %" SAL_PRIdINT32
" actual %" SAL_PRIdINT32
"\n", aAnchor
.maEnd
.Row(), pData
->maEnd
.Row() );
553 CPPUNIT_ASSERT_EQUAL( aAnchor
.maEnd
.Row(), pData
->maEnd
.Row() );
554 printf("expected endcol %d actual %d\n", aAnchor
.maEnd
.Col(), pData
->maEnd
.Col() );
555 CPPUNIT_ASSERT_EQUAL( aAnchor
.maEnd
.Col(), pData
->maEnd
.Col() );
558 void ScFiltersTest::testLegacyCellAnchoredRotatedShape()
561 // This example doc contains cell anchored shape that is rotated, the
562 // rotated shape is in fact clipped by the sheet boundaries (and thus
563 // is a good edge case test to see if we import it still correctly)
564 ScDocShellRef xDocSh
= loadDoc("legacycellanchoredrotatedclippedshape.", FORMAT_ODS
);
566 ScDocument
& rDoc
= xDocSh
->GetDocument();
567 // ensure the imported legacy rotated shape is in the expected position
568 tools::Rectangle
aRect( 6000, -2000, 8000, 4000 );
569 // ensure the imported ( and converted ) anchor ( note we internally now store the anchor in
570 // terms of the rotated shape ) is more or less contains the correct info
571 ScDrawObjData aAnchor
;
572 aAnchor
.maStart
.SetRow( 0 );
573 aAnchor
.maStart
.SetCol( 5 );
574 aAnchor
.maEnd
.SetRow( 3 );
575 aAnchor
.maEnd
.SetCol( 7 );
576 impl_testLegacyCellAnchoredRotatedShape( rDoc
, aRect
, aAnchor
);
577 // test save and reload
578 // for some reason having this test in subsequent_export-test.cxx causes
579 // a core dump in editeng ( so moved to here )
580 xDocSh
= saveAndReload( &(*xDocSh
), FORMAT_ODS
);
581 ScDocument
& rDoc2
= xDocSh
->GetDocument();
582 impl_testLegacyCellAnchoredRotatedShape( rDoc2
, aRect
, aAnchor
);
587 // This example doc contains cell anchored shape that is rotated, the
588 // rotated shape is in fact clipped by the sheet boundaries, additionally
589 // the shape is completely hidden because the rows the shape occupies
591 ScDocShellRef xDocSh
= loadDoc("legacycellanchoredrotatedhiddenshape.", FORMAT_ODS
, true);
592 ScDocument
& rDoc
= xDocSh
->GetDocument();
593 // ensure the imported legacy rotated shape is in the expected position
594 tools::Rectangle
aRect( 6000, -2000, 8000, 4000 );
596 // ensure the imported (and converted) anchor (note we internally now store the anchor in
597 // terms of the rotated shape) is more or less contains the correct info
598 ScDrawObjData aAnchor
;
599 aAnchor
.maStart
.SetRow( 0 );
600 aAnchor
.maStart
.SetCol( 5 );
601 aAnchor
.maEnd
.SetRow( 3 );
602 aAnchor
.maEnd
.SetCol( 7 );
603 rDoc
.ShowRows(0, 9, 0, true); // show relevant rows
604 rDoc
.SetDrawPageSize(0); // trigger recalcpos
605 impl_testLegacyCellAnchoredRotatedShape( rDoc
, aRect
, aAnchor
);
606 // test save and reload
607 xDocSh
= saveAndReload( &(*xDocSh
), FORMAT_ODS
);
608 ScDocument
& rDoc2
= xDocSh
->GetDocument();
609 impl_testLegacyCellAnchoredRotatedShape( rDoc2
, aRect
, aAnchor
);
614 // This example doc contains cell anchored shape that is rotated
615 ScDocShellRef xDocSh
= loadDoc("legacycellanchoredrotatedshape.", FORMAT_ODS
);
617 ScDocument
& rDoc
= xDocSh
->GetDocument();
618 // ensure the imported legacy rotated shape is in the expected position
619 tools::Rectangle
aRect( 6000, 3000, 8000, 9000 );
620 // ensure the imported (and converted) anchor (note we internally now store the anchor in
621 // terms of the rotated shape) more or less contains the correct info
623 ScDrawObjData aAnchor
;
624 aAnchor
.maStart
.SetRow( 3 );
625 aAnchor
.maStart
.SetCol( 6 );
626 aAnchor
.maEnd
.SetRow( 9 );
627 aAnchor
.maEnd
.SetCol( 7 );
629 impl_testLegacyCellAnchoredRotatedShape( rDoc
, aRect
, aAnchor
);
630 // test save and reload
631 xDocSh
= saveAndReload( &(*xDocSh
), FORMAT_ODS
);
632 ScDocument
& rDoc2
= xDocSh
->GetDocument();
633 impl_testLegacyCellAnchoredRotatedShape( rDoc2
, aRect
, aAnchor
);
639 static void testEnhancedProtectionImpl( const ScDocument
& rDoc
)
641 const ScTableProtection
* pProt
= rDoc
.GetTabProtection(0);
643 CPPUNIT_ASSERT( pProt
);
645 CPPUNIT_ASSERT( !pProt
->isBlockEditable( ScRange( 0, 0, 0, 0, 0, 0))); // locked
646 CPPUNIT_ASSERT( pProt
->isBlockEditable( ScRange( 0, 1, 0, 0, 1, 0))); // editable without password
647 CPPUNIT_ASSERT( pProt
->isBlockEditable( ScRange( 0, 2, 0, 0, 2, 0))); // editable without password
648 CPPUNIT_ASSERT( !pProt
->isBlockEditable( ScRange( 0, 3, 0, 0, 3, 0))); // editable with password "foo"
649 CPPUNIT_ASSERT( !pProt
->isBlockEditable( ScRange( 0, 4, 0, 0, 4, 0))); // editable with descriptor
650 CPPUNIT_ASSERT( !pProt
->isBlockEditable( ScRange( 0, 5, 0, 0, 5, 0))); // editable with descriptor and password "foo"
651 CPPUNIT_ASSERT( pProt
->isBlockEditable( ScRange( 0, 1, 0, 0, 2, 0))); // union of two different editables
652 CPPUNIT_ASSERT( !pProt
->isBlockEditable( ScRange( 0, 0, 0, 0, 1, 0))); // union of locked and editable
653 CPPUNIT_ASSERT( !pProt
->isBlockEditable( ScRange( 0, 2, 0, 0, 3, 0))); // union of editable and password editable
656 void ScFiltersTest::testEnhancedProtectionXLS()
658 ScDocShellRef xDocSh
= loadDoc("enhanced-protection.", FORMAT_XLS
);
659 CPPUNIT_ASSERT(xDocSh
.is());
660 ScDocument
& rDoc
= xDocSh
->GetDocument();
662 testEnhancedProtectionImpl( rDoc
);
667 void ScFiltersTest::testEnhancedProtectionXLSX()
669 ScDocShellRef xDocSh
= loadDoc("enhanced-protection.", FORMAT_XLSX
);
670 CPPUNIT_ASSERT(xDocSh
.is());
671 ScDocument
& rDoc
= xDocSh
->GetDocument();
673 testEnhancedProtectionImpl( rDoc
);
678 void ScFiltersTest::testSortWithSharedFormulasODS()
680 ScDocShellRef xDocSh
= loadDoc("shared-formula/sort-crash.", FORMAT_ODS
, true);
681 CPPUNIT_ASSERT(xDocSh
.is());
682 ScDocument
& rDoc
= xDocSh
->GetDocument();
684 // E2:E10 should be shared.
685 const ScFormulaCell
* pFC
= rDoc
.GetFormulaCell(ScAddress(4,1,0));
687 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(1), pFC
->GetSharedTopRow());
688 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(9), pFC
->GetSharedLength());
690 // E12:E17 should be shared.
691 pFC
= rDoc
.GetFormulaCell(ScAddress(4,11,0));
693 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(11), pFC
->GetSharedTopRow());
694 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(6), pFC
->GetSharedLength());
696 // Set A1:E17 as an anonymous database range to sheet, or else Calc would
697 // refuse to sort the range.
698 std::unique_ptr
<ScDBData
> pDBData(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 4, 16, true, true));
699 rDoc
.SetAnonymousDBData(0, std::move(pDBData
));
701 // Sort ascending by Column E.
703 ScSortParam aSortData
;
707 aSortData
.nRow2
= 16;
708 aSortData
.bHasHeader
= true;
709 aSortData
.maKeyState
[0].bDoSort
= true;
710 aSortData
.maKeyState
[0].nField
= 4;
711 aSortData
.maKeyState
[0].bAscending
= true;
713 // Do the sorting. This should not crash.
714 ScDBDocFunc
aFunc(*xDocSh
);
715 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
716 CPPUNIT_ASSERT(bSorted
);
718 // After the sort, E2:E16 should be shared.
719 pFC
= rDoc
.GetFormulaCell(ScAddress(4,1,0));
721 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(1), pFC
->GetSharedTopRow());
722 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW
>(15), pFC
->GetSharedLength());
727 // https://bugs.freedesktop.org/attachment.cgi?id=100089 from fdo#77018
728 // mentioned also in fdo#79441
729 // Document contains cached external references.
730 void ScFiltersTest::testSortWithSheetExternalReferencesODS()
732 ScDocShellRef xDocSh
= loadDoc("sort-with-sheet-external-references.", FORMAT_ODS
, true);
733 CPPUNIT_ASSERT(xDocSh
.is());
734 ScDocument
& rDoc
= xDocSh
->GetDocument();
735 sc::AutoCalcSwitch
aACSwitch(rDoc
, true); // turn auto calc on.
738 // We reset the SortRefUpdate value back to the original in tearDown().
739 ScInputOptions aInputOption
= SC_MOD()->GetInputOptions();
741 // The complete relative test only works with UpdateReferenceOnSort==true,
742 // but the internal and external sheet references have to work in both
745 aInputOption
.SetSortRefUpdate(true);
746 SC_MOD()->SetInputOptions(aInputOption
);
748 // Sort A15:D20 with relative row references. UpdateReferenceOnSort==true
749 // With in-sheet relative references.
750 testSortWithSheetExternalReferencesODS_Impl( xDocSh
, 14, 19, true);
752 // Undo sort with relative references to perform same sort.
753 rDoc
.GetUndoManager()->Undo();
756 aInputOption
.SetSortRefUpdate(false);
757 SC_MOD()->SetInputOptions(aInputOption
);
759 // Sort A15:D20 with relative row references. UpdateReferenceOnSort==false
760 // Without in-sheet relative references.
761 testSortWithSheetExternalReferencesODS_Impl( xDocSh
, 14, 19, false);
763 // Undo sort with relative references to perform new sort.
764 rDoc
.GetUndoManager()->Undo();
767 // Sort with absolute references has to work in both UpdateReferenceOnSort
770 aInputOption
.SetSortRefUpdate(true);
771 SC_MOD()->SetInputOptions(aInputOption
);
773 // Sort A23:D28 with absolute row references. UpdateReferenceOnSort==true
774 // With in-sheet relative references.
775 testSortWithSheetExternalReferencesODS_Impl( xDocSh
, 22, 27, true);
777 // Undo sort with absolute references to perform same sort.
778 rDoc
.GetUndoManager()->Undo();
781 aInputOption
.SetSortRefUpdate(false);
782 SC_MOD()->SetInputOptions(aInputOption
);
784 // Sort A23:D28 with absolute row references. UpdateReferenceOnSort==false
785 // With in-sheet relative references.
786 testSortWithSheetExternalReferencesODS_Impl( xDocSh
, 22, 27, true);
791 void ScFiltersTest::testSortWithSheetExternalReferencesODS_Impl( ScDocShellRef
const & xDocSh
, SCROW nRow1
, SCROW nRow2
,
792 bool bCheckRelativeInSheet
)
794 ScDocument
& rDoc
= xDocSh
->GetDocument();
796 // Check the original data is there.
797 for (SCROW nRow
=nRow1
+1; nRow
<= nRow2
; ++nRow
)
799 double const aCheck
[] = { 1, 2, 3, 4, 5 };
800 CPPUNIT_ASSERT_EQUAL( aCheck
[nRow
-nRow1
-1], rDoc
.GetValue( ScAddress(0,nRow
,0)));
802 for (SCROW nRow
=nRow1
+1; nRow
<= nRow2
; ++nRow
)
804 for (SCCOL nCol
=1; nCol
<= 3; ++nCol
)
806 double const aCheck
[] = { 1, 12, 123, 1234, 12345 };
807 CPPUNIT_ASSERT_EQUAL( aCheck
[nRow
-nRow1
-1], rDoc
.GetValue( ScAddress(nCol
,nRow
,0)));
811 // Set as an anonymous database range to sort.
812 std::unique_ptr
<ScDBData
> pDBData(new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, nRow1
, 3, nRow2
, true, true));
813 rDoc
.SetAnonymousDBData(0, std::move(pDBData
));
815 // Sort descending by Column A.
816 ScSortParam aSortData
;
819 aSortData
.nRow1
= nRow1
;
820 aSortData
.nRow2
= nRow2
;
821 aSortData
.bHasHeader
= true;
822 aSortData
.maKeyState
[0].bDoSort
= true;
823 aSortData
.maKeyState
[0].nField
= 0;
824 aSortData
.maKeyState
[0].bAscending
= false;
827 ScDBDocFunc
aFunc(*xDocSh
);
828 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
829 CPPUNIT_ASSERT(bSorted
);
832 // Check the sort and that all sheet references and external references are
833 // adjusted to point to the original location.
834 for (SCROW nRow
=nRow1
+1; nRow
<= nRow2
; ++nRow
)
836 double const aCheck
[] = { 5, 4, 3, 2, 1 };
837 CPPUNIT_ASSERT_EQUAL( aCheck
[nRow
-nRow1
-1], rDoc
.GetValue( ScAddress(0,nRow
,0)));
839 // The last column (D) are in-sheet relative references.
840 SCCOL nEndCol
= (bCheckRelativeInSheet
? 3 : 2);
841 for (SCROW nRow
=nRow1
+1; nRow
<= nRow2
; ++nRow
)
843 for (SCCOL nCol
=1; nCol
<= nEndCol
; ++nCol
)
845 double const aCheck
[] = { 12345, 1234, 123, 12, 1 };
846 CPPUNIT_ASSERT_EQUAL( aCheck
[nRow
-nRow1
-1], rDoc
.GetValue( ScAddress(nCol
,nRow
,0)));
851 void ScFiltersTest::testSortWithFormattingXLS()
853 ScDocShellRef xDocSh
= loadDoc("tdf129127.", FORMAT_XLS
, true);
854 CPPUNIT_ASSERT(xDocSh
.is());
855 ScDocument
& rDoc
= xDocSh
->GetDocument();
857 // Set as an anonymous database range to sort.
858 std::unique_ptr
<ScDBData
> pDBData(
859 new ScDBData(STR_DB_LOCAL_NONAME
, 0, 0, 0, 4, 9, false, false));
860 rDoc
.SetAnonymousDBData(0, std::move(pDBData
));
862 // Sort ascending by Row 1
863 ScSortParam aSortData
;
868 aSortData
.bHasHeader
= false;
869 aSortData
.bByRow
= false;
870 aSortData
.maKeyState
[0].bDoSort
= true;
871 aSortData
.maKeyState
[0].nField
= 0;
872 aSortData
.maKeyState
[0].bAscending
= true;
875 ScDBDocFunc
aFunc(*xDocSh
);
876 // Without the fix, sort would crash.
877 bool bSorted
= aFunc
.Sort(0, aSortData
, true, true, true);
878 CPPUNIT_ASSERT(bSorted
);
882 ScFiltersTest::ScFiltersTest()
883 : ScBootstrapFixture( "sc/qa/unit/data" )
884 , mbUpdateReferenceOnSort(false)
888 void ScFiltersTest::setUp()
890 test::BootstrapFixture::setUp();
892 // This is a bit of a fudge, we do this to ensure that ScGlobals::ensure,
893 // which is a private symbol to us, gets called
895 getMultiServiceFactory()->createInstance("com.sun.star.comp.Calc.SpreadsheetDocument");
896 CPPUNIT_ASSERT_MESSAGE("no calc component!", m_xCalcComponent
.is());
898 // one test sets this configuration option; make sure we remember the
900 ScInputOptions aInputOption
= SC_MOD()->GetInputOptions();
901 mbUpdateReferenceOnSort
= aInputOption
.GetSortRefUpdate();
904 void ScFiltersTest::tearDown()
906 uno::Reference
< lang::XComponent
>( m_xCalcComponent
, UNO_QUERY_THROW
)->dispose();
907 test::BootstrapFixture::tearDown();
909 // one test sets this configuration option; make sure we return it back
910 ScInputOptions aInputOption
= SC_MOD()->GetInputOptions();
911 if (mbUpdateReferenceOnSort
!= aInputOption
.GetSortRefUpdate())
913 aInputOption
.SetSortRefUpdate(mbUpdateReferenceOnSort
);
914 SC_MOD()->SetInputOptions(aInputOption
);
918 CPPUNIT_TEST_SUITE_REGISTRATION(ScFiltersTest
);
920 CPPUNIT_PLUGIN_IMPLEMENT();
922 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */