Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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 : */
9 :
10 : #include <sal/config.h>
11 : #include <unotest/filters-test.hxx>
12 : #include <test/bootstrapfixture.hxx>
13 : #include <rtl/strbuf.hxx>
14 : #include <osl/file.hxx>
15 :
16 : #include "scdll.hxx"
17 : #include <sfx2/app.hxx>
18 : #include <sfx2/docfilt.hxx>
19 : #include <sfx2/docfile.hxx>
20 : #include <sfx2/sfxmodelfactory.hxx>
21 : #include <svl/stritem.hxx>
22 :
23 : #include "helper/qahelper.hxx"
24 :
25 : #include "docsh.hxx"
26 : #include "inputopt.hxx"
27 : #include "postit.hxx"
28 : #include "patattr.hxx"
29 : #include "scitems.hxx"
30 : #include "document.hxx"
31 : #include "cellform.hxx"
32 : #include "drwlayer.hxx"
33 : #include "userdat.hxx"
34 : #include "formulacell.hxx"
35 : #include "tabprotection.hxx"
36 : #include <dbdocfun.hxx>
37 : #include <globalnames.hxx>
38 : #include <dbdata.hxx>
39 : #include <sortparam.hxx>
40 : #include "scopetools.hxx"
41 :
42 : #include <svx/svdpage.hxx>
43 :
44 : using namespace ::com::sun::star;
45 : using namespace ::com::sun::star::uno;
46 :
47 : /* Implementation of Filters test */
48 :
49 34 : class ScFiltersTest
50 : : public test::FiltersTest
51 : , public ScBootstrapFixture
52 : {
53 : public:
54 : ScFiltersTest();
55 :
56 : virtual void setUp() SAL_OVERRIDE;
57 : virtual void tearDown() SAL_OVERRIDE;
58 :
59 : virtual bool load( const OUString &rFilter, const OUString &rURL,
60 : const OUString &rUserData, SfxFilterFlags nFilterFlags,
61 : SotClipboardFormatId nClipboardID, unsigned int nFilterVersion) SAL_OVERRIDE;
62 : /**
63 : * Ensure CVEs remain unbroken
64 : */
65 : void testCVEs();
66 :
67 : //ods, xls, xlsx filter tests
68 : void testRangeNameODS(); // only test ods here, xls and xlsx in subsequent_filters-test
69 : void testContentODS();
70 : void testContentXLS();
71 : void testContentXLSX();
72 : void testContentXLSXStrict(); // strict OOXML
73 : void testContentLotus123();
74 : void testContentDIF();
75 : void testContentXLSB();
76 : //void testContentXLS_XML();
77 : void testSharedFormulaXLS();
78 : void testSharedFormulaXLSX();
79 : void testSheetNamesXLSX();
80 : void testLegacyCellAnchoredRotatedShape();
81 : void testEnhancedProtectionXLS();
82 : void testEnhancedProtectionXLSX();
83 : void testSortWithSharedFormulasODS();
84 : void testSortWithSheetExternalReferencesODS();
85 : void testSortWithSheetExternalReferencesODS_Impl( ScDocShellRef xDocShRef, SCROW nRow1, SCROW nRow2,
86 : bool bCheckRelativeInSheet );
87 :
88 2 : CPPUNIT_TEST_SUITE(ScFiltersTest);
89 1 : CPPUNIT_TEST(testCVEs);
90 1 : CPPUNIT_TEST(testRangeNameODS);
91 1 : CPPUNIT_TEST(testContentODS);
92 1 : CPPUNIT_TEST(testContentXLS);
93 1 : CPPUNIT_TEST(testContentXLSX);
94 1 : CPPUNIT_TEST(testContentXLSXStrict);
95 1 : CPPUNIT_TEST(testContentLotus123);
96 1 : CPPUNIT_TEST(testContentDIF);
97 1 : CPPUNIT_TEST(testContentXLSB);
98 : //CPPUNIT_TEST(testContentXLS_XML);
99 1 : CPPUNIT_TEST(testSharedFormulaXLS);
100 1 : CPPUNIT_TEST(testSharedFormulaXLSX);
101 1 : CPPUNIT_TEST(testSheetNamesXLSX);
102 1 : CPPUNIT_TEST(testLegacyCellAnchoredRotatedShape);
103 1 : CPPUNIT_TEST(testEnhancedProtectionXLS);
104 1 : CPPUNIT_TEST(testEnhancedProtectionXLSX);
105 1 : CPPUNIT_TEST(testSortWithSharedFormulasODS);
106 1 : CPPUNIT_TEST(testSortWithSheetExternalReferencesODS);
107 :
108 5 : CPPUNIT_TEST_SUITE_END();
109 :
110 : private:
111 : uno::Reference<uno::XInterface> m_xCalcComponent;
112 : bool mbUpdateReferenceOnSort; ///< Remember the configuration option so that we can set it back.
113 : };
114 :
115 25 : bool ScFiltersTest::load(const OUString &rFilter, const OUString &rURL,
116 : const OUString &rUserData, SfxFilterFlags nFilterFlags,
117 : SotClipboardFormatId nClipboardID, unsigned int nFilterVersion)
118 : {
119 : ScDocShellRef xDocShRef = ScBootstrapFixture::load(rURL, rFilter, rUserData,
120 25 : OUString(), nFilterFlags, nClipboardID, nFilterVersion );
121 25 : bool bLoaded = xDocShRef.Is();
122 : //reference counting of ScDocShellRef is very confused.
123 25 : if (bLoaded)
124 21 : xDocShRef->DoClose();
125 25 : return bLoaded;
126 : }
127 :
128 1 : void ScFiltersTest::testCVEs()
129 : {
130 : #ifndef DISABLE_CVE_TESTS
131 : testDir(OUString("Quattro Pro 6.0"),
132 1 : getURLFromSrc("/sc/qa/unit/data/qpro/"), OUString());
133 :
134 : //warning, the current "sylk filter" in sc (docsh.cxx) automatically
135 : //chains on failure on trying as csv, rtf, etc. so "success" may
136 : //not indicate that it imported as .slk.
137 : testDir(OUString("SYLK"),
138 1 : getURLFromSrc("/sc/qa/unit/data/slk/"), OUString());
139 :
140 : testDir(OUString("MS Excel 97"),
141 1 : getURLFromSrc("/sc/qa/unit/data/xls/"), OUString());
142 :
143 : testDir(OUString("dBase"),
144 1 : getURLFromSrc("/sc/qa/unit/data/dbf/"), OUString());
145 :
146 : testDir(OUString("Lotus"),
147 1 : getURLFromSrc("/sc/qa/unit/data/wks/"), OUString());
148 :
149 : #endif
150 1 : }
151 :
152 : namespace {
153 :
154 1 : void testRangeNameImpl(ScDocument& rDoc)
155 : {
156 : //check one range data per sheet and one global more detailed
157 : //add some more checks here
158 1 : ScRangeData* pRangeData = rDoc.GetRangeName()->findByUpperName(OUString("GLOBAL1"));
159 1 : CPPUNIT_ASSERT_MESSAGE("range name Global1 not found", pRangeData);
160 : double aValue;
161 1 : rDoc.GetValue(1,0,0,aValue);
162 1 : CPPUNIT_ASSERT_MESSAGE("range name Global1 should reference Sheet1.A1", aValue == 1);
163 1 : pRangeData = rDoc.GetRangeName(0)->findByUpperName(OUString("LOCAL1"));
164 1 : CPPUNIT_ASSERT_MESSAGE("range name Sheet1.Local1 not found", pRangeData);
165 1 : rDoc.GetValue(1,2,0,aValue);
166 1 : CPPUNIT_ASSERT_MESSAGE("range name Sheet1.Local1 should reference Sheet1.A3", aValue == 3);
167 1 : pRangeData = rDoc.GetRangeName(1)->findByUpperName(OUString("LOCAL2"));
168 1 : CPPUNIT_ASSERT_MESSAGE("range name Sheet2.Local2 not found", pRangeData);
169 : //check for correct results for the remaining formulas
170 1 : rDoc.GetValue(1,1,0, aValue);
171 1 : CPPUNIT_ASSERT_MESSAGE("=global2 should be 2", aValue == 2);
172 1 : rDoc.GetValue(1,3,0, aValue);
173 1 : CPPUNIT_ASSERT_MESSAGE("=local2 should be 4", aValue == 4);
174 1 : rDoc.GetValue(2,0,0, aValue);
175 1 : CPPUNIT_ASSERT_MESSAGE("=SUM(global3) should be 10", aValue == 10);
176 1 : }
177 :
178 : }
179 :
180 1 : void ScFiltersTest::testRangeNameODS()
181 : {
182 1 : ScDocShellRef xDocSh = loadDoc("named-ranges-global.", ODS);
183 :
184 1 : CPPUNIT_ASSERT_MESSAGE("Failed to load named-ranges-globals.*", xDocSh.Is());
185 :
186 1 : xDocSh->DoHardRecalc(true);
187 :
188 1 : ScDocument& rDoc = xDocSh->GetDocument();
189 1 : testRangeNameImpl(rDoc);
190 :
191 2 : OUString aSheet2CSV("rangeExp_Sheet2.");
192 2 : OUString aCSVPath;
193 1 : createCSVPath( aSheet2CSV, aCSVPath );
194 1 : testFile( aCSVPath, rDoc, 1);
195 2 : xDocSh->DoClose();
196 1 : }
197 :
198 : namespace {
199 :
200 6 : void testContentImpl(ScDocument& rDoc, sal_Int32 nFormat ) //same code for ods, xls, xlsx
201 : {
202 : double fValue;
203 : //check value import
204 6 : rDoc.GetValue(0,0,0,fValue);
205 6 : CPPUNIT_ASSERT_MESSAGE("value not imported correctly", fValue == 1);
206 6 : rDoc.GetValue(0,1,0,fValue);
207 6 : CPPUNIT_ASSERT_MESSAGE("value not imported correctly", fValue == 2);
208 6 : OUString aString = rDoc.GetString(1, 0, 0);
209 :
210 : //check string import
211 6 : CPPUNIT_ASSERT_MESSAGE("string imported not correctly", aString == "String1");
212 6 : aString = rDoc.GetString(1, 1, 0);
213 6 : CPPUNIT_ASSERT_MESSAGE("string not imported correctly", aString == "String2");
214 :
215 : //check basic formula import
216 : // in case of DIF it just contains values
217 6 : rDoc.GetValue(2,0,0,fValue);
218 6 : CPPUNIT_ASSERT_MESSAGE("=2*3", fValue == 6);
219 6 : rDoc.GetValue(2,1,0,fValue);
220 6 : CPPUNIT_ASSERT_MESSAGE("=2+3", fValue == 5);
221 6 : rDoc.GetValue(2,2,0,fValue);
222 6 : CPPUNIT_ASSERT_MESSAGE("=2-3", fValue == -1);
223 6 : rDoc.GetValue(2,3,0,fValue);
224 6 : CPPUNIT_ASSERT_MESSAGE("=C1+C2", fValue == 11);
225 :
226 : //check merged cells import
227 6 : if(nFormat != LOTUS123 && nFormat != DIF)
228 : {
229 5 : SCCOL nCol = 4;
230 5 : SCROW nRow = 1;
231 5 : rDoc.ExtendMerge(4, 1, nCol, nRow, 0, false);
232 5 : CPPUNIT_ASSERT_MESSAGE("merged cells are not imported", nCol == 5 && nRow == 2);
233 :
234 : //check notes import
235 5 : ScAddress aAddress(7, 2, 0);
236 5 : ScPostIt* pNote = rDoc.GetNote(aAddress);
237 5 : CPPUNIT_ASSERT_MESSAGE("note not imported", pNote);
238 5 : CPPUNIT_ASSERT_EQUAL_MESSAGE("note text not imported correctly", pNote->GetText(), OUString("Test"));
239 6 : }
240 :
241 : //add additional checks here
242 6 : }
243 :
244 : }
245 :
246 1 : void ScFiltersTest::testContentODS()
247 : {
248 1 : ScDocShellRef xDocSh = loadDoc("universal-content.", ODS);
249 1 : xDocSh->DoHardRecalc(true);
250 :
251 1 : ScDocument& rDoc = xDocSh->GetDocument();
252 1 : testContentImpl(rDoc, ODS);
253 1 : xDocSh->DoClose();
254 1 : }
255 :
256 1 : void ScFiltersTest::testContentXLS()
257 : {
258 1 : ScDocShellRef xDocSh = loadDoc("universal-content.", XLS);
259 1 : xDocSh->DoHardRecalc(true);
260 :
261 1 : ScDocument& rDoc = xDocSh->GetDocument();
262 1 : testContentImpl(rDoc, XLS);
263 1 : xDocSh->DoClose();
264 1 : }
265 :
266 1 : void ScFiltersTest::testContentXLSX()
267 : {
268 1 : ScDocShellRef xDocSh = loadDoc("universal-content.", XLSX);
269 1 : xDocSh->DoHardRecalc(true);
270 :
271 1 : ScDocument& rDoc = xDocSh->GetDocument();
272 1 : testContentImpl(rDoc, XLSX);
273 1 : xDocSh->DoClose();
274 1 : }
275 :
276 1 : void ScFiltersTest::testContentXLSXStrict()
277 : {
278 1 : ScDocShellRef xDocSh = loadDoc("universal-content-strict.", XLSX);
279 1 : xDocSh->DoHardRecalc(true);
280 :
281 1 : ScDocument& rDoc = xDocSh->GetDocument();
282 1 : testContentImpl(rDoc, XLSX);
283 1 : xDocSh->DoClose();
284 1 : }
285 :
286 1 : void ScFiltersTest::testContentLotus123()
287 : {
288 1 : ScDocShellRef xDocSh = loadDoc("universal-content.", LOTUS123);
289 1 : xDocSh->DoHardRecalc(true);
290 :
291 1 : ScDocument& rDoc = xDocSh->GetDocument();
292 1 : CPPUNIT_ASSERT(&rDoc);
293 1 : testContentImpl(rDoc, LOTUS123);
294 1 : xDocSh->DoClose();
295 1 : }
296 :
297 1 : void ScFiltersTest::testContentDIF()
298 : {
299 1 : ScDocShellRef xDocSh = loadDoc("universal-content.", DIF);
300 :
301 1 : ScDocument& rDoc = xDocSh->GetDocument();
302 1 : CPPUNIT_ASSERT(&rDoc);
303 1 : xDocSh->DoClose();
304 1 : }
305 :
306 1 : void ScFiltersTest::testContentXLSB()
307 : {
308 1 : ScDocShellRef xDocSh = loadDoc("universal-content.", XLSB);
309 1 : xDocSh->DoHardRecalc(true);
310 :
311 1 : ScDocument& rDoc = xDocSh->GetDocument();
312 1 : testContentImpl(rDoc, XLSB);
313 1 : xDocSh->DoClose();
314 1 : }
315 :
316 : // void ScFiltersTest::testContentXLS_XML()
317 : // {
318 : // ScDocShellRef xDocSh = loadDoc("universal-content.", XLS_XML);
319 : // CPPUNIT_ASSERT(xDocSh);
320 : //
321 : // ScDocument& rDoc = xDocSh->GetDocument();
322 : // CPPUNIT_ASSERT(&rDoc);
323 : // testContentImpl(pDoc, XLS_XML);
324 : // xDocSh->DoClose();
325 : // }
326 :
327 1 : void ScFiltersTest::testSharedFormulaXLS()
328 : {
329 1 : ScDocShellRef xDocSh = loadDoc("shared-formula/basic.", XLS);
330 1 : CPPUNIT_ASSERT(xDocSh.Is());
331 1 : ScDocument& rDoc = xDocSh->GetDocument();
332 1 : xDocSh->DoHardRecalc(true);
333 : // Check the results of formula cells in the shared formula range.
334 19 : for (SCROW i = 1; i <= 18; ++i)
335 : {
336 18 : double fVal = rDoc.GetValue(ScAddress(1,i,0));
337 18 : double fCheck = i*10.0;
338 18 : CPPUNIT_ASSERT_EQUAL(fCheck, fVal);
339 : }
340 :
341 1 : ScFormulaCell* pCell = rDoc.GetFormulaCell(ScAddress(1,18,0));
342 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pCell);
343 2 : ScFormulaCellGroupRef xGroup = pCell->GetCellGroup();
344 1 : CPPUNIT_ASSERT_MESSAGE("This cell should be a part of a cell group.", xGroup);
345 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup->mpTopCell->aPos.Row() == 1 && xGroup->mnLength == 18);
346 :
347 1 : xDocSh->DoClose();
348 :
349 : // The following file contains shared formula whose range is inaccurate.
350 : // Excel can easily mess up shared formula ranges, so we need to be able
351 : // to handle these wrong ranges that Excel stores.
352 :
353 1 : xDocSh = loadDoc("shared-formula/gap.", XLS);
354 1 : CPPUNIT_ASSERT(xDocSh.Is());
355 1 : ScDocument& rDoc2 = xDocSh->GetDocument();
356 1 : rDoc2.CalcAll();
357 :
358 1 : if (!checkFormula(rDoc2, ScAddress(1,0,0), "A1*20"))
359 0 : CPPUNIT_FAIL("Wrong formula.");
360 :
361 1 : if (!checkFormula(rDoc2, ScAddress(1,1,0), "A2*20"))
362 0 : CPPUNIT_FAIL("Wrong formula.");
363 :
364 1 : if (!checkFormula(rDoc2, ScAddress(1,2,0), "A3*20"))
365 0 : CPPUNIT_FAIL("Wrong formula.");
366 :
367 : // There is an intentional gap at row 4.
368 :
369 1 : if (!checkFormula(rDoc2, ScAddress(1,4,0), "A5*20"))
370 0 : CPPUNIT_FAIL("Wrong formula.");
371 :
372 1 : if (!checkFormula(rDoc2, ScAddress(1,5,0), "A6*20"))
373 0 : CPPUNIT_FAIL("Wrong formula.");
374 :
375 1 : if (!checkFormula(rDoc2, ScAddress(1,6,0), "A7*20"))
376 0 : CPPUNIT_FAIL("Wrong formula.");
377 :
378 1 : if (!checkFormula(rDoc2, ScAddress(1,7,0), "A8*20"))
379 0 : CPPUNIT_FAIL("Wrong formula.");
380 :
381 : // We re-group formula cells on load. Let's check that as well.
382 :
383 1 : ScFormulaCell* pFC = rDoc2.GetFormulaCell(ScAddress(1,0,0));
384 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch formula cell.", pFC);
385 1 : CPPUNIT_ASSERT_MESSAGE("This should be the top cell in formula group.", pFC->IsSharedTop());
386 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
387 :
388 1 : pFC = rDoc2.GetFormulaCell(ScAddress(1,4,0));
389 1 : CPPUNIT_ASSERT_MESSAGE("Failed to fetch formula cell.", pFC);
390 1 : CPPUNIT_ASSERT_MESSAGE("This should be the top cell in formula group.", pFC->IsSharedTop());
391 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
392 :
393 2 : xDocSh->DoClose();
394 1 : }
395 :
396 1 : void ScFiltersTest::testSharedFormulaXLSX()
397 : {
398 1 : ScDocShellRef xDocSh = loadDoc("shared-formula/basic.", XLSX);
399 1 : ScDocument& rDoc = xDocSh->GetDocument();
400 1 : CPPUNIT_ASSERT(&rDoc);
401 1 : xDocSh->DoHardRecalc(true);
402 : // Check the results of formula cells in the shared formula range.
403 19 : for (SCROW i = 1; i <= 18; ++i)
404 : {
405 18 : double fVal = rDoc.GetValue(ScAddress(1,i,0));
406 18 : double fCheck = i*10.0;
407 18 : CPPUNIT_ASSERT_EQUAL(fCheck, fVal);
408 : }
409 :
410 1 : ScFormulaCell* pCell = rDoc.GetFormulaCell(ScAddress(1,18,0));
411 1 : CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pCell);
412 2 : ScFormulaCellGroupRef xGroup = pCell->GetCellGroup();
413 1 : CPPUNIT_ASSERT_MESSAGE("This cell should be a part of a cell group.", xGroup);
414 1 : CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup->mpTopCell->aPos.Row() == 1 && xGroup->mnLength == 18);
415 :
416 2 : xDocSh->DoClose();
417 1 : }
418 :
419 1 : void ScFiltersTest::testSheetNamesXLSX()
420 : {
421 1 : ScDocShellRef xDocSh = loadDoc("sheet-names.", XLSX);
422 1 : ScDocument& rDoc = xDocSh->GetDocument();
423 :
424 2 : std::vector<OUString> aTabNames = rDoc.GetAllTableNames();
425 1 : CPPUNIT_ASSERT_MESSAGE("The document should have 5 sheets in total.", aTabNames.size() == 5);
426 1 : CPPUNIT_ASSERT_EQUAL(OUString("S&P"), aTabNames[0]);
427 1 : CPPUNIT_ASSERT_EQUAL(OUString("Sam's Club"), aTabNames[1]);
428 1 : CPPUNIT_ASSERT_EQUAL(OUString("\"The Sheet\""), aTabNames[2]);
429 1 : CPPUNIT_ASSERT_EQUAL(OUString("A<B"), aTabNames[3]);
430 1 : CPPUNIT_ASSERT_EQUAL(OUString("C>D"), aTabNames[4]);
431 :
432 2 : xDocSh->DoClose();
433 1 : }
434 :
435 5 : void impl_testLegacyCellAnchoredRotatedShape( ScDocument& rDoc, Rectangle& aRect, ScDrawObjData& aAnchor, long TOLERANCE = 30 /* 30 hmm */ )
436 : {
437 5 : ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
438 5 : CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
439 5 : SdrPage* pPage = pDrawLayer->GetPage(0);
440 5 : CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
441 5 : CPPUNIT_ASSERT_EQUAL( static_cast<size_t>(1), pPage->GetObjCount() );
442 :
443 5 : SdrObject* pObj = pPage->GetObj(0);
444 5 : const Rectangle& aSnap = pObj->GetSnapRect();
445 5 : printf("expected height %ld actual %ld\n", aRect.GetHeight(), aSnap.GetHeight() );
446 5 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
447 5 : printf("expected width %ld actual %ld\n", aRect.GetWidth(), aSnap.GetWidth() );
448 5 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
449 5 : printf("expected left %ld actual %ld\n", aRect.Left(), aSnap.Left() );
450 5 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect.Left(), aSnap.Left(), TOLERANCE ) );
451 5 : printf("expected right %ld actual %ld\n", aRect.Top(), aSnap.Top() );
452 5 : CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect.Top(), aSnap.Top(), TOLERANCE ) );
453 :
454 5 : ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj );
455 5 : CPPUNIT_ASSERT_MESSAGE("expected object meta data", pData);
456 5 : printf("expected startrow %" SAL_PRIdINT32 " actual %" SAL_PRIdINT32 "\n", aAnchor.maStart.Row(), pData->maStart.Row() );
457 5 : CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() );
458 5 : printf("expected startcol %d actual %d\n", aAnchor.maStart.Col(), pData->maStart.Col() );
459 5 : CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() );
460 5 : printf("expected endrow %" SAL_PRIdINT32 " actual %" SAL_PRIdINT32 "\n", aAnchor.maEnd.Row(), pData->maEnd.Row() );
461 5 : CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() );
462 5 : printf("expected endcol %d actual %d\n", aAnchor.maEnd.Col(), pData->maEnd.Col() );
463 5 : CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() );
464 5 : }
465 :
466 1 : void ScFiltersTest::testLegacyCellAnchoredRotatedShape()
467 : {
468 : {
469 : // This example doc contains cell anchored shape that is rotated, the
470 : // rotated shape is in fact cliped by the sheet boundries ( and thus
471 : // is a good edge case test to see if we import it still correctly )
472 1 : ScDocShellRef xDocSh = loadDoc("legacycellanchoredrotatedclippedshape.", ODS);
473 :
474 1 : ScDocument& rDoc = xDocSh->GetDocument();
475 1 : CPPUNIT_ASSERT(&rDoc);
476 : // ensure the imported legacy rotated shape is in the expected position
477 1 : Rectangle aRect( 6000, -2000, 8000, 4000 );
478 : // ensure the imported ( and converted ) anchor ( note we internally now store the anchor in
479 : // terms of the rotated shape ) is more or less contains the correct info
480 2 : ScDrawObjData aAnchor;
481 1 : aAnchor.maStart.SetRow( 0 );
482 1 : aAnchor.maStart.SetCol( 5 );
483 1 : aAnchor.maEnd.SetRow( 3 );
484 1 : aAnchor.maEnd.SetCol( 7 );
485 1 : impl_testLegacyCellAnchoredRotatedShape( rDoc, aRect, aAnchor );
486 : // test save and reload
487 : // for some reason having this test in subsequent_export-test.cxx causes
488 : // a core dump in editeng ( so moved to here )
489 1 : xDocSh = saveAndReload( &(*xDocSh), ODS);
490 1 : ScDocument& rDoc2 = xDocSh->GetDocument();
491 1 : CPPUNIT_ASSERT(&rDoc2);
492 1 : impl_testLegacyCellAnchoredRotatedShape( rDoc2, aRect, aAnchor );
493 :
494 2 : xDocSh->DoClose();
495 : }
496 : {
497 : // This example doc contains cell anchored shape that is rotated, the
498 : // rotated shape is in fact clipped by the sheet boundries, additionally
499 : // the shape is completely hidden because the rows the shape occupies
500 : // are hidden
501 1 : ScDocShellRef xDocSh = loadDoc("legacycellanchoredrotatedhiddenshape.", ODS, true);
502 1 : ScDocument& rDoc = xDocSh->GetDocument();
503 1 : CPPUNIT_ASSERT(&rDoc);
504 : // ensure the imported legacy rotated shape is in the expected position
505 : // when a shape is fully hidden reloading seems to result is in some errors, usually
506 : // ( same but different error happens pre-patch ) - we should do better here, I regard it
507 : // as a pre-existing bug though (#FIXME)
508 : //Rectangle aRect( 6000, -2000, 8000, 4000 ); // proper dimensions
509 1 : Rectangle aRect( 6000, -2000, 7430, 4000 );
510 : // ensure the imported (and converted) anchor (note we internally now store the anchor in
511 : // terms of the rotated shape) is more or less contains the correct info
512 2 : ScDrawObjData aAnchor;
513 1 : aAnchor.maStart.SetRow( 0 );
514 1 : aAnchor.maStart.SetCol( 5 );
515 1 : aAnchor.maEnd.SetRow( 3 );
516 1 : aAnchor.maEnd.SetCol( 7 );
517 1 : rDoc.ShowRows(0, 9, 0, true); // show relavent rows
518 1 : rDoc.SetDrawPageSize(0); // trigger recalcpos
519 :
520 : // apply hefty (1 mm) tolerance here, as some opensuse tinderbox
521 : // failing
522 1 : impl_testLegacyCellAnchoredRotatedShape( rDoc, aRect, aAnchor, 100 );
523 :
524 2 : xDocSh->DoClose();
525 : }
526 : {
527 : // This example doc contains cell anchored shape that is rotated
528 1 : ScDocShellRef xDocSh = loadDoc("legacycellanchoredrotatedshape.", ODS);
529 :
530 1 : ScDocument& rDoc = xDocSh->GetDocument();
531 1 : CPPUNIT_ASSERT(&rDoc);
532 : // ensure the imported legacy rotated shape is in the expected position
533 1 : Rectangle aRect( 6000, 3000, 8000, 9000 );
534 : // ensure the imported (and converted) anchor (note we internally now store the anchor in
535 : // terms of the rotated shape) more or less contains the correct info
536 :
537 2 : ScDrawObjData aAnchor;
538 1 : aAnchor.maStart.SetRow( 3 );
539 1 : aAnchor.maStart.SetCol( 6 );
540 1 : aAnchor.maEnd.SetRow( 9 );
541 1 : aAnchor.maEnd.SetCol( 7 );
542 : // test import
543 1 : impl_testLegacyCellAnchoredRotatedShape( rDoc, aRect, aAnchor );
544 : // test save and reload
545 1 : xDocSh = saveAndReload( &(*xDocSh), ODS);
546 1 : ScDocument& rDoc2 = xDocSh->GetDocument();
547 1 : CPPUNIT_ASSERT(&rDoc2);
548 1 : impl_testLegacyCellAnchoredRotatedShape( rDoc2, aRect, aAnchor );
549 :
550 2 : xDocSh->DoClose();
551 : }
552 1 : }
553 :
554 2 : void testEnhancedProtectionImpl( ScDocument& rDoc )
555 : {
556 2 : const ScTableProtection* pProt = rDoc.GetTabProtection(0);
557 :
558 2 : CPPUNIT_ASSERT( pProt);
559 :
560 2 : CPPUNIT_ASSERT( !pProt->isBlockEditable( ScRange( 0, 0, 0, 0, 0, 0))); // locked
561 2 : CPPUNIT_ASSERT( pProt->isBlockEditable( ScRange( 0, 1, 0, 0, 1, 0))); // editable without password
562 2 : CPPUNIT_ASSERT( pProt->isBlockEditable( ScRange( 0, 2, 0, 0, 2, 0))); // editable without password
563 2 : CPPUNIT_ASSERT( !pProt->isBlockEditable( ScRange( 0, 3, 0, 0, 3, 0))); // editable with password "foo"
564 2 : CPPUNIT_ASSERT( !pProt->isBlockEditable( ScRange( 0, 4, 0, 0, 4, 0))); // editable with descriptor
565 2 : CPPUNIT_ASSERT( !pProt->isBlockEditable( ScRange( 0, 5, 0, 0, 5, 0))); // editable with descriptor and password "foo"
566 2 : CPPUNIT_ASSERT( pProt->isBlockEditable( ScRange( 0, 1, 0, 0, 2, 0))); // union of two different editables
567 2 : CPPUNIT_ASSERT( !pProt->isBlockEditable( ScRange( 0, 0, 0, 0, 1, 0))); // union of locked and editable
568 2 : CPPUNIT_ASSERT( !pProt->isBlockEditable( ScRange( 0, 2, 0, 0, 3, 0))); // union of editable and password editable
569 2 : }
570 :
571 1 : void ScFiltersTest::testEnhancedProtectionXLS()
572 : {
573 1 : ScDocShellRef xDocSh = loadDoc("enhanced-protection.", XLS);
574 1 : CPPUNIT_ASSERT(xDocSh.Is());
575 1 : ScDocument& rDoc = xDocSh->GetDocument();
576 :
577 1 : testEnhancedProtectionImpl( rDoc);
578 :
579 1 : xDocSh->DoClose();
580 1 : }
581 :
582 1 : void ScFiltersTest::testEnhancedProtectionXLSX()
583 : {
584 1 : ScDocShellRef xDocSh = loadDoc("enhanced-protection.", XLSX);
585 1 : CPPUNIT_ASSERT(xDocSh.Is());
586 1 : ScDocument& rDoc = xDocSh->GetDocument();
587 :
588 1 : testEnhancedProtectionImpl( rDoc);
589 :
590 1 : xDocSh->DoClose();
591 1 : }
592 :
593 1 : void ScFiltersTest::testSortWithSharedFormulasODS()
594 : {
595 1 : ScDocShellRef xDocSh = loadDoc("shared-formula/sort-crash.", ODS, true);
596 1 : CPPUNIT_ASSERT(xDocSh.Is());
597 1 : ScDocument& rDoc = xDocSh->GetDocument();
598 :
599 : // E2:E10 should be shared.
600 1 : const ScFormulaCell* pFC = rDoc.GetFormulaCell(ScAddress(4,1,0));
601 1 : CPPUNIT_ASSERT(pFC);
602 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
603 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedLength());
604 :
605 : // E12:E17 should be shared.
606 1 : pFC = rDoc.GetFormulaCell(ScAddress(4,11,0));
607 1 : CPPUNIT_ASSERT(pFC);
608 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(11), pFC->GetSharedTopRow());
609 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), pFC->GetSharedLength());
610 :
611 : // Set A1:E17 as an anonymous database range to sheet, or else Calc would
612 : // refuse to sort the range.
613 1 : ScDBData* pDBData = new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 4, 16, true, true);
614 1 : rDoc.SetAnonymousDBData(0, pDBData);
615 :
616 : // Sort ascending by Column E.
617 :
618 2 : ScSortParam aSortData;
619 1 : aSortData.nCol1 = 0;
620 1 : aSortData.nCol2 = 4;
621 1 : aSortData.nRow1 = 0;
622 1 : aSortData.nRow2 = 16;
623 1 : aSortData.bHasHeader = true;
624 1 : aSortData.maKeyState[0].bDoSort = true;
625 1 : aSortData.maKeyState[0].nField = 4;
626 1 : aSortData.maKeyState[0].bAscending = true;
627 :
628 : // Do the sorting. This should not crash.
629 2 : ScDBDocFunc aFunc(*xDocSh);
630 1 : bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
631 1 : CPPUNIT_ASSERT(bSorted);
632 :
633 : // After the sort, E2:E16 should be shared.
634 1 : pFC = rDoc.GetFormulaCell(ScAddress(4,1,0));
635 1 : CPPUNIT_ASSERT(pFC);
636 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
637 1 : CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(15), pFC->GetSharedLength());
638 :
639 2 : xDocSh->DoClose();
640 1 : }
641 :
642 : // https://bugs.freedesktop.org/attachment.cgi?id=100089 from fdo#77018
643 : // mentioned also in fdo#79441
644 : // Document contains cached external references.
645 1 : void ScFiltersTest::testSortWithSheetExternalReferencesODS()
646 : {
647 1 : ScDocShellRef xDocSh = loadDoc("sort-with-sheet-external-references.", ODS, true);
648 1 : CPPUNIT_ASSERT(xDocSh.Is());
649 1 : ScDocument& rDoc = xDocSh->GetDocument();
650 2 : sc::AutoCalcSwitch aACSwitch(rDoc, true); // turn auto calc on.
651 1 : rDoc.CalcAll();
652 :
653 : // We reset the SortRefUpdate value back to the original in tearDown().
654 2 : ScInputOptions aInputOption = SC_MOD()->GetInputOptions();
655 :
656 : // The complete relative test only works with UpdateReferenceOnSort==true,
657 : // but the internal and external sheet references have to work in both
658 : // modes.
659 :
660 1 : aInputOption.SetSortRefUpdate(true);
661 1 : SC_MOD()->SetInputOptions(aInputOption);
662 :
663 : // Sort A15:D20 with relative row references. UpdateReferenceOnSort==true
664 : // With in-sheet relative references.
665 1 : testSortWithSheetExternalReferencesODS_Impl( xDocSh, 14, 19, true);
666 :
667 : // Undo sort with relative references to perform same sort.
668 1 : rDoc.GetUndoManager()->Undo();
669 1 : rDoc.CalcAll();
670 :
671 1 : aInputOption.SetSortRefUpdate(false);
672 1 : SC_MOD()->SetInputOptions(aInputOption);
673 :
674 : // Sort A15:D20 with relative row references. UpdateReferenceOnSort==false
675 : // Without in-sheet relative references.
676 1 : testSortWithSheetExternalReferencesODS_Impl( xDocSh, 14, 19, false);
677 :
678 : // Undo sort with relative references to perform new sort.
679 1 : rDoc.GetUndoManager()->Undo();
680 1 : rDoc.CalcAll();
681 :
682 : // Sort with absolute references has to work in both UpdateReferenceOnSort
683 : // modes.
684 :
685 1 : aInputOption.SetSortRefUpdate(true);
686 1 : SC_MOD()->SetInputOptions(aInputOption);
687 :
688 : // Sort A23:D28 with absolute row references. UpdateReferenceOnSort==true
689 : // With in-sheet relative references.
690 1 : testSortWithSheetExternalReferencesODS_Impl( xDocSh, 22, 27, true);
691 :
692 : // Undo sort with absolute references to perform same sort.
693 1 : rDoc.GetUndoManager()->Undo();
694 1 : rDoc.CalcAll();
695 :
696 1 : aInputOption.SetSortRefUpdate(false);
697 1 : SC_MOD()->SetInputOptions(aInputOption);
698 :
699 : // Sort A23:D28 with absolute row references. UpdateReferenceOnSort==false
700 : // With in-sheet relative references.
701 1 : testSortWithSheetExternalReferencesODS_Impl( xDocSh, 22, 27, true);
702 :
703 2 : xDocSh->DoClose();
704 1 : }
705 :
706 4 : void ScFiltersTest::testSortWithSheetExternalReferencesODS_Impl( ScDocShellRef xDocSh, SCROW nRow1, SCROW nRow2,
707 : bool bCheckRelativeInSheet )
708 : {
709 4 : ScDocument& rDoc = xDocSh->GetDocument();
710 :
711 : // Check the original data is there.
712 24 : for (SCROW nRow=nRow1+1; nRow <= nRow2; ++nRow)
713 : {
714 20 : double aCheck[] = { 1, 2, 3, 4, 5 };
715 20 : CPPUNIT_ASSERT_EQUAL( aCheck[nRow-nRow1-1], rDoc.GetValue( ScAddress(0,nRow,0)));
716 : }
717 24 : for (SCROW nRow=nRow1+1; nRow <= nRow2; ++nRow)
718 : {
719 80 : for (SCCOL nCol=1; nCol <= 3; ++nCol)
720 : {
721 60 : double aCheck[] = { 1, 12, 123, 1234, 12345 };
722 60 : CPPUNIT_ASSERT_EQUAL( aCheck[nRow-nRow1-1], rDoc.GetValue( ScAddress(nCol,nRow,0)));
723 : }
724 : }
725 :
726 : // Set as an anonymous database range to sort.
727 4 : ScDBData* pDBData = new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, nRow1, 3, nRow2, true, true);
728 4 : rDoc.SetAnonymousDBData(0, pDBData);
729 :
730 : // Sort descending by Column A.
731 4 : ScSortParam aSortData;
732 4 : aSortData.nCol1 = 0;
733 4 : aSortData.nCol2 = 3;
734 4 : aSortData.nRow1 = nRow1;
735 4 : aSortData.nRow2 = nRow2;
736 4 : aSortData.bHasHeader = true;
737 4 : aSortData.maKeyState[0].bDoSort = true;
738 4 : aSortData.maKeyState[0].nField = 0;
739 4 : aSortData.maKeyState[0].bAscending = false;
740 :
741 : // Do the sorting.
742 8 : ScDBDocFunc aFunc(*xDocSh);
743 4 : bool bSorted = aFunc.Sort(0, aSortData, true, true, true);
744 4 : CPPUNIT_ASSERT(bSorted);
745 4 : rDoc.CalcAll();
746 :
747 : // Check the sort and that all sheet references and external references are
748 : // adjusted to point to the original location.
749 24 : for (SCROW nRow=nRow1+1; nRow <= nRow2; ++nRow)
750 : {
751 20 : double aCheck[] = { 5, 4, 3, 2, 1 };
752 20 : CPPUNIT_ASSERT_EQUAL( aCheck[nRow-nRow1-1], rDoc.GetValue( ScAddress(0,nRow,0)));
753 : }
754 : // The last column (D) are in-sheet relative references.
755 4 : SCCOL nEndCol = (bCheckRelativeInSheet ? 3 : 2);
756 24 : for (SCROW nRow=nRow1+1; nRow <= nRow2; ++nRow)
757 : {
758 75 : for (SCCOL nCol=1; nCol <= nEndCol; ++nCol)
759 : {
760 55 : double aCheck[] = { 12345, 1234, 123, 12, 1 };
761 55 : CPPUNIT_ASSERT_EQUAL( aCheck[nRow-nRow1-1], rDoc.GetValue( ScAddress(nCol,nRow,0)));
762 : }
763 4 : }
764 4 : }
765 :
766 17 : ScFiltersTest::ScFiltersTest()
767 : : ScBootstrapFixture( "/sc/qa/unit/data" )
768 17 : , mbUpdateReferenceOnSort(false)
769 : {
770 17 : }
771 :
772 17 : void ScFiltersTest::setUp()
773 : {
774 17 : test::BootstrapFixture::setUp();
775 :
776 : // This is a bit of a fudge, we do this to ensure that ScGlobals::ensure,
777 : // which is a private symbol to us, gets called
778 34 : m_xCalcComponent =
779 51 : getMultiServiceFactory()->createInstance("com.sun.star.comp.Calc.SpreadsheetDocument");
780 17 : CPPUNIT_ASSERT_MESSAGE("no calc component!", m_xCalcComponent.is());
781 :
782 : // one test sets this configuration option; make sure we remember the
783 : // original value
784 17 : ScInputOptions aInputOption = SC_MOD()->GetInputOptions();
785 17 : mbUpdateReferenceOnSort = aInputOption.GetSortRefUpdate();
786 17 : }
787 :
788 17 : void ScFiltersTest::tearDown()
789 : {
790 17 : uno::Reference< lang::XComponent >( m_xCalcComponent, UNO_QUERY_THROW )->dispose();
791 17 : test::BootstrapFixture::tearDown();
792 :
793 : // one test sets this configuration option; make sure we return it back
794 17 : ScInputOptions aInputOption = SC_MOD()->GetInputOptions();
795 17 : if (mbUpdateReferenceOnSort != aInputOption.GetSortRefUpdate())
796 : {
797 0 : aInputOption.SetSortRefUpdate(mbUpdateReferenceOnSort);
798 0 : SC_MOD()->SetInputOptions(aInputOption);
799 17 : }
800 17 : }
801 :
802 1 : CPPUNIT_TEST_SUITE_REGISTRATION(ScFiltersTest);
803 :
804 4 : CPPUNIT_PLUGIN_IMPLEMENT();
805 :
806 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|