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 : #ifndef SC_QA_HELPER_HXX
11 : #define SC_QA_HELPER_HXX
12 :
13 : #include <test/bootstrapfixture.hxx>
14 : #define __ORCUS_STATIC_LIB
15 : #include "helper/csv_handler.hxx"
16 : #include "helper/debughelper.hxx"
17 : #include "orcus/csv_parser.hpp"
18 : #include <fstream>
19 : #include <string>
20 : #include <sstream>
21 :
22 : #include <comphelper/documentconstants.hxx>
23 :
24 : #include <osl/detail/android-bootstrap.h>
25 :
26 : #include <unotools/tempfile.hxx>
27 : #include <comphelper/storagehelper.hxx>
28 : #include <sfx2/docfilt.hxx>
29 :
30 : #define ODS_FORMAT_TYPE 50331943
31 : #define XLS_FORMAT_TYPE 318767171
32 : #define XLSX_FORMAT_TYPE 268959811
33 : #define LOTUS123_FORMAT_TYPE 268435649
34 : #define CSV_FORMAT_TYPE (SFX_FILTER_IMPORT | SFX_FILTER_EXPORT | SFX_FILTER_ALIEN | SFX_FILTER_USESOPTIONS)
35 : #define HTML_FORMAT_TYPE (SFX_FILTER_IMPORT | SFX_FILTER_EXPORT | SFX_FILTER_ALIEN | SFX_FILTER_USESOPTIONS)
36 : #define DIF_FORMAT_TYPE 195
37 :
38 : #define ODS 0
39 : #define XLS 1
40 : #define XLSX 2
41 : #define CSV 3
42 : #define HTML 4
43 : #define LOTUS123 5
44 : #define DIF 6
45 :
46 26 : bool testEqualsWithTolerance( long nVal1, long nVal2, long nTol )
47 : {
48 26 : return ( labs( nVal1 - nVal2 ) <= nTol );
49 : }
50 :
51 : struct FileFormat {
52 : const char* pName; const char* pFilterName; const char* pTypeName; unsigned int nFormatType;
53 : };
54 :
55 : #define CHECK_OPTIMAL 0x1
56 :
57 : // data format for row height tests
58 : struct TestParam
59 : {
60 : struct RowData
61 : {
62 : SCROW nStartRow;
63 : SCROW nEndRow;
64 : SCTAB nTab;
65 : int nExpectedHeight; // -1 for default height
66 : int nCheck; // currently only CHECK_OPTIMAL ( we could add CHECK_MANUAL etc.)
67 : bool bOptimal;
68 : };
69 : const char* sTestDoc;
70 : int nImportType;
71 : int nExportType; // -1 for import test, otherwise this is an export test
72 : int nRowData;
73 : RowData* pData;
74 : };
75 :
76 : FileFormat aFileFormats[] = {
77 : { "ods" , "calc8", "", ODS_FORMAT_TYPE },
78 : { "xls" , "MS Excel 97", "calc_MS_EXCEL_97", XLS_FORMAT_TYPE },
79 : { "xlsx", "Calc MS Excel 2007 XML" , "MS Excel 2007 XML", XLSX_FORMAT_TYPE },
80 : { "csv" , "Text - txt - csv (StarCalc)", "generic_Text", CSV_FORMAT_TYPE },
81 : { "html" , "calc_HTML_WebQuery", "generic_HTML", HTML_FORMAT_TYPE },
82 : { "123" , "Lotus", "calc_Lotus", LOTUS123_FORMAT_TYPE },
83 : { "dif", "DIF", "calc_DIF", DIF_FORMAT_TYPE },
84 : };
85 :
86 : // Why is this here and not in osl, and using the already existing file
87 : // handling APIs? Do we really want to add arbitrary new file handling
88 : // wrappers here and there (and then having to handle the Android (and
89 : // eventually perhaps iOS) special cases here, too)? Please move this to osl,
90 : // it sure looks gemerally useful. Or am I missing something?
91 :
92 33 : void loadFile(const OUString& aFileName, std::string& aContent)
93 : {
94 33 : OString aOFileName = OUStringToOString(aFileName, RTL_TEXTENCODING_UTF8);
95 :
96 : #ifdef ANDROID
97 : size_t size;
98 : if (strncmp(aOFileName.getStr(), "/assets/", sizeof("/assets/")-1) == 0) {
99 : const char *contents = (const char *) lo_apkentry(aOFileName.getStr(), &size);
100 : if (contents != 0) {
101 : aContent = std::string(contents, size);
102 : return;
103 : }
104 : }
105 : #endif
106 :
107 66 : std::ifstream aFile(aOFileName.getStr());
108 :
109 66 : OStringBuffer aErrorMsg("Could not open csv file: ");
110 33 : aErrorMsg.append(aOFileName);
111 33 : CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), aFile);
112 66 : std::ostringstream aOStream;
113 33 : aOStream << aFile.rdbuf();
114 33 : aFile.close();
115 66 : aContent = aOStream.str();
116 33 : }
117 :
118 0 : std::string print(const ScAddress& rAddr)
119 : {
120 0 : std::stringstream str;
121 0 : str << "Col: " << rAddr.Col();
122 0 : str << " Row: " << rAddr.Row();
123 0 : str << " Tab: " << rAddr.Tab();
124 0 : return str.str();
125 : }
126 :
127 : namespace CppUnit {
128 :
129 : template<>
130 : struct assertion_traits<ScRange>
131 : {
132 2 : static bool equal( const ScRange& x, const ScRange& y )
133 : {
134 2 : return x == y;
135 : }
136 :
137 0 : static std::string toString( const ScRange& x )
138 : {
139 0 : std::stringstream str;
140 0 : str << "Start: " << print(x.aStart);
141 0 : str << "\nEnd: " << print(x.aEnd);
142 0 : return str.str();
143 : }
144 : };
145 :
146 : }
147 :
148 63 : class ScBootstrapFixture : public test::BootstrapFixture
149 : {
150 : protected:
151 : OUString m_aBaseString;
152 98 : ScDocShellRef load( bool bReadWrite,
153 : const OUString& rURL, const OUString& rFilter, const OUString &rUserData,
154 : const OUString& rTypeName, unsigned int nFilterFlags, unsigned int nClipboardID, sal_uIntPtr nFilterVersion = SOFFICE_FILEFORMAT_CURRENT, const OUString* pPassword = NULL )
155 : {
156 : SfxFilter* pFilter = new SfxFilter(
157 : rFilter,
158 : OUString(), nFilterFlags, nClipboardID, rTypeName, 0, OUString(),
159 98 : rUserData, OUString("private:factory/scalc*"));
160 98 : pFilter->SetVersion(nFilterVersion);
161 :
162 98 : ScDocShellRef xDocShRef = new ScDocShell;
163 98 : xDocShRef->GetDocument()->EnableUserInteraction(false);
164 98 : SfxMedium* pSrcMed = new SfxMedium(rURL, bReadWrite ? STREAM_STD_READWRITE : STREAM_STD_READ );
165 98 : pSrcMed->SetFilter(pFilter);
166 98 : pSrcMed->UseInteractionHandler(false);
167 98 : if (pPassword)
168 : {
169 1 : SfxItemSet* pSet = pSrcMed->GetItemSet();
170 1 : pSet->Put(SfxStringItem(SID_PASSWORD, *pPassword));
171 : }
172 98 : printf("about to load %s\n", OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
173 98 : if (!xDocShRef->DoLoad(pSrcMed))
174 : {
175 3 : xDocShRef->DoClose();
176 : // load failed.
177 3 : xDocShRef.Clear();
178 : }
179 :
180 98 : return xDocShRef;
181 : }
182 :
183 35 : ScDocShellRef load(
184 : const OUString& rURL, const OUString& rFilter, const OUString &rUserData,
185 : const OUString& rTypeName, unsigned int nFilterFlags, unsigned int nClipboardID, sal_uIntPtr nFilterVersion = SOFFICE_FILEFORMAT_CURRENT, const OUString* pPassword = NULL )
186 : {
187 35 : return load( false, rURL, rFilter, rUserData, rTypeName, nFilterFlags, nClipboardID, nFilterVersion, pPassword );
188 : }
189 :
190 63 : ScDocShellRef loadDoc(const OUString& rFileName, sal_Int32 nFormat, bool bReadWrite = false )
191 : {
192 63 : OUString aFileExtension(aFileFormats[nFormat].pName, strlen(aFileFormats[nFormat].pName), RTL_TEXTENCODING_UTF8 );
193 126 : OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ;
194 126 : OUString aFileName;
195 63 : createFileURL( rFileName, aFileExtension, aFileName );
196 126 : OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8);
197 63 : unsigned int nFormatType = aFileFormats[nFormat].nFormatType;
198 63 : unsigned int nClipboardId = nFormatType ? SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS : 0;
199 :
200 126 : return load(bReadWrite, aFileName, aFilterName, OUString(), aFilterType, nFormatType, nClipboardId, nFormatType);
201 : }
202 :
203 :
204 : public:
205 63 : ScBootstrapFixture( const OUString& rsBaseString ) : m_aBaseString( rsBaseString ) {}
206 67 : void createFileURL(const OUString& aFileBase, const OUString& aFileExtension, OUString& rFilePath)
207 : {
208 67 : OUString aSep("/");
209 134 : OUStringBuffer aBuffer( getSrcRootURL() );
210 67 : aBuffer.append(m_aBaseString).append(aSep).append(aFileExtension);
211 67 : aBuffer.append(aSep).append(aFileBase).append(aFileExtension);
212 134 : rFilePath = aBuffer.makeStringAndClear();
213 67 : }
214 :
215 33 : void createCSVPath(const OUString& aFileBase, OUString& rCSVPath)
216 : {
217 33 : OUStringBuffer aBuffer( getSrcRootPath());
218 33 : aBuffer.append(m_aBaseString).append(OUString("/contentCSV/"));
219 33 : aBuffer.append(aFileBase).append(OUString("csv"));
220 33 : rCSVPath = aBuffer.makeStringAndClear();
221 33 : }
222 :
223 11 : ScDocShellRef saveAndReload(ScDocShell* pShell, const OUString &rFilter,
224 : const OUString &rUserData, const OUString& rTypeName, sal_uLong nFormatType)
225 : {
226 :
227 11 : utl::TempFile aTempFile;
228 11 : aTempFile.EnableKillingFile();
229 22 : SfxMedium aStoreMedium( aTempFile.GetURL(), STREAM_STD_WRITE );
230 11 : sal_uInt32 nExportFormat = 0;
231 11 : if (nFormatType == ODS_FORMAT_TYPE)
232 5 : nExportFormat = SFX_FILTER_EXPORT | SFX_FILTER_USESOPTIONS;
233 : SfxFilter* pExportFilter = new SfxFilter(
234 : rFilter,
235 : OUString(), nFormatType, nExportFormat, rTypeName, 0, OUString(),
236 11 : rUserData, OUString("private:factory/scalc*") );
237 11 : pExportFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT);
238 11 : aStoreMedium.SetFilter(pExportFilter);
239 11 : pShell->DoSaveAs( aStoreMedium );
240 11 : pShell->DoClose();
241 :
242 : //std::cout << "File: " << aTempFile.GetURL() << std::endl;
243 :
244 11 : sal_uInt32 nFormat = 0;
245 11 : if (nFormatType == ODS_FORMAT_TYPE)
246 5 : nFormat = SFX_FILTER_IMPORT | SFX_FILTER_USESOPTIONS;
247 :
248 22 : return load(aTempFile.GetURL(), rFilter, rUserData, rTypeName, nFormatType, nFormat );
249 : }
250 11 : ScDocShellRef saveAndReload( ScDocShell* pShell, sal_Int32 nFormat )
251 : {
252 11 : OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ;
253 22 : OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8);
254 11 : ScDocShellRef xDocSh = saveAndReload(pShell, aFilterName, OUString(), aFilterType, aFileFormats[nFormat].nFormatType);
255 :
256 11 : CPPUNIT_ASSERT(xDocSh.Is());
257 22 : return xDocSh;
258 : }
259 :
260 2 : void miscRowHeightsTest( TestParam* aTestValues, unsigned int numElems )
261 : {
262 8 : for ( unsigned int index=0; index<numElems; ++index )
263 : {
264 6 : OUString sFileName = OUString::createFromAscii( aTestValues[ index ].sTestDoc );
265 6 : printf("aTestValues[%u] %s\n", index, OUStringToOString( sFileName, RTL_TEXTENCODING_UTF8 ).getStr() );
266 6 : int nImportType = aTestValues[ index ].nImportType;
267 6 : int nExportType = aTestValues[ index ].nExportType;
268 12 : ScDocShellRef xShell = loadDoc( sFileName, nImportType );
269 6 : CPPUNIT_ASSERT(xShell.Is());
270 :
271 6 : if ( nExportType != -1 )
272 4 : xShell = saveAndReload(&(*xShell), nExportType );
273 :
274 6 : CPPUNIT_ASSERT(xShell.Is());
275 :
276 6 : ScDocument* pDoc = xShell->GetDocument();
277 :
278 24 : for (int i=0; i<aTestValues[ index ].nRowData; ++i)
279 : {
280 18 : SCROW nRow = aTestValues[ index ].pData[ i].nStartRow;
281 18 : SCROW nEndRow = aTestValues[ index ].pData[ i ].nEndRow;
282 18 : SCTAB nTab = aTestValues[ index ].pData[ i ].nTab;
283 18 : int nExpectedHeight = aTestValues[ index ].pData[ i ].nExpectedHeight;
284 18 : if ( nExpectedHeight == -1 )
285 2 : nExpectedHeight = sc::TwipsToHMM( ScGlobal::nStdRowHeight );
286 18 : bool bCheckOpt = ( ( aTestValues[ index ].pData[ i ].nCheck & CHECK_OPTIMAL ) == CHECK_OPTIMAL );
287 92 : for ( ; nRow <= nEndRow; ++nRow )
288 : {
289 74 : printf("\t checking row %" SAL_PRIdINT32 " for height %d\n", nRow, nExpectedHeight );
290 74 : int nHeight = sc::TwipsToHMM( pDoc->GetRowHeight(nRow, nTab, false) );
291 74 : if ( bCheckOpt )
292 : {
293 2 : bool bOpt = !(pDoc->GetRowFlags( nRow, nTab ) & CR_MANUALSIZE);
294 2 : CPPUNIT_ASSERT_EQUAL(aTestValues[ index ].pData[ i ].bOptimal, bOpt);
295 : }
296 74 : CPPUNIT_ASSERT_EQUAL(nExpectedHeight, nHeight);
297 : }
298 : }
299 6 : xShell->DoClose();
300 6 : }
301 2 : }
302 : };
303 :
304 28 : void testFile(OUString& aFileName, ScDocument* pDoc, SCTAB nTab, StringType aStringFormat = StringValue)
305 : {
306 28 : csv_handler aHandler(pDoc, nTab, aStringFormat);
307 28 : orcus::csv_parser_config aConfig;
308 28 : aConfig.delimiters.push_back(',');
309 28 : aConfig.delimiters.push_back(';');
310 28 : aConfig.text_qualifier = '"';
311 28 : aConfig.trim_cell_value = false;
312 :
313 :
314 56 : std::string aContent;
315 28 : loadFile(aFileName, aContent);
316 56 : orcus::csv_parser<csv_handler> parser ( &aContent[0], aContent.size() , aHandler, aConfig);
317 : try
318 : {
319 28 : parser.parse();
320 : }
321 0 : catch (const orcus::csv_parse_error& e)
322 : {
323 0 : std::cout << "reading csv content file failed: " << e.what() << std::endl;
324 0 : OStringBuffer aErrorMsg("csv parser error: ");
325 0 : aErrorMsg.append(e.what());
326 0 : CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false);
327 28 : }
328 28 : }
329 :
330 : //need own handler because conditional formatting strings must be generated
331 5 : void testCondFile(OUString& aFileName, ScDocument* pDoc, SCTAB nTab)
332 : {
333 5 : conditional_format_handler aHandler(pDoc, nTab);
334 5 : orcus::csv_parser_config aConfig;
335 5 : aConfig.delimiters.push_back(',');
336 5 : aConfig.delimiters.push_back(';');
337 5 : aConfig.text_qualifier = '"';
338 10 : std::string aContent;
339 5 : loadFile(aFileName, aContent);
340 10 : orcus::csv_parser<conditional_format_handler> parser ( &aContent[0], aContent.size() , aHandler, aConfig);
341 : try
342 : {
343 5 : parser.parse();
344 : }
345 0 : catch (const orcus::csv_parse_error& e)
346 : {
347 0 : std::cout << "reading csv content file failed: " << e.what() << std::endl;
348 0 : OStringBuffer aErrorMsg("csv parser error: ");
349 0 : aErrorMsg.append(e.what());
350 0 : CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false);
351 5 : }
352 :
353 5 : }
354 :
355 : #define ASSERT_DOUBLES_EQUAL( expected, result ) \
356 : CPPUNIT_ASSERT_DOUBLES_EQUAL( (expected), (result), 1e-14 )
357 :
358 : #define ASSERT_DOUBLES_EQUAL_MESSAGE( message, expected, result ) \
359 : CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( (message), (expected), (result), 1e-14 )
360 :
361 : #endif
362 :
363 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|