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>
13 #include <rtl/strbuf.hxx>
14 #include <osl/file.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>
23 #define CALC_DEBUG_OUTPUT 0
24 #define TEST_BUG_FILES 0
26 #include "helper/qahelper.hxx"
30 #include "patattr.hxx"
31 #include "scitems.hxx"
32 #include "document.hxx"
33 #include "cellform.hxx"
34 #include "drwlayer.hxx"
35 #include "userdat.hxx"
36 #include <svx/svdpage.hxx>
38 using namespace ::com::sun::star
;
39 using namespace ::com::sun::star::uno
;
41 /* Implementation of Filters test */
44 : public test::FiltersTest
45 , public ScBootstrapFixture
51 virtual void tearDown();
53 virtual bool load( const OUString
&rFilter
, const OUString
&rURL
,
54 const OUString
&rUserData
, unsigned int nFilterFlags
,
55 unsigned int nClipboardID
, unsigned int nFilterVersion
);
57 * Ensure CVEs remain unbroken
61 //ods, xls, xlsx filter tests
62 void testRangeNameODS(); // only test ods here, xls and xlsx in subsequent_filters-test
63 void testContentODS();
64 void testContentXLS();
65 void testContentXLSX();
66 void testContentLotus123();
68 //goes recursively through all files in this dir and tries to open them
69 void testDir(osl::Directory
& rDir
, sal_Int32 nType
);
70 //test Bug Files and search for files that crash LibO
72 void testBugFilesXLS();
73 void testBugFilesXLSX();
75 void testLegacyCellAnchoredRotatedShape();
77 CPPUNIT_TEST_SUITE(ScFiltersTest
);
78 CPPUNIT_TEST(testCVEs
);
79 CPPUNIT_TEST(testRangeNameODS
);
80 CPPUNIT_TEST(testContentODS
);
81 CPPUNIT_TEST(testContentXLS
);
82 CPPUNIT_TEST(testContentXLSX
);
83 CPPUNIT_TEST(testContentLotus123
);
84 CPPUNIT_TEST(testLegacyCellAnchoredRotatedShape
);
87 CPPUNIT_TEST(testBugFiles
);
88 CPPUNIT_TEST(testBugFilesXLS
);
89 CPPUNIT_TEST(testBugFilesXLSX
);
91 CPPUNIT_TEST_SUITE_END();
94 uno::Reference
<uno::XInterface
> m_xCalcComponent
;
97 bool ScFiltersTest::load(const OUString
&rFilter
, const OUString
&rURL
,
98 const OUString
&rUserData
, unsigned int nFilterFlags
,
99 unsigned int nClipboardID
, unsigned int nFilterVersion
)
101 ScDocShellRef xDocShRef
= ScBootstrapFixture::load(rURL
, rFilter
, rUserData
,
102 OUString(), nFilterFlags
, nClipboardID
, nFilterVersion
);
103 bool bLoaded
= xDocShRef
.Is();
104 //reference counting of ScDocShellRef is very confused.
106 xDocShRef
->DoClose();
110 void ScFiltersTest::testCVEs()
112 #ifndef DISABLE_CVE_TESTS
113 testDir(OUString("Quattro Pro 6.0"),
114 getURLFromSrc("/sc/qa/unit/data/qpro/"), OUString());
116 //warning, the current "sylk filter" in sc (docsh.cxx) automatically
117 //chains on failure on trying as csv, rtf, etc. so "success" may
118 //not indicate that it imported as .slk.
119 testDir(OUString("SYLK"),
120 getURLFromSrc("/sc/qa/unit/data/slk/"), OUString());
122 testDir(OUString("MS Excel 97"),
123 getURLFromSrc("/sc/qa/unit/data/xls/"), OUString());
129 void ScFiltersTest::testDir(osl::Directory
& rDir
, sal_uInt32 nType
)
131 OUString
aFilterName(aFileFormats
[nType
].pFilterName
, strlen(aFileFormats
[nType
].pFilterName
), RTL_TEXTENCODING_UTF8
) ;
132 OUString
aFilterType(aFileFormats
[nType
].pTypeName
, strlen(aFileFormats
[nType
].pTypeName
), RTL_TEXTENCODING_UTF8
);
134 osl::DirectoryItem aItem
;
135 osl::FileStatus
aFileStatus(osl_FileStatus_Mask_FileURL
|osl_FileStatus_Mask_Type
);
136 while (rDir
.getNextItem(aItem
) == osl::FileBase::E_None
)
138 aItem
.getFileStatus(aFileStatus
);
139 OUString sURL
= aFileStatus
.getFileURL();
140 std::cout
<< "File: " << OUStringToOString(sURL
, RTL_TEXTENCODING_UTF8
).getStr() << std::endl
;
141 //OStringBuffer aMessage("Failed loading: ");
142 //aMessage.append(OUStringToOString(sURL, RTL_TEXTENCODING_UTF8));
144 unsigned int nFormatType
= aFileFormats
[nType
].nFormatType
;
145 unsigned int nClipboardId
= nFormatType
? SFX_FILTER_IMPORT
| SFX_FILTER_USESOPTIONS
: 0;
146 ScDocShellRef xDocSh
= load(sURL
, aFilterName
, OUString(),
147 aFilterType
, nFormatType
, nClipboardId
);
148 // use this only if you're sure that all files can be loaded
149 // pay attention to lock files
150 //CPPUNIT_ASSERT_MESSAGE(aMessage.getStr(), xDocSh.Is());
156 void ScFiltersTest::testBugFiles()
158 OUString aDirName
= getURLFromSrc("/sc/qa/unit/data/bugODS/");
159 osl::Directory
aDir(aDirName
);
161 CPPUNIT_ASSERT(osl::FileBase::E_None
== aDir
.open());
165 void ScFiltersTest::testBugFilesXLS()
167 OUString aDirName
= getURLFromSrc("/sc/qa/unit/data/bugXLS/");
168 osl::Directory
aDir(aDirName
);
170 CPPUNIT_ASSERT(osl::FileBase::E_None
== aDir
.open());
174 void ScFiltersTest::testBugFilesXLSX()
176 OUString aDirName
= getURLFromSrc("/sc/qa/unit/data/bugXLSX/");
177 osl::Directory
aDir(aDirName
);
179 CPPUNIT_ASSERT(osl::FileBase::E_None
== aDir
.open());
187 void testRangeNameImpl(ScDocument
* pDoc
)
189 //check one range data per sheet and one global more detailed
190 //add some more checks here
191 ScRangeData
* pRangeData
= pDoc
->GetRangeName()->findByUpperName(OUString("GLOBAL1"));
192 CPPUNIT_ASSERT_MESSAGE("range name Global1 not found", pRangeData
);
194 pDoc
->GetValue(1,0,0,aValue
);
195 CPPUNIT_ASSERT_MESSAGE("range name Global1 should reference Sheet1.A1", aValue
== 1);
196 pRangeData
= pDoc
->GetRangeName(0)->findByUpperName(OUString("LOCAL1"));
197 CPPUNIT_ASSERT_MESSAGE("range name Sheet1.Local1 not found", pRangeData
);
198 pDoc
->GetValue(1,2,0,aValue
);
199 CPPUNIT_ASSERT_MESSAGE("range name Sheet1.Local1 should reference Sheet1.A3", aValue
== 3);
200 pRangeData
= pDoc
->GetRangeName(1)->findByUpperName(OUString("LOCAL2"));
201 CPPUNIT_ASSERT_MESSAGE("range name Sheet2.Local2 not found", pRangeData
);
202 //check for correct results for the remaining formulas
203 pDoc
->GetValue(1,1,0, aValue
);
204 CPPUNIT_ASSERT_MESSAGE("=global2 should be 2", aValue
== 2);
205 pDoc
->GetValue(1,3,0, aValue
);
206 CPPUNIT_ASSERT_MESSAGE("=local2 should be 4", aValue
== 4);
207 pDoc
->GetValue(2,0,0, aValue
);
208 CPPUNIT_ASSERT_MESSAGE("=SUM(global3) should be 10", aValue
== 10);
213 void ScFiltersTest::testRangeNameODS()
215 ScDocShellRef xDocSh
= loadDoc("named-ranges-global.", ODS
);
217 CPPUNIT_ASSERT_MESSAGE("Failed to load named-ranges-globals.*", xDocSh
.Is());
219 xDocSh
->DoHardRecalc(true);
221 ScDocument
* pDoc
= xDocSh
->GetDocument();
222 testRangeNameImpl(pDoc
);
224 OUString
aSheet2CSV("rangeExp_Sheet2.");
226 createCSVPath( aSheet2CSV
, aCSVPath
);
227 testFile( aCSVPath
, pDoc
, 1);
233 void testContentImpl(ScDocument
* pDoc
, sal_Int32 nFormat
) //same code for ods, xls, xlsx
237 pDoc
->GetValue(0,0,0,fValue
);
238 CPPUNIT_ASSERT_MESSAGE("value not imported correctly", fValue
== 1);
239 pDoc
->GetValue(0,1,0,fValue
);
240 CPPUNIT_ASSERT_MESSAGE("value not imported correctly", fValue
== 2);
241 OUString aString
= pDoc
->GetString(1, 0, 0);
243 //check string import
244 CPPUNIT_ASSERT_MESSAGE("string imported not correctly", aString
== OUString("String1"));
245 aString
= pDoc
->GetString(1, 1, 0);
246 CPPUNIT_ASSERT_MESSAGE("string not imported correctly", aString
== OUString("String2"));
248 //check basic formula import
249 pDoc
->GetValue(2,0,0,fValue
);
250 CPPUNIT_ASSERT_MESSAGE("=2*3", fValue
== 6);
251 pDoc
->GetValue(2,1,0,fValue
);
252 CPPUNIT_ASSERT_MESSAGE("=2+3", fValue
== 5);
253 pDoc
->GetValue(2,2,0,fValue
);
254 CPPUNIT_ASSERT_MESSAGE("=2-3", fValue
== -1);
255 pDoc
->GetValue(2,3,0,fValue
);
256 CPPUNIT_ASSERT_MESSAGE("=C1+C2", fValue
== 11);
258 //check merged cells import
259 if(nFormat
!= LOTUS123
)
263 pDoc
->ExtendMerge(4, 1, nCol
, nRow
, 0, false);
264 CPPUNIT_ASSERT_MESSAGE("merged cells are not imported", nCol
== 5 && nRow
== 2);
267 ScAddress
aAddress(7, 2, 0);
268 ScPostIt
* pNote
= pDoc
->GetNotes(aAddress
.Tab())->findByAddress(aAddress
);
269 CPPUNIT_ASSERT_MESSAGE("note not imported", pNote
);
270 CPPUNIT_ASSERT_EQUAL_MESSAGE("note text not imported correctly", pNote
->GetText(), OUString("Test"));
273 //add additional checks here
278 void ScFiltersTest::testContentODS()
280 ScDocShellRef xDocSh
= loadDoc("universal-content.", ODS
);
281 xDocSh
->DoHardRecalc(true);
283 ScDocument
* pDoc
= xDocSh
->GetDocument();
284 testContentImpl(pDoc
, ODS
);
288 void ScFiltersTest::testContentXLS()
290 ScDocShellRef xDocSh
= loadDoc("universal-content.", XLS
);
291 xDocSh
->DoHardRecalc(true);
293 ScDocument
* pDoc
= xDocSh
->GetDocument();
294 testContentImpl(pDoc
, XLS
);
298 void ScFiltersTest::testContentXLSX()
300 ScDocShellRef xDocSh
= loadDoc("universal-content.", XLSX
);
301 xDocSh
->DoHardRecalc(true);
303 ScDocument
* pDoc
= xDocSh
->GetDocument();
304 testContentImpl(pDoc
, XLSX
);
308 void ScFiltersTest::testContentLotus123()
310 ScDocShellRef xDocSh
= loadDoc("universal-content.", LOTUS123
);
311 xDocSh
->DoHardRecalc(true);
313 ScDocument
* pDoc
= xDocSh
->GetDocument();
314 CPPUNIT_ASSERT(pDoc
);
315 testContentImpl(pDoc
, LOTUS123
);
318 void impl_testLegacyCellAnchoredRotatedShape( ScDocument
* pDoc
, Rectangle
& aRect
, ScDrawObjData
& aAnchor
, long TOLERANCE
= 30 /* 30 hmm */ )
320 ScDrawLayer
* pDrawLayer
= pDoc
->GetDrawLayer();
321 CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer
);
322 SdrPage
* pPage
= pDrawLayer
->GetPage(0);
323 CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage
);
324 CPPUNIT_ASSERT_EQUAL( sal_uIntPtr(1), pPage
->GetObjCount() );
326 SdrObject
* pObj
= pPage
->GetObj(0);
327 const Rectangle
& aSnap
= pObj
->GetSnapRect();
328 printf("expected height %ld actual %ld\n", aRect
.GetHeight(), aSnap
.GetHeight() );
329 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect
.GetHeight(), aSnap
.GetHeight(), TOLERANCE
) );
330 printf("expected width %ld actual %ld\n", aRect
.GetWidth(), aSnap
.GetWidth() );
331 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect
.GetWidth(), aSnap
.GetWidth(), TOLERANCE
) );
332 printf("expected left %ld actual %ld\n", aRect
.Left(), aSnap
.Left() );
333 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect
.Left(), aSnap
.Left(), TOLERANCE
) );
334 printf("expected right %ld actual %ld\n", aRect
.Top(), aSnap
.Top() );
335 CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRect
.Top(), aSnap
.Top(), TOLERANCE
) );
338 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( pObj
);
339 printf("expected startrow %" SAL_PRIdINT32
" actual %" SAL_PRIdINT32
"\n", aAnchor
.maStart
.Row(), pData
->maStart
.Row() );
340 CPPUNIT_ASSERT_EQUAL( aAnchor
.maStart
.Row(), pData
->maStart
.Row() );
341 printf("expected startcol %d actual %d\n", aAnchor
.maStart
.Col(), pData
->maStart
.Col() );
342 CPPUNIT_ASSERT_EQUAL( aAnchor
.maStart
.Col(), pData
->maStart
.Col() );
343 printf("expected endrow %" SAL_PRIdINT32
" actual %" SAL_PRIdINT32
"\n", aAnchor
.maEnd
.Row(), pData
->maEnd
.Row() );
344 CPPUNIT_ASSERT_EQUAL( aAnchor
.maEnd
.Row(), pData
->maEnd
.Row() );
345 printf("expected endcol %d actual %d\n", aAnchor
.maEnd
.Col(), pData
->maEnd
.Col() );
346 CPPUNIT_ASSERT_EQUAL( aAnchor
.maEnd
.Col(), pData
->maEnd
.Col() );
349 void ScFiltersTest::testLegacyCellAnchoredRotatedShape()
352 // This example doc contains cell anchored shape that is rotated, the
353 // rotated shape is in fact cliped by the sheet boundries ( and thus
354 // is a good edge case test to see if we import it still correctly )
355 ScDocShellRef xDocSh
= loadDoc("legacycellanchoredrotatedclippedshape.", ODS
);
357 ScDocument
* pDoc
= xDocSh
->GetDocument();
358 CPPUNIT_ASSERT(pDoc
);
359 // ensure the imported legacy rotated shape is in the expected position
360 Rectangle
aRect( 6000, -2000, 8000, 4000 );
361 // ensure the imported ( and converted ) anchor ( note we internally now store the anchor in
362 // terms of the rotated shape ) is more or less contains the correct info
363 ScDrawObjData aAnchor
;
364 aAnchor
.maStart
.SetRow( 0 );
365 aAnchor
.maStart
.SetCol( 5 );
366 aAnchor
.maEnd
.SetRow( 3 );
367 aAnchor
.maEnd
.SetCol( 7 );
368 impl_testLegacyCellAnchoredRotatedShape( pDoc
, aRect
, aAnchor
);
369 // test save and reload
370 // for some reason having this test in subsequent_export-test.cxx causes
371 // a core dump in editeng ( so moved to here )
372 xDocSh
= saveAndReload( &(*xDocSh
), ODS
);
373 pDoc
= xDocSh
->GetDocument();
374 CPPUNIT_ASSERT(pDoc
);
375 impl_testLegacyCellAnchoredRotatedShape( pDoc
, aRect
, aAnchor
);
378 // This example doc contains cell anchored shape that is rotated, the
379 // rotated shape is in fact clipped by the sheet boundries, additionally
380 // the shape is completely hidden because the rows the shape occupies
382 ScDocShellRef xDocSh
= loadDoc("legacycellanchoredrotatedhiddenshape.", ODS
, true);
383 ScDocument
* pDoc
= xDocSh
->GetDocument();
384 CPPUNIT_ASSERT(pDoc
);
385 // ensure the imported legacy rotated shape is in the expected position
386 // when a shape is fully hidden reloading seems to result is in some errors, usually
387 // ( same but different error happens pre-patch ) - we should do better here, I regard it
388 // as a pre-existing bug though ( #FIXME )
389 //Rectangle aRect( 6000, -2000, 8000, 4000 ); // proper dimensions
390 Rectangle
aRect( 6000, -2000, 7430, 4000 );
391 // ensure the imported ( and converted ) anchor ( note we internally now store the anchor in
392 // terms of the rotated shape ) is more or less contains the correct info
393 ScDrawObjData aAnchor
;
394 aAnchor
.maStart
.SetRow( 0 );
395 aAnchor
.maStart
.SetCol( 5 );
396 aAnchor
.maEnd
.SetRow( 3 );
397 aAnchor
.maEnd
.SetCol( 7 );
398 pDoc
->ShowRows(0, 9, 0, true); // show relavent rows
399 pDoc
->SetDrawPageSize(0); // trigger recalcpos
401 // apply hefty ( 1 mm ) tolerence here, as some opensuse tinderbox
403 impl_testLegacyCellAnchoredRotatedShape( pDoc
, aRect
, aAnchor
, 100 );
407 // This example doc contains cell anchored shape that is rotated
408 ScDocShellRef xDocSh
= loadDoc("legacycellanchoredrotatedshape.", ODS
);
410 ScDocument
* pDoc
= xDocSh
->GetDocument();
411 CPPUNIT_ASSERT(pDoc
);
412 // ensure the imported legacy rotated shape is in the expected position
413 Rectangle
aRect( 6000, 3000, 8000, 9000 );
414 // ensure the imported ( and converted ) anchor ( note we internally now store the anchor in
415 // terms of the rotated shape ) is more or less contains the correct info
417 ScDrawObjData aAnchor
;
418 aAnchor
.maStart
.SetRow( 3 );
419 aAnchor
.maStart
.SetCol( 6 );
420 aAnchor
.maEnd
.SetRow( 9 );
421 aAnchor
.maEnd
.SetCol( 7 );
423 impl_testLegacyCellAnchoredRotatedShape( pDoc
, aRect
, aAnchor
);
424 // test save and reload
425 xDocSh
= saveAndReload( &(*xDocSh
), ODS
);
426 pDoc
= xDocSh
->GetDocument();
427 CPPUNIT_ASSERT(pDoc
);
428 impl_testLegacyCellAnchoredRotatedShape( pDoc
, aRect
, aAnchor
);
432 ScFiltersTest::ScFiltersTest()
433 : ScBootstrapFixture( "/sc/qa/unit/data" )
437 void ScFiltersTest::setUp()
439 test::BootstrapFixture::setUp();
441 // This is a bit of a fudge, we do this to ensure that ScGlobals::ensure,
442 // which is a private symbol to us, gets called
444 getMultiServiceFactory()->createInstance(OUString("com.sun.star.comp.Calc.SpreadsheetDocument"));
445 CPPUNIT_ASSERT_MESSAGE("no calc component!", m_xCalcComponent
.is());
448 void ScFiltersTest::tearDown()
450 uno::Reference
< lang::XComponent
>( m_xCalcComponent
, UNO_QUERY_THROW
)->dispose();
451 test::BootstrapFixture::tearDown();
454 CPPUNIT_TEST_SUITE_REGISTRATION(ScFiltersTest
);
456 CPPUNIT_PLUGIN_IMPLEMENT();
458 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */