Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * Version: MPL 1.1 / GPLv3+ / LGPLv3+
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License or as specified alternatively below. You may obtain a copy of
8 : * the License at http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * Major Contributor(s):
16 : * Copyright (C) 2010 Red Hat, Inc., Caolán McNamara <caolanm@redhat.com>
17 : * (initial developer)
18 : * Copyright (C) 2011 Markus Mohrhard <markus.mohrhard@googlemail.com>
19 : *
20 : * All Rights Reserved.
21 : *
22 : * For minor contributions see the git repository.
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 3 or later (the "GPLv3+"), or
26 : * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
27 : * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
28 : * instead of those above.
29 : */
30 :
31 : #include <sal/config.h>
32 : #include <unotest/filters-test.hxx>
33 : #include <test/bootstrapfixture.hxx>
34 : #include <rtl/strbuf.hxx>
35 : #include <osl/file.hxx>
36 :
37 : #include <sfx2/app.hxx>
38 : #include <sfx2/docfilt.hxx>
39 : #include <sfx2/docfile.hxx>
40 : #include <sfx2/sfxmodelfactory.hxx>
41 : #include <svl/stritem.hxx>
42 :
43 : #define CALC_DEBUG_OUTPUT 0
44 : #define TEST_BUG_FILES 0
45 :
46 : #include "helper/qahelper.hxx"
47 :
48 : #include "docsh.hxx"
49 : #include "postit.hxx"
50 : #include "patattr.hxx"
51 : #include "scitems.hxx"
52 : #include "document.hxx"
53 : #include "cellform.hxx"
54 :
55 : #define ODS_FORMAT_TYPE 50331943
56 : #define XLS_FORMAT_TYPE 318767171
57 : #define XLSX_FORMAT_TYPE 268959811
58 : #define LOTUS123_FORMAT_TYPE 268435649
59 :
60 : #define ODS 0
61 : #define XLS 1
62 : #define XLSX 2
63 : #define LOTUS123 3
64 :
65 : using namespace ::com::sun::star;
66 : using namespace ::com::sun::star::uno;
67 :
68 : namespace {
69 :
70 : struct FileFormat {
71 : const char* pName; const char* pFilterName; const char* pTypeName; unsigned int nFormatType;
72 : };
73 :
74 : FileFormat aFileFormats[] = {
75 : { "ods" , "calc8", "", ODS_FORMAT_TYPE },
76 : { "xls" , "MS Excel 97", "calc_MS_EXCEL_97", XLS_FORMAT_TYPE },
77 : { "xlsx", "Calc MS Excel 2007 XML" , "MS Excel 2007 XML", XLSX_FORMAT_TYPE },
78 : { "123" , "Lotus", "calc_Lotus", LOTUS123_FORMAT_TYPE }
79 : };
80 :
81 : }
82 :
83 : /* Implementation of Filters test */
84 :
85 12 : class ScFiltersTest
86 : : public test::FiltersTest
87 : , public test::BootstrapFixture
88 : {
89 : public:
90 : ScFiltersTest();
91 :
92 : virtual bool load( const rtl::OUString &rFilter, const rtl::OUString &rURL,
93 : const rtl::OUString &rUserData, unsigned int nFilterFlags,
94 : unsigned int nClipboardID, unsigned int nFilterVersion);
95 :
96 : ScDocShellRef load(const rtl::OUString &rFilter, const rtl::OUString &rURL,
97 : const rtl::OUString &rUserData, const rtl::OUString& rTypeName,
98 : unsigned int nFilterFlags, unsigned int nClipboardID, unsigned int nFilterVersion);
99 :
100 : void createFileURL(const rtl::OUString& aFileBase, const rtl::OUString& aFileExtension, rtl::OUString& rFilePath);
101 : void createCSVPath(const rtl::OUString& aFileBase, rtl::OUString& rFilePath);
102 :
103 : virtual void setUp();
104 : virtual void tearDown();
105 :
106 : /**
107 : * Ensure CVEs remain unbroken
108 : */
109 : void testCVEs();
110 :
111 : //ods, xls, xlsx filter tests
112 : void testRangeNameODS(); // only test ods here, xls and xlsx in subsequent_filters-test
113 : void testContentODS();
114 : void testContentXLS();
115 : void testContentXLSX();
116 : void testContentLotus123();
117 :
118 : #if TEST_BUG_FILES
119 : //goes recursively through all files in this dir and tries to open them
120 : void testDir(osl::Directory& rDir, sal_Int32 nType);
121 : //test Bug Files and search for files that crash LibO
122 : void testBugFiles();
123 : void testBugFilesXLS();
124 : void testBugFilesXLSX();
125 : #endif
126 :
127 2 : CPPUNIT_TEST_SUITE(ScFiltersTest);
128 1 : CPPUNIT_TEST(testCVEs);
129 1 : CPPUNIT_TEST(testRangeNameODS);
130 1 : CPPUNIT_TEST(testContentODS);
131 1 : CPPUNIT_TEST(testContentXLS);
132 1 : CPPUNIT_TEST(testContentXLSX);
133 1 : CPPUNIT_TEST(testContentLotus123);
134 :
135 : #if TEST_BUG_FILES
136 : CPPUNIT_TEST(testBugFiles);
137 : CPPUNIT_TEST(testBugFilesXLS);
138 : CPPUNIT_TEST(testBugFilesXLSX);
139 : #endif
140 2 : CPPUNIT_TEST_SUITE_END();
141 :
142 : private:
143 : ScDocShellRef loadDoc(const rtl::OUString& rName, sal_Int32 nFormat);
144 : uno::Reference<uno::XInterface> m_xCalcComponent;
145 : ::rtl::OUString m_aBaseString;
146 : };
147 :
148 26 : ScDocShellRef ScFiltersTest::load(const rtl::OUString &rFilter, const rtl::OUString &rURL,
149 : const rtl::OUString &rUserData, const rtl::OUString& rTypeName,
150 : unsigned int nFilterFlags, unsigned int nClipboardID, unsigned int nFilterVersion)
151 : {
152 : SfxFilter* pFilter = new SfxFilter(
153 : rFilter,
154 : rtl::OUString(), nFilterFlags, nClipboardID, rTypeName, 0, rtl::OUString(),
155 26 : rUserData, rtl::OUString("private:factory/scalc*") );
156 26 : pFilter->SetVersion(nFilterVersion);
157 :
158 26 : ScDocShellRef xDocShRef = new ScDocShell;
159 26 : xDocShRef->GetDocument()->EnableUserInteraction(false);
160 26 : SfxMedium* pSrcMed = new SfxMedium(rURL, STREAM_STD_READ);
161 26 : pSrcMed->UseInteractionHandler(false);
162 26 : pSrcMed->SetFilter(pFilter);
163 26 : if (!xDocShRef->DoLoad(pSrcMed))
164 : {
165 3 : xDocShRef->DoClose();
166 : // load failed.
167 3 : xDocShRef.Clear();
168 : }
169 :
170 26 : return xDocShRef;
171 : }
172 :
173 21 : bool ScFiltersTest::load(const rtl::OUString &rFilter, const rtl::OUString &rURL,
174 : const rtl::OUString &rUserData, unsigned int nFilterFlags,
175 : unsigned int nClipboardID, unsigned int nFilterVersion)
176 : {
177 : ScDocShellRef xDocShRef = load(rFilter, rURL, rUserData,
178 21 : rtl::OUString(), nFilterFlags, nClipboardID, nFilterVersion);
179 21 : bool bLoaded = xDocShRef.Is();
180 : //reference counting of ScDocShellRef is very confused.
181 21 : if (bLoaded)
182 18 : xDocShRef->DoClose();
183 21 : return bLoaded;
184 : }
185 :
186 5 : void ScFiltersTest::createFileURL(const rtl::OUString& aFileBase, const rtl::OUString& aFileExtension, rtl::OUString& rFilePath)
187 : {
188 5 : rtl::OUString aSep(RTL_CONSTASCII_USTRINGPARAM("/"));
189 5 : rtl::OUStringBuffer aBuffer( getSrcRootURL() );
190 5 : aBuffer.append(m_aBaseString).append(aSep).append(aFileExtension);
191 5 : aBuffer.append(aSep).append(aFileBase).append(aFileExtension);
192 5 : rFilePath = aBuffer.makeStringAndClear();
193 5 : }
194 :
195 1 : void ScFiltersTest::createCSVPath(const rtl::OUString& aFileBase, rtl::OUString& rCSVPath)
196 : {
197 1 : rtl::OUStringBuffer aBuffer(getSrcRootPath());
198 1 : aBuffer.append(m_aBaseString).append(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/contentCSV/")));
199 1 : aBuffer.append(aFileBase).append(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("csv")));
200 1 : rCSVPath = aBuffer.makeStringAndClear();
201 1 : }
202 :
203 1 : void ScFiltersTest::testCVEs()
204 : {
205 : #ifndef DISABLE_CVE_TESTS
206 : testDir(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Quattro Pro 6.0")),
207 1 : getURLFromSrc("/sc/qa/unit/data/qpro/"), rtl::OUString());
208 :
209 : //warning, the current "sylk filter" in sc (docsh.cxx) automatically
210 : //chains on failure on trying as csv, rtf, etc. so "success" may
211 : //not indicate that it imported as .slk.
212 : testDir(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("SYLK")),
213 1 : getURLFromSrc("/sc/qa/unit/data/slk/"), rtl::OUString());
214 :
215 : testDir(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("MS Excel 97")),
216 1 : getURLFromSrc("/sc/qa/unit/data/xls/"), rtl::OUString());
217 : #endif
218 1 : }
219 :
220 : #if TEST_BUG_FILES
221 :
222 : void ScFiltersTest::testDir(osl::Directory& rDir, sal_uInt32 nType)
223 : {
224 : rtl::OUString aFilterName(aFileFormats[nType].pFilterName, strlen(aFileFormats[nType].pFilterName), RTL_TEXTENCODING_UTF8) ;
225 : rtl::OUString aFilterType(aFileFormats[nType].pTypeName, strlen(aFileFormats[nType].pTypeName), RTL_TEXTENCODING_UTF8);
226 :
227 : osl::DirectoryItem aItem;
228 : osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL|osl_FileStatus_Mask_Type);
229 : while (rDir.getNextItem(aItem) == osl::FileBase::E_None)
230 : {
231 : aItem.getFileStatus(aFileStatus);
232 : rtl::OUString sURL = aFileStatus.getFileURL();
233 : std::cout << "File: " << rtl::OUStringToOString(sURL, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
234 : //rtl::OStringBuffer aMessage("Failed loading: ");
235 : //aMessage.append(rtl::OUStringToOString(sURL, RTL_TEXTENCODING_UTF8));
236 :
237 : unsigned int nFormatType = aFileFormats[nType].nFormatType;
238 : unsigned int nClipboardId = nFormatType ? SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS : 0;
239 : ScDocShellRef xDocSh = load(aFilterName, sURL, rtl::OUString(),
240 : aFilterType, nFormatType, nClipboardId, SOFFICE_FILEFORMAT_CURRENT);
241 : // use this only if you're sure that all files can be loaded
242 : // pay attention to lock files
243 : //CPPUNIT_ASSERT_MESSAGE(aMessage.getStr(), xDocSh.Is());
244 : if (xDocSh.Is())
245 : xDocSh->DoClose();
246 : }
247 : }
248 :
249 : void ScFiltersTest::testBugFiles()
250 : {
251 : rtl::OUString aDirName = getURLFromSrc("/sc/qa/unit/data/bugODS/");
252 : osl::Directory aDir(aDirName);
253 :
254 : CPPUNIT_ASSERT(osl::FileBase::E_None == aDir.open());
255 : testDir(aDir, 0);
256 : }
257 :
258 : void ScFiltersTest::testBugFilesXLS()
259 : {
260 : rtl::OUString aDirName = getURLFromSrc("/sc/qa/unit/data/bugXLS/");
261 : osl::Directory aDir(aDirName);
262 :
263 : CPPUNIT_ASSERT(osl::FileBase::E_None == aDir.open());
264 : testDir(aDir, 1);
265 : }
266 :
267 : void ScFiltersTest::testBugFilesXLSX()
268 : {
269 : rtl::OUString aDirName = getURLFromSrc("/sc/qa/unit/data/bugXLSX/");
270 : osl::Directory aDir(aDirName);
271 :
272 : CPPUNIT_ASSERT(osl::FileBase::E_None == aDir.open());
273 : testDir(aDir, 2);
274 : }
275 :
276 : #endif
277 :
278 : namespace {
279 :
280 1 : void testRangeNameImpl(ScDocument* pDoc)
281 : {
282 : //check one range data per sheet and one global more detailed
283 : //add some more checks here
284 1 : ScRangeData* pRangeData = pDoc->GetRangeName()->findByUpperName(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("GLOBAL1")));
285 1 : CPPUNIT_ASSERT_MESSAGE("range name Global1 not found", pRangeData);
286 : double aValue;
287 1 : pDoc->GetValue(1,0,0,aValue);
288 1 : CPPUNIT_ASSERT_MESSAGE("range name Global1 should reference Sheet1.A1", aValue == 1);
289 1 : pRangeData = pDoc->GetRangeName(0)->findByUpperName(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("LOCAL1")));
290 1 : CPPUNIT_ASSERT_MESSAGE("range name Sheet1.Local1 not found", pRangeData);
291 1 : pDoc->GetValue(1,2,0,aValue);
292 1 : CPPUNIT_ASSERT_MESSAGE("range name Sheet1.Local1 should reference Sheet1.A3", aValue == 3);
293 1 : pRangeData = pDoc->GetRangeName(1)->findByUpperName(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("LOCAL2")));
294 1 : CPPUNIT_ASSERT_MESSAGE("range name Sheet2.Local2 not found", pRangeData);
295 : //check for correct results for the remaining formulas
296 1 : pDoc->GetValue(1,1,0, aValue);
297 1 : CPPUNIT_ASSERT_MESSAGE("=global2 should be 2", aValue == 2);
298 1 : pDoc->GetValue(1,3,0, aValue);
299 1 : CPPUNIT_ASSERT_MESSAGE("=local2 should be 4", aValue == 4);
300 1 : pDoc->GetValue(2,0,0, aValue);
301 1 : CPPUNIT_ASSERT_MESSAGE("=SUM(global3) should be 10", aValue == 10);
302 1 : }
303 :
304 : }
305 :
306 5 : ScDocShellRef ScFiltersTest::loadDoc(const rtl::OUString& rName, sal_Int32 nFormat)
307 : {
308 5 : rtl::OUString aFileExtension(aFileFormats[nFormat].pName, strlen(aFileFormats[nFormat].pName), RTL_TEXTENCODING_UTF8 );
309 5 : rtl::OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ;
310 5 : rtl::OUString aFileName;
311 5 : createFileURL( rName, aFileExtension, aFileName );
312 5 : rtl::OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8);
313 5 : unsigned int nFormatType = aFileFormats[nFormat].nFormatType;
314 5 : unsigned int nClipboardId = nFormatType ? SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS : 0;
315 : ScDocShellRef xDocSh = load(aFilterName, aFileName, rtl::OUString(), aFilterType,
316 5 : nFormatType, nClipboardId, SOFFICE_FILEFORMAT_CURRENT);
317 5 : CPPUNIT_ASSERT(xDocSh.Is());
318 5 : return xDocSh;
319 : }
320 :
321 1 : void ScFiltersTest::testRangeNameODS()
322 : {
323 1 : const rtl::OUString aFileNameBase(RTL_CONSTASCII_USTRINGPARAM("named-ranges-global."));
324 1 : ScDocShellRef xDocSh = loadDoc(aFileNameBase, 0);
325 :
326 1 : CPPUNIT_ASSERT_MESSAGE("Failed to load named-ranges-globals.*", xDocSh.Is());
327 :
328 1 : xDocSh->DoHardRecalc(true);
329 :
330 1 : ScDocument* pDoc = xDocSh->GetDocument();
331 1 : testRangeNameImpl(pDoc);
332 :
333 1 : rtl::OUString aSheet2CSV(RTL_CONSTASCII_USTRINGPARAM("rangeExp_Sheet2."));
334 1 : rtl::OUString aCSVPath;
335 1 : createCSVPath( aSheet2CSV, aCSVPath );
336 1 : testFile( aCSVPath, pDoc, 1);
337 1 : xDocSh->DoClose();
338 1 : }
339 :
340 : namespace {
341 :
342 4 : void testContentImpl(ScDocument* pDoc, sal_Int32 nFormat ) //same code for ods, xls, xlsx
343 : {
344 : double fValue;
345 : //check value import
346 4 : pDoc->GetValue(0,0,0,fValue);
347 4 : CPPUNIT_ASSERT_MESSAGE("value not imported correctly", fValue == 1);
348 4 : pDoc->GetValue(0,1,0,fValue);
349 4 : CPPUNIT_ASSERT_MESSAGE("value not imported correctly", fValue == 2);
350 4 : rtl::OUString aString;
351 4 : pDoc->GetString(1,0,0,aString);
352 :
353 : //check string import
354 4 : CPPUNIT_ASSERT_MESSAGE("string imported not correctly", aString == rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("String1")));
355 4 : pDoc->GetString(1,1,0,aString);
356 4 : CPPUNIT_ASSERT_MESSAGE("string not imported correctly", aString == rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("String2")));
357 :
358 : //check basic formula import
359 4 : pDoc->GetValue(2,0,0,fValue);
360 4 : CPPUNIT_ASSERT_MESSAGE("=2*3", fValue == 6);
361 4 : pDoc->GetValue(2,1,0,fValue);
362 4 : CPPUNIT_ASSERT_MESSAGE("=2+3", fValue == 5);
363 4 : pDoc->GetValue(2,2,0,fValue);
364 4 : CPPUNIT_ASSERT_MESSAGE("=2-3", fValue == -1);
365 4 : pDoc->GetValue(2,3,0,fValue);
366 4 : CPPUNIT_ASSERT_MESSAGE("=C1+C2", fValue == 11);
367 :
368 : //check merged cells import
369 4 : if(nFormat != LOTUS123)
370 : {
371 3 : SCCOL nCol = 4;
372 3 : SCROW nRow = 1;
373 3 : pDoc->ExtendMerge(4, 1, nCol, nRow, 0, false);
374 3 : CPPUNIT_ASSERT_MESSAGE("merged cells are not imported", nCol == 5 && nRow == 2);
375 :
376 : //check notes import
377 3 : ScAddress aAddress(7, 2, 0);
378 3 : ScPostIt* pNote = pDoc->GetNotes(aAddress.Tab())->findByAddress(aAddress);
379 3 : CPPUNIT_ASSERT_MESSAGE("note not imported", pNote);
380 3 : CPPUNIT_ASSERT_EQUAL_MESSAGE("note text not imported correctly", pNote->GetText(), rtl::OUString("Test"));
381 4 : }
382 :
383 : //add additional checks here
384 4 : }
385 :
386 : }
387 :
388 1 : void ScFiltersTest::testContentODS()
389 : {
390 1 : const rtl::OUString aFileNameBase(RTL_CONSTASCII_USTRINGPARAM("universal-content."));
391 1 : ScDocShellRef xDocSh = loadDoc(aFileNameBase, ODS);
392 1 : xDocSh->DoHardRecalc(true);
393 :
394 1 : ScDocument* pDoc = xDocSh->GetDocument();
395 1 : testContentImpl(pDoc, ODS);
396 1 : xDocSh->DoClose();
397 1 : }
398 :
399 1 : void ScFiltersTest::testContentXLS()
400 : {
401 1 : const rtl::OUString aFileNameBase(RTL_CONSTASCII_USTRINGPARAM("universal-content."));
402 1 : ScDocShellRef xDocSh = loadDoc(aFileNameBase, XLS);
403 1 : xDocSh->DoHardRecalc(true);
404 :
405 1 : ScDocument* pDoc = xDocSh->GetDocument();
406 1 : testContentImpl(pDoc, XLS);
407 1 : xDocSh->DoClose();
408 1 : }
409 :
410 1 : void ScFiltersTest::testContentXLSX()
411 : {
412 1 : const rtl::OUString aFileNameBase(RTL_CONSTASCII_USTRINGPARAM("universal-content."));
413 1 : ScDocShellRef xDocSh = loadDoc(aFileNameBase, XLSX);
414 1 : xDocSh->DoHardRecalc(true);
415 :
416 1 : ScDocument* pDoc = xDocSh->GetDocument();
417 1 : testContentImpl(pDoc, XLSX);
418 1 : xDocSh->DoClose();
419 1 : }
420 :
421 1 : void ScFiltersTest::testContentLotus123()
422 : {
423 1 : const rtl::OUString aFileNameBase(RTL_CONSTASCII_USTRINGPARAM("universal-content."));
424 1 : ScDocShellRef xDocSh = loadDoc(aFileNameBase, LOTUS123);
425 1 : xDocSh->DoHardRecalc(true);
426 :
427 1 : ScDocument* pDoc = xDocSh->GetDocument();
428 1 : CPPUNIT_ASSERT(pDoc);
429 1 : testContentImpl(pDoc, LOTUS123);
430 1 : xDocSh->DoClose();
431 1 : }
432 :
433 6 : ScFiltersTest::ScFiltersTest()
434 6 : : m_aBaseString(RTL_CONSTASCII_USTRINGPARAM("/sc/qa/unit/data"))
435 : {
436 6 : }
437 :
438 6 : void ScFiltersTest::setUp()
439 : {
440 6 : test::BootstrapFixture::setUp();
441 :
442 : // This is a bit of a fudge, we do this to ensure that ScGlobals::ensure,
443 : // which is a private symbol to us, gets called
444 : m_xCalcComponent =
445 12 : getMultiServiceFactory()->createInstance(rtl::OUString(
446 6 : RTL_CONSTASCII_USTRINGPARAM("com.sun.star.comp.Calc.SpreadsheetDocument")));
447 6 : CPPUNIT_ASSERT_MESSAGE("no calc component!", m_xCalcComponent.is());
448 6 : }
449 :
450 6 : void ScFiltersTest::tearDown()
451 : {
452 6 : uno::Reference< lang::XComponent >( m_xCalcComponent, UNO_QUERY_THROW )->dispose();
453 6 : test::BootstrapFixture::tearDown();
454 6 : }
455 :
456 1 : CPPUNIT_TEST_SUITE_REGISTRATION(ScFiltersTest);
457 :
458 4 : CPPUNIT_PLUGIN_IMPLEMENT();
459 :
460 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|