Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / qa / unit / helper / qahelper.cxx
blob4dc09a65de6e24380af6cf0c71d8016b66358dad
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 "qahelper.hxx"
11 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
12 #include <comphelper/propertysequence.hxx>
13 #include "csv_handler.hxx"
14 #include "debughelper.hxx"
15 #include <drwlayer.hxx>
16 #include <comphelper/sequence.hxx>
17 #include <comphelper/servicehelper.hxx>
18 #include <compiler.hxx>
19 #include <conditio.hxx>
20 #include <stlsheet.hxx>
21 #include <formulacell.hxx>
22 #include <formulagroup.hxx>
23 #include <svx/svdpage.hxx>
24 #include <svx/svdoole2.hxx>
25 #include <tools/UnitConversion.hxx>
26 #include <editeng/brushitem.hxx>
27 #include <editeng/justifyitem.hxx>
28 #include <clipcontext.hxx>
29 #include <clipparam.hxx>
30 #include <refundo.hxx>
31 #include <sal/log.hxx>
32 #include <svl/gridprinter.hxx>
33 #include <sfx2/docfile.hxx>
34 #include <undoblk.hxx>
35 #include <scdll.hxx>
36 #include <scitems.hxx>
37 #include <stringutil.hxx>
38 #include <tokenarray.hxx>
39 #include <vcl/keycodes.hxx>
40 #include <vcl/scheduler.hxx>
41 #include <o3tl/safeint.hxx>
43 #include <orcus/csv_parser.hpp>
45 #include <cstdlib>
46 #include <fstream>
48 #include <com/sun/star/chart2/XChartDocument.hpp>
49 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
50 #include <com/sun/star/document/MacroExecMode.hpp>
52 using namespace com::sun::star;
53 using namespace ::com::sun::star::uno;
54 using ::std::cout;
55 using ::std::cerr;
56 using ::std::endl;
58 FormulaGrammarSwitch::FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar) :
59 mpDoc(pDoc), meOldGrammar(pDoc->GetGrammar())
61 mpDoc->SetGrammar(eGrammar);
64 FormulaGrammarSwitch::~FormulaGrammarSwitch()
66 mpDoc->SetGrammar(meOldGrammar);
69 // calc data structure pretty printer
70 std::ostream& operator<<(std::ostream& rStrm, const ScAddress& rAddr)
72 rStrm << "Col: " << rAddr.Col() << " Row: " << rAddr.Row() << " Tab: " << rAddr.Tab() << "\n";
73 return rStrm;
76 std::ostream& operator<<(std::ostream& rStrm, const ScRange& rRange)
78 rStrm << "ScRange: " << rRange.aStart << rRange.aEnd << "\n";
79 return rStrm;
82 std::ostream& operator<<(std::ostream& rStrm, const ScRangeList& rList)
84 rStrm << "ScRangeList: \n";
85 for(size_t i = 0; i < rList.size(); ++i)
86 rStrm << rList[i];
87 return rStrm;
90 std::ostream& operator<<(std::ostream& rStrm, const OpCode& rCode)
92 rStrm << static_cast<sal_uInt16>(rCode);
93 return rStrm;
96 void ScModelTestBase::loadFile(const OUString& aFileName, std::string& aContent)
98 OString aOFileName = OUStringToOString(aFileName, RTL_TEXTENCODING_UTF8);
100 #ifdef ANDROID
101 size_t size;
102 if (strncmp(aOFileName.getStr(), "/assets/", sizeof("/assets/")-1) == 0) {
103 const char *contents = (const char *) lo_apkentry(aOFileName.getStr(), &size);
104 if (contents != 0) {
105 aContent = std::string(contents, size);
106 return;
109 #endif
111 std::ifstream aFile(aOFileName.getStr());
113 OString aErrorMsg = "Could not open csv file: " + aOFileName;
114 CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), aFile);
115 std::ostringstream aOStream;
116 aOStream << aFile.rdbuf();
117 aFile.close();
118 aContent = aOStream.str();
121 void ScModelTestBase::testFile(const OUString& aFileName, ScDocument& rDoc, SCTAB nTab, StringType aStringFormat)
123 csv_handler aHandler(&rDoc, nTab, aStringFormat);
124 orcus::csv::parser_config aConfig;
125 aConfig.delimiters.push_back(',');
126 aConfig.delimiters.push_back(';');
127 aConfig.text_qualifier = '"';
128 aConfig.trim_cell_value = false;
130 std::string aContent;
131 loadFile(aFileName, aContent);
132 orcus::csv_parser<csv_handler> parser(aContent, aHandler, aConfig);
135 parser.parse();
137 catch (const orcus::parse_error& e)
139 std::cout << "reading csv content file failed: " << e.what() << std::endl;
140 OString aErrorMsg = OString::Concat("csv parser error: ") + e.what();
141 CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false);
145 void ScModelTestBase::testCondFile( const OUString& aFileName, ScDocument* pDoc, SCTAB nTab, bool bCommaAsDelimiter )
147 conditional_format_handler aHandler(pDoc, nTab);
148 orcus::csv::parser_config aConfig;
149 if ( bCommaAsDelimiter )
150 aConfig.delimiters.push_back(',');
151 aConfig.delimiters.push_back(';');
152 aConfig.text_qualifier = '"';
153 std::string aContent;
154 loadFile(aFileName, aContent);
155 orcus::csv_parser<conditional_format_handler> parser(aContent, aHandler, aConfig);
158 parser.parse();
160 catch (const orcus::parse_error& e)
162 std::cout << "reading csv content file failed: " << e.what() << std::endl;
163 OString aErrorMsg = OString::Concat("csv parser error: ") + e.what();
164 CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false);
168 void ScModelTestBase::testFormats(ScDocument* pDoc,std::u16string_view sFormat)
170 //test Sheet1 with csv file
171 OUString aCSVFileName = createFilePath(u"contentCSV/numberFormat.csv");
172 testFile(aCSVFileName, *pDoc, 0, StringType::PureString);
173 //need to test the color of B3
174 //it's not a font color!
175 //formatting for B5: # ??/100 gets lost during import
177 //test Sheet2
178 const ScPatternAttr* pPattern = pDoc->GetPattern(0, 0, 1);
179 vcl::Font aFont;
180 model::ComplexColor aComplexColor;
182 pPattern->fillFontOnly(aFont);
183 pPattern->fillColor(aComplexColor, ScAutoFontColorMode::Raw);
184 CPPUNIT_ASSERT_EQUAL_MESSAGE("font size should be 10", tools::Long(200), aFont.GetFontSize().getHeight());
185 CPPUNIT_ASSERT_EQUAL_MESSAGE("font color should be black", COL_AUTO, aComplexColor.getFinalColor());
186 pPattern = pDoc->GetPattern(0,1,1);
187 pPattern->fillFontOnly(aFont);
188 CPPUNIT_ASSERT_EQUAL_MESSAGE("font size should be 12", tools::Long(240), aFont.GetFontSize().getHeight());
189 pPattern = pDoc->GetPattern(0,2,1);
190 pPattern->fillFontOnly(aFont);
191 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be italic", ITALIC_NORMAL, aFont.GetItalic());
192 pPattern = pDoc->GetPattern(0,4,1);
193 pPattern->fillFontOnly(aFont);
194 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, aFont.GetWeight());
195 pPattern = pDoc->GetPattern(1,0,1);
196 pPattern->fillFontOnly(aFont);
197 pPattern->fillColor(aComplexColor, ScAutoFontColorMode::Raw);
198 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be blue", COL_BLUE, aComplexColor.getFinalColor());
199 pPattern = pDoc->GetPattern(1,1,1);
200 pPattern->fillFontOnly(aFont);
201 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be striked out with a single line", STRIKEOUT_SINGLE, aFont.GetStrikeout());
202 //some tests on sheet2 only for ods
203 if (sFormat == u"calc8")
205 pPattern = pDoc->GetPattern(1,2,1);
206 pPattern->fillFontOnly(aFont);
207 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be striked out with a double line", STRIKEOUT_DOUBLE, aFont.GetStrikeout());
208 pPattern = pDoc->GetPattern(1,3,1);
209 pPattern->fillFontOnly(aFont);
210 CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be underlined with a dotted line", LINESTYLE_DOTTED, aFont.GetUnderline());
211 //check row height import
212 //disable for now until we figure out cause of win tinderboxes test failures
213 //CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(256), pDoc->GetRowHeight(0,1) ); //0.178in
214 //CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(304), pDoc->GetRowHeight(1,1) ); //0.211in
215 //CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(477), pDoc->GetRowHeight(5,1) ); //0.3311in
216 //check column width import
217 CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(555), pDoc->GetColWidth(4,1) ); //0.3854in
218 CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(1280), pDoc->GetColWidth(5,1) ); //0.889in
219 CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(4153), pDoc->GetColWidth(6,1) ); //2.8839in
220 //test case for i53253 where a cell has text with different styles and space between the text.
221 OUString aTestStr = pDoc->GetString(3,0,1);
222 OUString aKnownGoodStr("text14 space");
223 CPPUNIT_ASSERT_EQUAL( aKnownGoodStr, aTestStr );
224 //test case for cell text with line breaks.
225 aTestStr = pDoc->GetString(3,5,1);
226 aKnownGoodStr = "Hello,\nCalc!";
227 CPPUNIT_ASSERT_EQUAL( aKnownGoodStr, aTestStr );
229 pPattern = pDoc->GetPattern(1,4,1);
230 Color aColor = pPattern->GetItem(ATTR_BACKGROUND).GetColor();
231 CPPUNIT_ASSERT_EQUAL_MESSAGE("background color should be green", COL_LIGHTGREEN, aColor);
232 pPattern = pDoc->GetPattern(2,0,1);
233 SvxCellHorJustify eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
234 CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify);
235 //test alignment
236 pPattern = pDoc->GetPattern(2,1,1);
237 eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
238 CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned right horizontally", SvxCellHorJustify::Right, eHorJustify);
239 pPattern = pDoc->GetPattern(2,2,1);
240 eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue();
241 CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned block horizontally", SvxCellHorJustify::Block, eHorJustify);
243 //test Sheet3 only for ods and xlsx
244 if ( sFormat == u"calc8" || sFormat == u"Calc Office Open XML" )
246 aCSVFileName = createFilePath(u"contentCSV/conditionalFormatting.csv");
247 testCondFile(aCSVFileName, pDoc, 2);
248 // test parent cell style import ( fdo#55198 )
249 if ( sFormat == u"Calc Office Open XML" )
251 pPattern = pDoc->GetPattern(1,1,3);
252 ScStyleSheet* pStyleSheet = const_cast<ScStyleSheet*>(pPattern->GetStyleSheet());
253 // check parent style name
254 OUString sExpected("Excel Built-in Date");
255 OUString sResult = pStyleSheet->GetName();
256 CPPUNIT_ASSERT_EQUAL_MESSAGE("parent style for Sheet4.B2 is 'Excel Built-in Date'", sExpected, sResult);
257 // check align of style
258 SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
259 eHorJustify = rItemSet.Get( ATTR_HOR_JUSTIFY ).GetValue();
260 CPPUNIT_ASSERT_EQUAL_MESSAGE("'Excel Built-in Date' style should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify);
261 // check date format ( should be just month e.g. 29 )
262 sResult =pDoc->GetString( 1,1,3 );
263 sExpected = "29";
264 CPPUNIT_ASSERT_EQUAL_MESSAGE("'Excel Built-in Date' style should just display month", sExpected, sResult );
266 // check actual align applied to cell, should be the same as
267 // the style
268 eHorJustify = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
269 CPPUNIT_ASSERT_EQUAL_MESSAGE("cell with 'Excel Built-in Date' style should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify);
273 ScConditionalFormat* pCondFormat = pDoc->GetCondFormat(0,0,2);
274 const ScRangeList& rRange = pCondFormat->GetRange();
275 CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(0,0,2,3,0,2)), rRange);
277 pCondFormat = pDoc->GetCondFormat(0,1,2);
278 const ScRangeList& rRange2 = pCondFormat->GetRange();
279 CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(0,1,2,0,1,2)), rRange2);
281 pCondFormat = pDoc->GetCondFormat(1,1,2);
282 const ScRangeList& rRange3 = pCondFormat->GetRange();
283 CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(1,1,2,3,1,2)), rRange3);
286 void ScModelTestBase::goToCell(const OUString& rCell)
288 uno::Sequence<beans::PropertyValue> aArgs
289 = comphelper::InitPropertySequence({ { "ToPoint", uno::Any(rCell) } });
290 dispatchCommand(mxComponent, ".uno:GoToCell", aArgs);
293 void ScModelTestBase::typeString(const std::u16string_view& rStr)
295 ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
296 for (const char16_t c : rStr)
298 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, c, 0);
299 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, c, 0);
300 Scheduler::ProcessEventsToIdle();
304 void ScModelTestBase::insertStringToCell(const OUString& rCell, const std::u16string_view& rStr)
306 goToCell(rCell);
308 typeString(rStr);
310 ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
311 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
312 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
313 Scheduler::ProcessEventsToIdle();
316 void ScModelTestBase::insertArrayToCell(const OUString& rCell, const std::u16string_view& rStr)
318 goToCell(rCell);
320 typeString(rStr);
322 ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
323 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_MOD1 | KEY_SHIFT | awt::Key::RETURN);
324 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_MOD1 | KEY_SHIFT | awt::Key::RETURN);
325 Scheduler::ProcessEventsToIdle();
328 void ScModelTestBase::insertNewSheet(ScDocument& rDoc)
330 sal_Int32 nTabs = static_cast<sal_Int32>(rDoc.GetTableCount());
332 uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
333 { { "Name", uno::Any(OUString("NewTab")) }, { "Index", uno::Any(nTabs + 1) } }));
334 dispatchCommand(mxComponent, ".uno:Insert", aArgs);
336 CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(nTabs + 1), rDoc.GetTableCount());
339 void ScModelTestBase::executeAutoSum()
341 dispatchCommand(mxComponent, ".uno:AutoSum", {});
343 // Use RETURN key to exit autosum edit view
344 ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
345 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
346 pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
347 Scheduler::ProcessEventsToIdle();
350 const SdrOle2Obj* ScModelTestBase::getSingleOleObject(ScDocument& rDoc, sal_uInt16 nPage)
352 // Retrieve the chart object instance from the 2nd page (for the 2nd sheet).
353 ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
354 if (!pDrawLayer)
356 cout << "Failed to retrieve the drawing layer object." << endl;
357 return nullptr;
360 const SdrPage* pPage = pDrawLayer->GetPage(nPage);
361 if (!pPage)
363 cout << "Failed to retrieve the page object." << endl;
364 return nullptr;
367 if (pPage->GetObjCount() != 1)
369 cout << "This page should contain one drawing object." << endl;
370 return nullptr;
373 const SdrObject* pObj = pPage->GetObj(0);
374 if (!pObj)
376 cout << "Failed to retrieve the drawing object." << endl;
377 return nullptr;
380 if (pObj->GetObjIdentifier() != SdrObjKind::OLE2)
382 cout << "This is not an OLE2 object." << endl;
383 return nullptr;
386 return static_cast<const SdrOle2Obj*>(pObj);
389 const SdrOle2Obj* ScModelTestBase::getSingleChartObject(ScDocument& rDoc, sal_uInt16 nPage)
391 const SdrOle2Obj* pObj = getSingleOleObject(rDoc, nPage);
393 if (!pObj)
394 return pObj;
396 if (!pObj->IsChart())
398 cout << "This should be a chart object." << endl;
399 return nullptr;
402 return pObj;
405 static std::vector<OUString> getChartRangeRepresentations(const SdrOle2Obj& rChartObj)
407 std::vector<OUString> aRangeReps;
409 // Make sure the chart object has correct range references.
410 Reference<frame::XModel> xModel = rChartObj.getXModel();
411 if (!xModel.is())
413 cout << "Failed to get the embedded object interface." << endl;
414 return aRangeReps;
417 Reference<chart2::XChartDocument> xChartDoc(xModel, UNO_QUERY);
418 if (!xChartDoc.is())
420 cout << "Failed to get the chart document interface." << endl;
421 return aRangeReps;
424 Reference<chart2::data::XDataSource> xDataSource(xChartDoc, UNO_QUERY);
425 if (!xDataSource.is())
427 cout << "Failed to get the data source interface." << endl;
428 return aRangeReps;
431 Sequence<Reference<chart2::data::XLabeledDataSequence> > xDataSeqs = xDataSource->getDataSequences();
432 if (!xDataSeqs.hasElements())
434 cout << "There should be at least one data sequences." << endl;
435 return aRangeReps;
438 Reference<chart2::data::XDataReceiver> xDataRec(xChartDoc, UNO_QUERY);
439 if (!xDataRec.is())
441 cout << "Failed to get the data receiver interface." << endl;
442 return aRangeReps;
445 Sequence<OUString> aRangeRepSeqs = xDataRec->getUsedRangeRepresentations();
446 comphelper::sequenceToContainer(aRangeReps, aRangeRepSeqs);
448 return aRangeReps;
451 ScRangeList ScModelTestBase::getChartRanges(ScDocument& rDoc, const SdrOle2Obj& rChartObj)
453 std::vector<OUString> aRangeReps = getChartRangeRepresentations(rChartObj);
454 ScRangeList aRanges;
455 for (size_t i = 0, n = aRangeReps.size(); i < n; ++i)
457 ScRange aRange;
458 ScRefFlags nRes = aRange.Parse(aRangeReps[i], rDoc, rDoc.GetAddressConvention());
459 if (nRes & ScRefFlags::VALID)
460 // This is a range address.
461 aRanges.push_back(aRange);
462 else
464 // Parse it as a single cell address.
465 ScAddress aAddr;
466 nRes = aAddr.Parse(aRangeReps[i], rDoc, rDoc.GetAddressConvention());
467 CPPUNIT_ASSERT_MESSAGE("Failed to parse a range representation.", (nRes & ScRefFlags::VALID));
468 aRanges.push_back(aAddr);
472 return aRanges;
475 bool checkOutput(
476 const ScDocument* pDoc, const ScRange& aOutRange,
477 const std::vector<std::vector<const char*>>& aCheck, const char* pCaption )
479 bool bResult = true;
480 const ScAddress& s = aOutRange.aStart;
481 const ScAddress& e = aOutRange.aEnd;
482 svl::GridPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1, CALC_DEBUG_OUTPUT != 0);
483 SCROW nOutRowSize = e.Row() - s.Row() + 1;
484 SCCOL nOutColSize = e.Col() - s.Col() + 1;
486 // Check if expected size iz smaller than actual size (and prevent a crash)
487 if (aCheck.size() < o3tl::make_unsigned(nOutRowSize) || aCheck[0].size() < o3tl::make_unsigned(nOutColSize))
489 // Dump the arrays to console, so we can compare
490 std::cout << "Expected data:" << std::endl;
491 for (size_t nRow = 0; nRow < aCheck.size(); ++nRow)
493 for (size_t nCol = 0; nCol < aCheck[nRow].size(); ++nCol)
495 const char* p = aCheck[nRow][nCol];
496 if (p)
498 OUString aCheckVal = OUString::createFromAscii(p);
499 std::cout << "'" << aCheckVal << "', ";
501 else
502 std::cout << "null, ";
504 std::cout << std::endl;
507 std::cout << "Actual data:" << std::endl;
508 for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
510 for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
512 OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
513 std::cout << "'" << aVal << "', ";
515 std::cout << std::endl;
517 std::cout << std::endl;
519 return false;
522 for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
524 for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
526 OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
527 printer.set(nRow, nCol, aVal);
528 const char* p = aCheck[nRow][nCol];
529 if (p)
531 OUString aCheckVal = OUString::createFromAscii(p);
532 bool bEqual = aCheckVal == aVal;
533 if (!bEqual)
535 std::cout << "Expected: " << aCheckVal << " Actual: " << aVal << std::endl;
536 bResult = false;
539 else if (!aVal.isEmpty())
541 std::cout << "Empty cell expected" << std::endl;
542 bResult = false;
546 printer.print(pCaption);
547 return bResult;
550 void ScUcalcTestBase::setUp()
552 BootstrapFixture::setUp();
554 ScDLL::Init();
556 m_xDocShell
557 = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS
558 | SfxModelFlags::DISABLE_DOCUMENT_RECOVERY);
559 m_xDocShell->DoInitUnitTest();
561 m_pDoc = &m_xDocShell->GetDocument();
564 void ScUcalcTestBase::tearDown()
566 m_xDocShell->DoClose();
567 m_xDocShell.clear();
569 test::BootstrapFixture::tearDown();
572 void ScModelTestBase::createScDoc(const char* pName, const char* pPassword, bool bCheckWarningError)
574 if (!pName)
575 load("private:factory/scalc");
576 else
577 loadFromFile(OUString::createFromAscii(pName), pPassword);
579 uno::Reference<lang::XServiceInfo> xServiceInfo(mxComponent, uno::UNO_QUERY_THROW);
580 CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.sheet.SpreadsheetDocument"));
582 if (bCheckWarningError)
583 CPPUNIT_ASSERT(!getScDocShell()->GetMedium()->GetWarningError());
586 ScDocument* ScModelTestBase::getScDoc()
588 ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
589 CPPUNIT_ASSERT(pModelObj);
590 return pModelObj->GetDocument();
593 ScDocument* ScModelTestBase::getScDoc2()
595 ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(mxComponent2);
596 CPPUNIT_ASSERT(pModelObj);
597 return pModelObj->GetDocument();
600 ScDocShell* ScModelTestBase::getScDocShell()
602 SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(mxComponent);
603 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
604 ScDocShell* pDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
605 CPPUNIT_ASSERT(pDocSh);
606 return pDocSh;
609 ScTabViewShell* ScModelTestBase::getViewShell()
611 ScDocShell* pDocSh = getScDocShell();
612 ScTabViewShell* pTabViewShell = pDocSh->GetBestViewShell(false);
613 CPPUNIT_ASSERT_MESSAGE("No ScTabViewShell", pTabViewShell);
614 return pTabViewShell;
617 void ScModelTestBase::miscRowHeightsTest( TestParam const * aTestValues, unsigned int numElems)
619 for ( unsigned int index=0; index<numElems; ++index )
621 const std::u16string_view sFileName = aTestValues[ index ].sTestDoc;
622 const OUString sExportType = aTestValues[ index ].sExportType;
623 loadFromFile(sFileName);
625 if ( !sExportType.isEmpty() )
626 saveAndReload(sExportType);
628 ScDocument* pDoc = getScDoc();
630 for (int i=0; i<aTestValues[ index ].nRowData; ++i)
632 SCROW nRow = aTestValues[ index ].pData[ i].nStartRow;
633 SCROW nEndRow = aTestValues[ index ].pData[ i ].nEndRow;
634 SCTAB nTab = aTestValues[ index ].pData[ i ].nTab;
635 int nExpectedHeight = aTestValues[ index ].pData[ i ].nExpectedHeight;
636 if ( nExpectedHeight == -1 )
637 nExpectedHeight = convertTwipToMm100(ScGlobal::GetStandardRowHeight());
638 bool bCheckOpt = ( ( aTestValues[ index ].pData[ i ].nCheck & CHECK_OPTIMAL ) == CHECK_OPTIMAL );
639 for ( ; nRow <= nEndRow; ++nRow )
641 SAL_INFO( "sc.qa", " checking row " << nRow << " for height " << nExpectedHeight );
642 int nHeight = convertTwipToMm100(pDoc->GetRowHeight(nRow, nTab, false));
643 if ( bCheckOpt )
645 bool bOpt = !(pDoc->GetRowFlags( nRow, nTab ) & CRFlags::ManualSize);
646 CPPUNIT_ASSERT_EQUAL(aTestValues[ index ].pData[ i ].bOptimal, bOpt);
648 CPPUNIT_ASSERT_EQUAL(nExpectedHeight, nHeight);
654 void ScModelTestBase::enableOpenCL()
657 * Turn on OpenCL group interpreter. Call this after the document is
658 * loaded and before performing formula calculation.
660 sc::FormulaGroupInterpreter::enableOpenCL_UnitTestsOnly();
663 void ScModelTestBase::disableOpenCL()
665 sc::FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly();
668 void ScModelTestBase::initTestEnv(std::u16string_view fileName)
670 // Some documents contain macros, disable them, otherwise
671 // the "Error, BASIC runtime error." dialog is prompted
672 // and it crashes in tearDown
673 std::vector<beans::PropertyValue> args;
674 beans::PropertyValue aMacroValue;
675 aMacroValue.Name = "MacroExecutionMode";
676 aMacroValue.Handle = -1;
677 aMacroValue.Value <<= document::MacroExecMode::NEVER_EXECUTE;
678 aMacroValue.State = beans::PropertyState_DIRECT_VALUE;
679 args.push_back(aMacroValue);
681 disableOpenCL();
682 CPPUNIT_ASSERT(!ScCalcConfig::isOpenCLEnabled());
684 // Open the document with OpenCL disabled
685 mxComponent = mxDesktop->loadComponentFromURL(
686 createFileURL(fileName), "_default", 0, comphelper::containerToSequence(args));
688 enableOpenCL();
689 CPPUNIT_ASSERT(ScCalcConfig::isOpenCLEnabled());
691 // it's not possible to open the same document twice, thus, create a temp file
692 createTempCopy(fileName);
694 // Open the document with OpenCL enabled
695 mxComponent2 = mxDesktop->loadComponentFromURL(
696 maTempFile.GetURL(), "_default", 0, comphelper::containerToSequence(args));
698 // Check there are 2 documents
699 uno::Reference<frame::XFrames> xFrames = mxDesktop->getFrames();
700 CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xFrames->getCount());
703 ScRange ScUcalcTestBase::insertRangeData(
704 ScDocument* pDoc, const ScAddress& rPos, const std::vector<std::vector<const char*>>& rData )
706 if (rData.empty())
707 return ScRange(ScAddress::INITIALIZE_INVALID);
709 ScAddress aPos = rPos;
711 SCCOL nColWidth = 1;
712 for (const std::vector<const char*>& rRow : rData)
713 nColWidth = std::max<SCCOL>(nColWidth, rRow.size());
715 ScRange aRange(aPos);
716 aRange.aEnd.IncCol(nColWidth-1);
717 aRange.aEnd.IncRow(rData.size()-1);
719 clearRange(pDoc, aRange);
721 for (const std::vector<const char*>& rRow : rData)
723 aPos.SetCol(rPos.Col());
725 for (const char* pStr : rRow)
727 if (!pStr)
729 aPos.IncCol();
730 continue;
733 OUString aStr(pStr, strlen(pStr), RTL_TEXTENCODING_UTF8);
735 ScSetStringParam aParam; // Leave default.
736 aParam.meStartListening = sc::NoListening;
737 pDoc->SetString(aPos, aStr, &aParam);
739 aPos.IncCol();
742 aPos.IncRow();
745 pDoc->StartAllListeners(aRange);
746 printRange(pDoc, aRange, "Range data content");
747 return aRange;
750 ScUndoCut* ScUcalcTestBase::cutToClip(ScDocShell& rDocSh, const ScRange& rRange, ScDocument* pClipDoc, bool bCreateUndo)
752 ScDocument* pSrcDoc = &rDocSh.GetDocument();
754 ScClipParam aClipParam(rRange, true);
755 ScMarkData aMark(pSrcDoc->GetSheetLimits());
756 aMark.SetMarkArea(rRange);
757 pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aMark, false, false);
759 // Taken from ScViewFunc::CutToClip()
760 ScDocumentUniquePtr pUndoDoc;
761 if (bCreateUndo)
763 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
764 pUndoDoc->InitUndoSelected( *pSrcDoc, aMark );
765 // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
766 ScRange aCopyRange = rRange;
767 aCopyRange.aStart.SetTab(0);
768 aCopyRange.aEnd.SetTab(pSrcDoc->GetTableCount()-1);
769 pSrcDoc->CopyToDocument( aCopyRange,
770 (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS,
771 false, *pUndoDoc );
774 aMark.MarkToMulti();
775 pSrcDoc->DeleteSelection( InsertDeleteFlags::ALL, aMark );
776 aMark.MarkToSimple();
778 if (pUndoDoc)
779 return new ScUndoCut( &rDocSh, rRange, rRange.aEnd, aMark, std::move(pUndoDoc) );
781 return nullptr;
784 void ScUcalcTestBase::copyToClip(ScDocument* pSrcDoc, const ScRange& rRange, ScDocument* pClipDoc)
786 ScClipParam aClipParam(rRange, false);
787 ScMarkData aMark(pSrcDoc->GetSheetLimits());
788 aMark.SetMarkArea(rRange);
789 pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aMark, false, false);
792 void ScUcalcTestBase::pasteFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc)
794 ScMarkData aMark(pDestDoc->GetSheetLimits());
795 aMark.SetMarkArea(rDestRange);
796 pDestDoc->CopyFromClip(rDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pClipDoc);
799 ScUndoPaste* ScUcalcTestBase::createUndoPaste(ScDocShell& rDocSh, const ScRange& rRange, ScDocumentUniquePtr pUndoDoc)
801 ScDocument& rDoc = rDocSh.GetDocument();
802 ScMarkData aMarkData(rDoc.GetSheetLimits());
803 aMarkData.SetMarkArea(rRange);
804 std::unique_ptr<ScRefUndoData> pRefUndoData(new ScRefUndoData(&rDoc));
806 return new ScUndoPaste(
807 &rDocSh, rRange, aMarkData, std::move(pUndoDoc), nullptr, InsertDeleteFlags::ALL, std::move(pRefUndoData), false);
810 void ScUcalcTestBase::pasteOneCellFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc, InsertDeleteFlags eFlags)
812 ScMarkData aMark(pDestDoc->GetSheetLimits());
813 aMark.SetMarkArea(rDestRange);
814 sc::CopyFromClipContext aCxt(*pDestDoc, nullptr, pClipDoc, eFlags, false, false);
815 aCxt.setDestRange(rDestRange.aStart.Col(), rDestRange.aStart.Row(),
816 rDestRange.aEnd.Col(), rDestRange.aEnd.Row());
817 aCxt.setTabRange(rDestRange.aStart.Tab(), rDestRange.aEnd.Tab());
818 pDestDoc->CopyOneCellFromClip(aCxt, rDestRange.aStart.Col(), rDestRange.aStart.Row(),
819 rDestRange.aEnd.Col(), rDestRange.aEnd.Row());
822 void ScUcalcTestBase::setCalcAsShown(ScDocument* pDoc, bool bCalcAsShown)
824 ScDocOptions aOpt = pDoc->GetDocOptions();
825 aOpt.SetCalcAsShown(bCalcAsShown);
826 pDoc->SetDocOptions(aOpt);
829 ScDocShell* ScUcalcTestBase::findLoadedDocShellByName(std::u16string_view rName)
831 ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
832 while (pShell)
834 SfxMedium* pMedium = pShell->GetMedium();
835 if (pMedium)
837 OUString aName = pMedium->GetName();
838 if (aName == rName)
839 return pShell;
841 pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, false));
843 return nullptr;
846 bool ScUcalcTestBase::insertRangeNames(
847 ScDocument* pDoc, ScRangeName* pNames, const RangeNameDef* p, const RangeNameDef* pEnd)
849 ScAddress aA1(0, 0, 0);
850 for (; p != pEnd; ++p)
852 ScRangeData* pNew = new ScRangeData(
853 *pDoc,
854 OUString::createFromAscii(p->mpName),
855 OUString::createFromAscii(p->mpExpr),
856 aA1, ScRangeData::Type::Name,
857 formula::FormulaGrammar::GRAM_ENGLISH);
858 pNew->SetIndex(p->mnIndex);
859 bool bSuccess = pNames->insert(pNew);
860 if (!bSuccess)
862 cerr << "Insertion failed." << endl;
863 return false;
867 return true;
870 OUString ScUcalcTestBase::getRangeByName(ScDocument* pDoc, const OUString& aRangeName)
872 ScRangeData* pName = pDoc->GetRangeName()->findByUpperName(aRangeName.toAsciiUpperCase());
873 CPPUNIT_ASSERT(pName);
874 return pName->GetSymbol(pDoc->GetGrammar());
877 #if CALC_DEBUG_OUTPUT != 0
878 void ScUcalcTestBase::printFormula(ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, const char* pCaption)
880 if (pCaption != nullptr)
881 cout << pCaption << ", ";
882 cout << nCol << "/" << nRow << ": " << pDoc->GetFormula(nCol, nRow, nTab);
883 cout << endl;
885 #else
886 // Avoid unused parameter warning
887 void ScUcalcTestBase::printFormula(ScDocument*, SCCOL, SCROW, SCTAB, const char*) {}
888 #endif
890 void ScUcalcTestBase::printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption,
891 const bool printFormula)
893 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
894 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
895 svl::GridPrinter printer(nRow2 - nRow1 + 1, nCol2 - nCol1 + 1, CALC_DEBUG_OUTPUT != 0);
896 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
898 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
900 ScAddress aPos(nCol, nRow, rRange.aStart.Tab());
901 ScRefCellValue aCell(*pDoc, aPos);
902 OUString aVal = printFormula ? pDoc->GetFormula(nCol, nRow, rRange.aStart.Tab())
903 : ScCellFormat::GetOutputString(*pDoc, aPos, aCell);
904 printer.set(nRow - nRow1, nCol - nCol1, aVal);
907 printer.print(pCaption);
910 void ScUcalcTestBase::printRange(ScDocument* pDoc, const ScRange& rRange, const OString& rCaption,
911 const bool printFormula)
913 printRange(pDoc, rRange, rCaption.getStr(), printFormula);
916 void ScUcalcTestBase::clearRange(ScDocument* pDoc, const ScRange& rRange)
918 ScMarkData aMarkData(pDoc->GetSheetLimits());
919 aMarkData.SetMarkArea(rRange);
920 pDoc->DeleteArea(
921 rRange.aStart.Col(), rRange.aStart.Row(),
922 rRange.aEnd.Col(), rRange.aEnd.Row(), aMarkData, InsertDeleteFlags::CONTENTS);
925 void ScUcalcTestBase::clearSheet(ScDocument* pDoc, SCTAB nTab)
927 ScRange aRange(0,0,nTab,pDoc->MaxCol(),pDoc->MaxRow(),nTab);
928 clearRange(pDoc, aRange);
931 bool ScUcalcTestBase::checkFormulaPosition(ScDocument& rDoc, const ScAddress& rPos)
933 OUString aStr(rPos.Format(ScRefFlags::VALID));
934 const ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
935 if (!pFC)
937 cerr << "Formula cell expected at " << aStr << " but not found." << endl;
938 return false;
941 if (pFC->aPos != rPos)
943 OUString aStr2(pFC->aPos.Format(ScRefFlags::VALID));
944 cerr << "Formula cell at " << aStr << " has incorrect position of " << aStr2 << endl;
945 return false;
948 return true;
951 bool ScUcalcTestBase::checkFormulaPositions(
952 ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SCROW* pRows, size_t nRowCount)
954 ScAddress aPos(nCol, 0, nTab);
955 for (size_t i = 0; i < nRowCount; ++i)
957 SCROW nRow = pRows[i];
958 aPos.SetRow(nRow);
960 if (!checkFormulaPosition(rDoc, aPos))
962 OUString aStr(aPos.Format(ScRefFlags::VALID));
963 cerr << "Formula cell position failed at " << aStr << "." << endl;
964 return false;
967 return true;
970 std::unique_ptr<ScTokenArray> ScUcalcTestBase::compileFormula(
971 ScDocument* pDoc, const OUString& rFormula,
972 formula::FormulaGrammar::Grammar eGram )
974 ScAddress aPos(0,0,0);
975 ScCompiler aComp(*pDoc, aPos, eGram);
976 return aComp.CompileString(rFormula);
979 void ScUcalcTestBase::clearFormulaCellChangedFlag( ScDocument& rDoc, const ScRange& rRange )
981 const ScAddress& s = rRange.aStart;
982 const ScAddress& e = rRange.aEnd;
983 for (SCTAB nTab = s.Tab(); nTab <= e.Tab(); ++nTab)
985 for (SCCOL nCol = s.Col(); nCol <= e.Col(); ++nCol)
987 for (SCROW nRow = s.Row(); nRow <= e.Row(); ++nRow)
989 ScAddress aPos(nCol, nRow, nTab);
990 ScFormulaCell* pFC = rDoc.GetFormulaCell(aPos);
991 if (pFC)
992 pFC->SetChanged(false);
999 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */