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 "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>
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>
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
;
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";
76 std::ostream
& operator<<(std::ostream
& rStrm
, const ScRange
& rRange
)
78 rStrm
<< "ScRange: " << rRange
.aStart
<< rRange
.aEnd
<< "\n";
82 std::ostream
& operator<<(std::ostream
& rStrm
, const ScRangeList
& rList
)
84 rStrm
<< "ScRangeList: \n";
85 for(size_t i
= 0; i
< rList
.size(); ++i
)
90 std::ostream
& operator<<(std::ostream
& rStrm
, const OpCode
& rCode
)
92 rStrm
<< static_cast<sal_uInt16
>(rCode
);
96 void ScModelTestBase::loadFile(const OUString
& aFileName
, std::string
& aContent
)
98 OString aOFileName
= OUStringToOString(aFileName
, RTL_TEXTENCODING_UTF8
);
102 if (strncmp(aOFileName
.getStr(), "/assets/", sizeof("/assets/")-1) == 0) {
103 const char *contents
= (const char *) lo_apkentry(aOFileName
.getStr(), &size
);
105 aContent
= std::string(contents
, size
);
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();
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
);
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
);
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
178 const ScPatternAttr
* pPattern
= pDoc
->GetPattern(0, 0, 1);
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
);
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 );
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
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
)
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
)
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();
356 cout
<< "Failed to retrieve the drawing layer object." << endl
;
360 const SdrPage
* pPage
= pDrawLayer
->GetPage(nPage
);
363 cout
<< "Failed to retrieve the page object." << endl
;
367 if (pPage
->GetObjCount() != 1)
369 cout
<< "This page should contain one drawing object." << endl
;
373 const SdrObject
* pObj
= pPage
->GetObj(0);
376 cout
<< "Failed to retrieve the drawing object." << endl
;
380 if (pObj
->GetObjIdentifier() != SdrObjKind::OLE2
)
382 cout
<< "This is not an OLE2 object." << endl
;
386 return static_cast<const SdrOle2Obj
*>(pObj
);
389 const SdrOle2Obj
* ScModelTestBase::getSingleChartObject(ScDocument
& rDoc
, sal_uInt16 nPage
)
391 const SdrOle2Obj
* pObj
= getSingleOleObject(rDoc
, nPage
);
396 if (!pObj
->IsChart())
398 cout
<< "This should be a chart object." << endl
;
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();
413 cout
<< "Failed to get the embedded object interface." << endl
;
417 Reference
<chart2::XChartDocument
> xChartDoc(xModel
, UNO_QUERY
);
420 cout
<< "Failed to get the chart document interface." << endl
;
424 Reference
<chart2::data::XDataSource
> xDataSource(xChartDoc
, UNO_QUERY
);
425 if (!xDataSource
.is())
427 cout
<< "Failed to get the data source interface." << endl
;
431 Sequence
<Reference
<chart2::data::XLabeledDataSequence
> > xDataSeqs
= xDataSource
->getDataSequences();
432 if (!xDataSeqs
.hasElements())
434 cout
<< "There should be at least one data sequences." << endl
;
438 Reference
<chart2::data::XDataReceiver
> xDataRec(xChartDoc
, UNO_QUERY
);
441 cout
<< "Failed to get the data receiver interface." << endl
;
445 Sequence
<OUString
> aRangeRepSeqs
= xDataRec
->getUsedRangeRepresentations();
446 comphelper::sequenceToContainer(aRangeReps
, aRangeRepSeqs
);
451 ScRangeList
ScModelTestBase::getChartRanges(ScDocument
& rDoc
, const SdrOle2Obj
& rChartObj
)
453 std::vector
<OUString
> aRangeReps
= getChartRangeRepresentations(rChartObj
);
455 for (size_t i
= 0, n
= aRangeReps
.size(); i
< n
; ++i
)
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
);
464 // Parse it as a single cell address.
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
);
476 const ScDocument
* pDoc
, const ScRange
& aOutRange
,
477 const std::vector
<std::vector
<const char*>>& aCheck
, const char* pCaption
)
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
];
498 OUString aCheckVal
= OUString::createFromAscii(p
);
499 std::cout
<< "'" << aCheckVal
<< "', ";
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
;
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
];
531 OUString aCheckVal
= OUString::createFromAscii(p
);
532 bool bEqual
= aCheckVal
== aVal
;
535 std::cout
<< "Expected: " << aCheckVal
<< " Actual: " << aVal
<< std::endl
;
539 else if (!aVal
.isEmpty())
541 std::cout
<< "Empty cell expected" << std::endl
;
546 printer
.print(pCaption
);
550 void ScUcalcTestBase::setUp()
552 BootstrapFixture::setUp();
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();
569 test::BootstrapFixture::tearDown();
572 void ScModelTestBase::createScDoc(const char* pName
, const char* pPassword
, bool bCheckWarningError
)
575 load("private:factory/scalc");
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
);
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));
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
);
682 CPPUNIT_ASSERT(!ScCalcConfig::isOpenCLEnabled());
684 // Open the document with OpenCL disabled
685 mxComponent
= mxDesktop
->loadComponentFromURL(
686 createFileURL(fileName
), "_default", 0, comphelper::containerToSequence(args
));
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
)
707 return ScRange(ScAddress::INITIALIZE_INVALID
);
709 ScAddress aPos
= rPos
;
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
)
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
);
745 pDoc
->StartAllListeners(aRange
);
746 printRange(pDoc
, aRange
, "Range data content");
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
;
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
,
775 pSrcDoc
->DeleteSelection( InsertDeleteFlags::ALL
, aMark
);
776 aMark
.MarkToSimple();
779 return new ScUndoCut( &rDocSh
, rRange
, rRange
.aEnd
, aMark
, std::move(pUndoDoc
) );
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));
834 SfxMedium
* pMedium
= pShell
->GetMedium();
837 OUString aName
= pMedium
->GetName();
841 pShell
= static_cast<ScDocShell
*>(SfxObjectShell::GetNext(*pShell
, checkSfxObjectShell
<ScDocShell
>, false));
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(
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
);
862 cerr
<< "Insertion failed." << endl
;
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
);
886 // Avoid unused parameter warning
887 void ScUcalcTestBase::printFormula(ScDocument
*, SCCOL
, SCROW
, SCTAB
, const char*) {}
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
);
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
);
937 cerr
<< "Formula cell expected at " << aStr
<< " but not found." << endl
;
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
;
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
];
960 if (!checkFormulaPosition(rDoc
, aPos
))
962 OUString
aStr(aPos
.Format(ScRefFlags::VALID
));
963 cerr
<< "Formula cell position failed at " << aStr
<< "." << endl
;
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
);
992 pFC
->SetChanged(false);
999 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */