nss: upgrade to release 3.73
[LibreOffice.git] / sc / qa / unit / filters-test.cxx
bloba994297ff2c0c33c8196aa72b1f09c31ceab62cf
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <sal/config.h>
11 #include <unotest/filters-test.hxx>
12 #include <test/bootstrapfixture.hxx>
14 #include "helper/qahelper.hxx"
16 #include <docsh.hxx>
17 #include <inputopt.hxx>
18 #include <postit.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>
27 #include <dbdata.hxx>
28 #include <sortparam.hxx>
29 #include <scopetools.hxx>
30 #include <scmod.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 */
40 class ScFiltersTest
41 : public test::FiltersTest
42 , public ScBootstrapFixture
44 public:
45 ScFiltersTest();
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;
53 /**
54 * Ensure CVEs remain unbroken
56 void testCVEs();
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();
74 void testTdf79998();
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();
113 private:
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.
126 if (bLoaded)
127 xDocShRef->DoClose();
128 return bLoaded;
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.
140 testDir("SYLK",
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);
152 testDir("dBase",
153 m_directories.getURLFromSrc("/sc/qa/unit/data/dbf/"));
155 testDir("Lotus",
156 m_directories.getURLFromSrc("/sc/qa/unit/data/wks/"));
158 #endif
161 namespace {
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);
169 double aValue;
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);
200 OUString aCSVPath;
201 createCSVPath( "rangeExp_Sheet2.", aCSVPath );
202 testFile( aCSVPath, rDoc, 1);
203 xDocSh->DoClose();
206 namespace {
208 void testContentImpl(ScDocument& rDoc, sal_Int32 nFormat ) //same code for ods, xls, xlsx
210 double fValue;
211 //check value import
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)
238 SCCOL nCol = 4;
239 SCROW nRow = 1;
240 rDoc.ExtendMerge(4, 1, nCol, nRow, 0);
241 CPPUNIT_ASSERT_MESSAGE("merged cells are not imported", nCol == 5 && nRow == 2);
243 //check notes import
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);
262 xDocSh->DoClose();
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);
272 xDocSh->DoClose();
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);
282 xDocSh->DoClose();
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);
292 xDocSh->DoClose();
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);
302 xDocSh->DoClose();
305 void ScFiltersTest::testContentofz9704()
307 OUString aFileName;
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());
319 xDocSh->DoClose();
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);
329 xDocSh->DoClose();
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);
339 xDocSh->DoClose();
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);
349 xDocSh->DoClose();
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);
372 xDocSh->DoClose();
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();
381 rDoc2.CalcAll();
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());
406 xDocSh->DoClose();
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);
428 xDocSh->DoClose();
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.
438 struct TestCase {
439 ScAddress aPos;
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);
457 xDocSh->DoClose();
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]);
473 xDocSh->DoClose();
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]);
491 xDocSh->DoClose();
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());
524 xDocSh->DoClose();
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 );
584 xDocSh->DoClose();
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
590 // are hidden
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 );
611 xDocSh->DoClose();
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 );
628 // test import
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 );
635 xDocSh->DoClose();
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);
664 xDocSh->DoClose();
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);
675 xDocSh->DoClose();
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));
686 CPPUNIT_ASSERT(pFC);
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));
692 CPPUNIT_ASSERT(pFC);
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;
704 aSortData.nCol1 = 0;
705 aSortData.nCol2 = 4;
706 aSortData.nRow1 = 0;
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));
720 CPPUNIT_ASSERT(pFC);
721 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
722 CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(15), pFC->GetSharedLength());
724 xDocSh->DoClose();
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.
736 rDoc.CalcAll();
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
743 // modes.
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();
754 rDoc.CalcAll();
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();
765 rDoc.CalcAll();
767 // Sort with absolute references has to work in both UpdateReferenceOnSort
768 // modes.
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();
779 rDoc.CalcAll();
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);
788 xDocSh->DoClose();
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;
817 aSortData.nCol1 = 0;
818 aSortData.nCol2 = 3;
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;
826 // Do the sorting.
827 ScDBDocFunc aFunc(*xDocSh);
828 bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
829 CPPUNIT_ASSERT(bSorted);
830 rDoc.CalcAll();
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;
864 aSortData.nCol1 = 0;
865 aSortData.nCol2 = 4;
866 aSortData.nRow1 = 0;
867 aSortData.nRow2 = 9;
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;
874 // Do the sorting.
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);
879 xDocSh->DoClose();
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
894 m_xCalcComponent =
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
899 // original value
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: */