/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "qahelper.hxx" #include #include #include "csv_handler.hxx" #include "debughelper.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; using namespace ::com::sun::star::uno; using ::std::cout; using ::std::cerr; using ::std::endl; FormulaGrammarSwitch::FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar) : mpDoc(pDoc), meOldGrammar(pDoc->GetGrammar()) { mpDoc->SetGrammar(eGrammar); } FormulaGrammarSwitch::~FormulaGrammarSwitch() { mpDoc->SetGrammar(meOldGrammar); } // calc data structure pretty printer std::ostream& operator<<(std::ostream& rStrm, const ScAddress& rAddr) { rStrm << "Col: " << rAddr.Col() << " Row: " << rAddr.Row() << " Tab: " << rAddr.Tab() << "\n"; return rStrm; } std::ostream& operator<<(std::ostream& rStrm, const ScRange& rRange) { rStrm << "ScRange: " << rRange.aStart << rRange.aEnd << "\n"; return rStrm; } std::ostream& operator<<(std::ostream& rStrm, const ScRangeList& rList) { rStrm << "ScRangeList: \n"; for(size_t i = 0; i < rList.size(); ++i) rStrm << rList[i]; return rStrm; } std::ostream& operator<<(std::ostream& rStrm, const OpCode& rCode) { rStrm << static_cast(rCode); return rStrm; } void ScModelTestBase::loadFile(const OUString& aFileName, std::string& aContent) { OString aOFileName = OUStringToOString(aFileName, RTL_TEXTENCODING_UTF8); #ifdef ANDROID size_t size; if (strncmp(aOFileName.getStr(), "/assets/", sizeof("/assets/")-1) == 0) { const char *contents = (const char *) lo_apkentry(aOFileName.getStr(), &size); if (contents != 0) { aContent = std::string(contents, size); return; } } #endif std::ifstream aFile(aOFileName.getStr()); OString aErrorMsg = "Could not open csv file: " + aOFileName; CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), aFile); std::ostringstream aOStream; aOStream << aFile.rdbuf(); aFile.close(); aContent = aOStream.str(); } void ScModelTestBase::testFile(const OUString& aFileName, ScDocument& rDoc, SCTAB nTab, StringType aStringFormat) { csv_handler aHandler(&rDoc, nTab, aStringFormat); orcus::csv::parser_config aConfig; aConfig.delimiters.push_back(','); aConfig.delimiters.push_back(';'); aConfig.text_qualifier = '"'; aConfig.trim_cell_value = false; std::string aContent; loadFile(aFileName, aContent); orcus::csv_parser parser(aContent, aHandler, aConfig); try { parser.parse(); } catch (const orcus::parse_error& e) { std::cout << "reading csv content file failed: " << e.what() << std::endl; OString aErrorMsg = OString::Concat("csv parser error: ") + e.what(); CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false); } } void ScModelTestBase::testCondFile( const OUString& aFileName, ScDocument* pDoc, SCTAB nTab, bool bCommaAsDelimiter ) { conditional_format_handler aHandler(pDoc, nTab); orcus::csv::parser_config aConfig; if ( bCommaAsDelimiter ) aConfig.delimiters.push_back(','); aConfig.delimiters.push_back(';'); aConfig.text_qualifier = '"'; std::string aContent; loadFile(aFileName, aContent); orcus::csv_parser parser(aContent, aHandler, aConfig); try { parser.parse(); } catch (const orcus::parse_error& e) { std::cout << "reading csv content file failed: " << e.what() << std::endl; OString aErrorMsg = OString::Concat("csv parser error: ") + e.what(); CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false); } } void ScModelTestBase::testFormats(ScDocument* pDoc,std::u16string_view sFormat) { //test Sheet1 with csv file OUString aCSVFileName = createFilePath(u"contentCSV/numberFormat.csv"); testFile(aCSVFileName, *pDoc, 0, StringType::PureString); //need to test the color of B3 //it's not a font color! //formatting for B5: # ??/100 gets lost during import //test Sheet2 const ScPatternAttr* pPattern = pDoc->GetPattern(0, 0, 1); vcl::Font aFont; model::ComplexColor aComplexColor; pPattern->fillFontOnly(aFont); pPattern->fillColor(aComplexColor, ScAutoFontColorMode::Raw); CPPUNIT_ASSERT_EQUAL_MESSAGE("font size should be 10", tools::Long(200), aFont.GetFontSize().getHeight()); CPPUNIT_ASSERT_EQUAL_MESSAGE("font color should be black", COL_AUTO, aComplexColor.getFinalColor()); pPattern = pDoc->GetPattern(0,1,1); pPattern->fillFontOnly(aFont); CPPUNIT_ASSERT_EQUAL_MESSAGE("font size should be 12", tools::Long(240), aFont.GetFontSize().getHeight()); pPattern = pDoc->GetPattern(0,2,1); pPattern->fillFontOnly(aFont); CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be italic", ITALIC_NORMAL, aFont.GetItalic()); pPattern = pDoc->GetPattern(0,4,1); pPattern->fillFontOnly(aFont); CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, aFont.GetWeight()); pPattern = pDoc->GetPattern(1,0,1); pPattern->fillFontOnly(aFont); pPattern->fillColor(aComplexColor, ScAutoFontColorMode::Raw); CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be blue", COL_BLUE, aComplexColor.getFinalColor()); pPattern = pDoc->GetPattern(1,1,1); pPattern->fillFontOnly(aFont); CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be striked out with a single line", STRIKEOUT_SINGLE, aFont.GetStrikeout()); //some tests on sheet2 only for ods if (sFormat == u"calc8") { pPattern = pDoc->GetPattern(1,2,1); pPattern->fillFontOnly(aFont); CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be striked out with a double line", STRIKEOUT_DOUBLE, aFont.GetStrikeout()); pPattern = pDoc->GetPattern(1,3,1); pPattern->fillFontOnly(aFont); CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be underlined with a dotted line", LINESTYLE_DOTTED, aFont.GetUnderline()); //check row height import //disable for now until we figure out cause of win tinderboxes test failures //CPPUNIT_ASSERT_EQUAL( static_cast(256), pDoc->GetRowHeight(0,1) ); //0.178in //CPPUNIT_ASSERT_EQUAL( static_cast(304), pDoc->GetRowHeight(1,1) ); //0.211in //CPPUNIT_ASSERT_EQUAL( static_cast(477), pDoc->GetRowHeight(5,1) ); //0.3311in //check column width import CPPUNIT_ASSERT_EQUAL( static_cast(555), pDoc->GetColWidth(4,1) ); //0.3854in CPPUNIT_ASSERT_EQUAL( static_cast(1280), pDoc->GetColWidth(5,1) ); //0.889in CPPUNIT_ASSERT_EQUAL( static_cast(4153), pDoc->GetColWidth(6,1) ); //2.8839in //test case for i53253 where a cell has text with different styles and space between the text. OUString aTestStr = pDoc->GetString(3,0,1); OUString aKnownGoodStr("text14 space"); CPPUNIT_ASSERT_EQUAL( aKnownGoodStr, aTestStr ); //test case for cell text with line breaks. aTestStr = pDoc->GetString(3,5,1); aKnownGoodStr = "Hello,\nCalc!"; CPPUNIT_ASSERT_EQUAL( aKnownGoodStr, aTestStr ); } pPattern = pDoc->GetPattern(1,4,1); Color aColor = pPattern->GetItem(ATTR_BACKGROUND).GetColor(); CPPUNIT_ASSERT_EQUAL_MESSAGE("background color should be green", COL_LIGHTGREEN, aColor); pPattern = pDoc->GetPattern(2,0,1); SvxCellHorJustify eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue(); CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify); //test alignment pPattern = pDoc->GetPattern(2,1,1); eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue(); CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned right horizontally", SvxCellHorJustify::Right, eHorJustify); pPattern = pDoc->GetPattern(2,2,1); eHorJustify = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue(); CPPUNIT_ASSERT_EQUAL_MESSAGE("cell content should be aligned block horizontally", SvxCellHorJustify::Block, eHorJustify); //test Sheet3 only for ods and xlsx if ( sFormat == u"calc8" || sFormat == u"Calc Office Open XML" ) { aCSVFileName = createFilePath(u"contentCSV/conditionalFormatting.csv"); testCondFile(aCSVFileName, pDoc, 2); // test parent cell style import ( fdo#55198 ) if ( sFormat == u"Calc Office Open XML" ) { pPattern = pDoc->GetPattern(1,1,3); ScStyleSheet* pStyleSheet = const_cast(pPattern->GetStyleSheet()); // check parent style name OUString sExpected("Excel Built-in Date"); OUString sResult = pStyleSheet->GetName(); CPPUNIT_ASSERT_EQUAL_MESSAGE("parent style for Sheet4.B2 is 'Excel Built-in Date'", sExpected, sResult); // check align of style SfxItemSet& rItemSet = pStyleSheet->GetItemSet(); eHorJustify = rItemSet.Get( ATTR_HOR_JUSTIFY ).GetValue(); CPPUNIT_ASSERT_EQUAL_MESSAGE("'Excel Built-in Date' style should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify); // check date format ( should be just month e.g. 29 ) sResult =pDoc->GetString( 1,1,3 ); sExpected = "29"; CPPUNIT_ASSERT_EQUAL_MESSAGE("'Excel Built-in Date' style should just display month", sExpected, sResult ); // check actual align applied to cell, should be the same as // the style eHorJustify = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue(); CPPUNIT_ASSERT_EQUAL_MESSAGE("cell with 'Excel Built-in Date' style should be aligned centre horizontally", SvxCellHorJustify::Center, eHorJustify); } } ScConditionalFormat* pCondFormat = pDoc->GetCondFormat(0,0,2); const ScRangeList& rRange = pCondFormat->GetRange(); CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(0,0,2,3,0,2)), rRange); pCondFormat = pDoc->GetCondFormat(0,1,2); const ScRangeList& rRange2 = pCondFormat->GetRange(); CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(0,1,2,0,1,2)), rRange2); pCondFormat = pDoc->GetCondFormat(1,1,2); const ScRangeList& rRange3 = pCondFormat->GetRange(); CPPUNIT_ASSERT_EQUAL(ScRangeList(ScRange(1,1,2,3,1,2)), rRange3); } void ScModelTestBase::goToCell(const OUString& rCell) { uno::Sequence aArgs = comphelper::InitPropertySequence({ { "ToPoint", uno::Any(rCell) } }); dispatchCommand(mxComponent, ".uno:GoToCell", aArgs); } void ScModelTestBase::typeString(const std::u16string_view& rStr) { ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent); for (const char16_t c : rStr) { pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, c, 0); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, c, 0); Scheduler::ProcessEventsToIdle(); } } void ScModelTestBase::insertStringToCell(const OUString& rCell, const std::u16string_view& rStr) { goToCell(rCell); typeString(rStr); ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN); Scheduler::ProcessEventsToIdle(); } void ScModelTestBase::insertArrayToCell(const OUString& rCell, const std::u16string_view& rStr) { goToCell(rCell); typeString(rStr); ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_MOD1 | KEY_SHIFT | awt::Key::RETURN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_MOD1 | KEY_SHIFT | awt::Key::RETURN); Scheduler::ProcessEventsToIdle(); } void ScModelTestBase::insertNewSheet(ScDocument& rDoc) { sal_Int32 nTabs = static_cast(rDoc.GetTableCount()); uno::Sequence aArgs(comphelper::InitPropertySequence( { { "Name", uno::Any(OUString("NewTab")) }, { "Index", uno::Any(nTabs + 1) } })); dispatchCommand(mxComponent, ".uno:Insert", aArgs); CPPUNIT_ASSERT_EQUAL(static_cast(nTabs + 1), rDoc.GetTableCount()); } void ScModelTestBase::executeAutoSum() { dispatchCommand(mxComponent, ".uno:AutoSum", {}); // Use RETURN key to exit autosum edit view ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN); pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN); Scheduler::ProcessEventsToIdle(); } const SdrOle2Obj* ScModelTestBase::getSingleOleObject(ScDocument& rDoc, sal_uInt16 nPage) { // Retrieve the chart object instance from the 2nd page (for the 2nd sheet). ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); if (!pDrawLayer) { cout << "Failed to retrieve the drawing layer object." << endl; return nullptr; } const SdrPage* pPage = pDrawLayer->GetPage(nPage); if (!pPage) { cout << "Failed to retrieve the page object." << endl; return nullptr; } if (pPage->GetObjCount() != 1) { cout << "This page should contain one drawing object." << endl; return nullptr; } const SdrObject* pObj = pPage->GetObj(0); if (!pObj) { cout << "Failed to retrieve the drawing object." << endl; return nullptr; } if (pObj->GetObjIdentifier() != SdrObjKind::OLE2) { cout << "This is not an OLE2 object." << endl; return nullptr; } return static_cast(pObj); } const SdrOle2Obj* ScModelTestBase::getSingleChartObject(ScDocument& rDoc, sal_uInt16 nPage) { const SdrOle2Obj* pObj = getSingleOleObject(rDoc, nPage); if (!pObj) return pObj; if (!pObj->IsChart()) { cout << "This should be a chart object." << endl; return nullptr; } return pObj; } static std::vector getChartRangeRepresentations(const SdrOle2Obj& rChartObj) { std::vector aRangeReps; // Make sure the chart object has correct range references. Reference xModel = rChartObj.getXModel(); if (!xModel.is()) { cout << "Failed to get the embedded object interface." << endl; return aRangeReps; } Reference xChartDoc(xModel, UNO_QUERY); if (!xChartDoc.is()) { cout << "Failed to get the chart document interface." << endl; return aRangeReps; } Reference xDataSource(xChartDoc, UNO_QUERY); if (!xDataSource.is()) { cout << "Failed to get the data source interface." << endl; return aRangeReps; } Sequence > xDataSeqs = xDataSource->getDataSequences(); if (!xDataSeqs.hasElements()) { cout << "There should be at least one data sequences." << endl; return aRangeReps; } Reference xDataRec(xChartDoc, UNO_QUERY); if (!xDataRec.is()) { cout << "Failed to get the data receiver interface." << endl; return aRangeReps; } Sequence aRangeRepSeqs = xDataRec->getUsedRangeRepresentations(); comphelper::sequenceToContainer(aRangeReps, aRangeRepSeqs); return aRangeReps; } ScRangeList ScModelTestBase::getChartRanges(ScDocument& rDoc, const SdrOle2Obj& rChartObj) { std::vector aRangeReps = getChartRangeRepresentations(rChartObj); ScRangeList aRanges; for (size_t i = 0, n = aRangeReps.size(); i < n; ++i) { ScRange aRange; ScRefFlags nRes = aRange.Parse(aRangeReps[i], rDoc, rDoc.GetAddressConvention()); if (nRes & ScRefFlags::VALID) // This is a range address. aRanges.push_back(aRange); else { // Parse it as a single cell address. ScAddress aAddr; nRes = aAddr.Parse(aRangeReps[i], rDoc, rDoc.GetAddressConvention()); CPPUNIT_ASSERT_MESSAGE("Failed to parse a range representation.", (nRes & ScRefFlags::VALID)); aRanges.push_back(aAddr); } } return aRanges; } bool checkOutput( const ScDocument* pDoc, const ScRange& aOutRange, const std::vector>& aCheck, const char* pCaption ) { bool bResult = true; const ScAddress& s = aOutRange.aStart; const ScAddress& e = aOutRange.aEnd; svl::GridPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1, CALC_DEBUG_OUTPUT != 0); SCROW nOutRowSize = e.Row() - s.Row() + 1; SCCOL nOutColSize = e.Col() - s.Col() + 1; // Check if expected size iz smaller than actual size (and prevent a crash) if (aCheck.size() < o3tl::make_unsigned(nOutRowSize) || aCheck[0].size() < o3tl::make_unsigned(nOutColSize)) { // Dump the arrays to console, so we can compare std::cout << "Expected data:" << std::endl; for (size_t nRow = 0; nRow < aCheck.size(); ++nRow) { for (size_t nCol = 0; nCol < aCheck[nRow].size(); ++nCol) { const char* p = aCheck[nRow][nCol]; if (p) { OUString aCheckVal = OUString::createFromAscii(p); std::cout << "'" << aCheckVal << "', "; } else std::cout << "null, "; } std::cout << std::endl; } std::cout << "Actual data:" << std::endl; for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow) { for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol) { OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab()); std::cout << "'" << aVal << "', "; } std::cout << std::endl; } std::cout << std::endl; return false; } for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow) { for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol) { OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab()); printer.set(nRow, nCol, aVal); const char* p = aCheck[nRow][nCol]; if (p) { OUString aCheckVal = OUString::createFromAscii(p); bool bEqual = aCheckVal == aVal; if (!bEqual) { std::cout << "Expected: " << aCheckVal << " Actual: " << aVal << std::endl; bResult = false; } } else if (!aVal.isEmpty()) { std::cout << "Empty cell expected" << std::endl; bResult = false; } } } printer.print(pCaption); return bResult; } void ScUcalcTestBase::setUp() { BootstrapFixture::setUp(); ScDLL::Init(); m_xDocShell = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS | SfxModelFlags::DISABLE_DOCUMENT_RECOVERY); m_xDocShell->DoInitUnitTest(); m_pDoc = &m_xDocShell->GetDocument(); } void ScUcalcTestBase::tearDown() { m_xDocShell->DoClose(); m_xDocShell.clear(); test::BootstrapFixture::tearDown(); } void ScModelTestBase::createScDoc(const char* pName, const char* pPassword, bool bCheckWarningError) { if (!pName) load("private:factory/scalc"); else loadFromFile(OUString::createFromAscii(pName), pPassword); uno::Reference xServiceInfo(mxComponent, uno::UNO_QUERY_THROW); CPPUNIT_ASSERT(xServiceInfo->supportsService("com.sun.star.sheet.SpreadsheetDocument")); if (bCheckWarningError) CPPUNIT_ASSERT(!getScDocShell()->GetMedium()->GetWarningError()); } ScDocument* ScModelTestBase::getScDoc() { ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent); CPPUNIT_ASSERT(pModelObj); return pModelObj->GetDocument(); } ScDocument* ScModelTestBase::getScDoc2() { ScModelObj* pModelObj = comphelper::getFromUnoTunnel(mxComponent2); CPPUNIT_ASSERT(pModelObj); return pModelObj->GetDocument(); } ScDocShell* ScModelTestBase::getScDocShell() { SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(mxComponent); CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell); ScDocShell* pDocSh = dynamic_cast(pFoundShell); CPPUNIT_ASSERT(pDocSh); return pDocSh; } ScTabViewShell* ScModelTestBase::getViewShell() { ScDocShell* pDocSh = getScDocShell(); ScTabViewShell* pTabViewShell = pDocSh->GetBestViewShell(false); CPPUNIT_ASSERT_MESSAGE("No ScTabViewShell", pTabViewShell); return pTabViewShell; } void ScModelTestBase::miscRowHeightsTest( TestParam const * aTestValues, unsigned int numElems) { for ( unsigned int index=0; indexGetRowHeight(nRow, nTab, false)); if ( bCheckOpt ) { bool bOpt = !(pDoc->GetRowFlags( nRow, nTab ) & CRFlags::ManualSize); CPPUNIT_ASSERT_EQUAL(aTestValues[ index ].pData[ i ].bOptimal, bOpt); } CPPUNIT_ASSERT_EQUAL(nExpectedHeight, nHeight); } } } } void ScModelTestBase::enableOpenCL() { /** * Turn on OpenCL group interpreter. Call this after the document is * loaded and before performing formula calculation. */ sc::FormulaGroupInterpreter::enableOpenCL_UnitTestsOnly(); } void ScModelTestBase::disableOpenCL() { sc::FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly(); } void ScModelTestBase::initTestEnv(std::u16string_view fileName) { // Some documents contain macros, disable them, otherwise // the "Error, BASIC runtime error." dialog is prompted // and it crashes in tearDown std::vector args; beans::PropertyValue aMacroValue; aMacroValue.Name = "MacroExecutionMode"; aMacroValue.Handle = -1; aMacroValue.Value <<= document::MacroExecMode::NEVER_EXECUTE; aMacroValue.State = beans::PropertyState_DIRECT_VALUE; args.push_back(aMacroValue); disableOpenCL(); CPPUNIT_ASSERT(!ScCalcConfig::isOpenCLEnabled()); // Open the document with OpenCL disabled mxComponent = mxDesktop->loadComponentFromURL( createFileURL(fileName), "_default", 0, comphelper::containerToSequence(args)); enableOpenCL(); CPPUNIT_ASSERT(ScCalcConfig::isOpenCLEnabled()); // it's not possible to open the same document twice, thus, create a temp file createTempCopy(fileName); // Open the document with OpenCL enabled mxComponent2 = mxDesktop->loadComponentFromURL( maTempFile.GetURL(), "_default", 0, comphelper::containerToSequence(args)); // Check there are 2 documents uno::Reference xFrames = mxDesktop->getFrames(); CPPUNIT_ASSERT_EQUAL(static_cast(2), xFrames->getCount()); } ScRange ScUcalcTestBase::insertRangeData( ScDocument* pDoc, const ScAddress& rPos, const std::vector>& rData ) { if (rData.empty()) return ScRange(ScAddress::INITIALIZE_INVALID); ScAddress aPos = rPos; SCCOL nColWidth = 1; for (const std::vector& rRow : rData) nColWidth = std::max(nColWidth, rRow.size()); ScRange aRange(aPos); aRange.aEnd.IncCol(nColWidth-1); aRange.aEnd.IncRow(rData.size()-1); clearRange(pDoc, aRange); for (const std::vector& rRow : rData) { aPos.SetCol(rPos.Col()); for (const char* pStr : rRow) { if (!pStr) { aPos.IncCol(); continue; } OUString aStr(pStr, strlen(pStr), RTL_TEXTENCODING_UTF8); ScSetStringParam aParam; // Leave default. aParam.meStartListening = sc::NoListening; pDoc->SetString(aPos, aStr, &aParam); aPos.IncCol(); } aPos.IncRow(); } pDoc->StartAllListeners(aRange); printRange(pDoc, aRange, "Range data content"); return aRange; } ScUndoCut* ScUcalcTestBase::cutToClip(ScDocShell& rDocSh, const ScRange& rRange, ScDocument* pClipDoc, bool bCreateUndo) { ScDocument* pSrcDoc = &rDocSh.GetDocument(); ScClipParam aClipParam(rRange, true); ScMarkData aMark(pSrcDoc->GetSheetLimits()); aMark.SetMarkArea(rRange); pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aMark, false, false); // Taken from ScViewFunc::CutToClip() ScDocumentUniquePtr pUndoDoc; if (bCreateUndo) { pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); pUndoDoc->InitUndoSelected( *pSrcDoc, aMark ); // all sheets - CopyToDocument skips those that don't exist in pUndoDoc ScRange aCopyRange = rRange; aCopyRange.aStart.SetTab(0); aCopyRange.aEnd.SetTab(pSrcDoc->GetTableCount()-1); pSrcDoc->CopyToDocument( aCopyRange, (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc ); } aMark.MarkToMulti(); pSrcDoc->DeleteSelection( InsertDeleteFlags::ALL, aMark ); aMark.MarkToSimple(); if (pUndoDoc) return new ScUndoCut( &rDocSh, rRange, rRange.aEnd, aMark, std::move(pUndoDoc) ); return nullptr; } void ScUcalcTestBase::copyToClip(ScDocument* pSrcDoc, const ScRange& rRange, ScDocument* pClipDoc) { ScClipParam aClipParam(rRange, false); ScMarkData aMark(pSrcDoc->GetSheetLimits()); aMark.SetMarkArea(rRange); pSrcDoc->CopyToClip(aClipParam, pClipDoc, &aMark, false, false); } void ScUcalcTestBase::pasteFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc) { ScMarkData aMark(pDestDoc->GetSheetLimits()); aMark.SetMarkArea(rDestRange); pDestDoc->CopyFromClip(rDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pClipDoc); } ScUndoPaste* ScUcalcTestBase::createUndoPaste(ScDocShell& rDocSh, const ScRange& rRange, ScDocumentUniquePtr pUndoDoc) { ScDocument& rDoc = rDocSh.GetDocument(); ScMarkData aMarkData(rDoc.GetSheetLimits()); aMarkData.SetMarkArea(rRange); std::unique_ptr pRefUndoData(new ScRefUndoData(&rDoc)); return new ScUndoPaste( &rDocSh, rRange, aMarkData, std::move(pUndoDoc), nullptr, InsertDeleteFlags::ALL, std::move(pRefUndoData), false); } void ScUcalcTestBase::pasteOneCellFromClip(ScDocument* pDestDoc, const ScRange& rDestRange, ScDocument* pClipDoc, InsertDeleteFlags eFlags) { ScMarkData aMark(pDestDoc->GetSheetLimits()); aMark.SetMarkArea(rDestRange); sc::CopyFromClipContext aCxt(*pDestDoc, nullptr, pClipDoc, eFlags, false, false); aCxt.setDestRange(rDestRange.aStart.Col(), rDestRange.aStart.Row(), rDestRange.aEnd.Col(), rDestRange.aEnd.Row()); aCxt.setTabRange(rDestRange.aStart.Tab(), rDestRange.aEnd.Tab()); pDestDoc->CopyOneCellFromClip(aCxt, rDestRange.aStart.Col(), rDestRange.aStart.Row(), rDestRange.aEnd.Col(), rDestRange.aEnd.Row()); } void ScUcalcTestBase::setCalcAsShown(ScDocument* pDoc, bool bCalcAsShown) { ScDocOptions aOpt = pDoc->GetDocOptions(); aOpt.SetCalcAsShown(bCalcAsShown); pDoc->SetDocOptions(aOpt); } ScDocShell* ScUcalcTestBase::findLoadedDocShellByName(std::u16string_view rName) { ScDocShell* pShell = static_cast(SfxObjectShell::GetFirst(checkSfxObjectShell, false)); while (pShell) { SfxMedium* pMedium = pShell->GetMedium(); if (pMedium) { OUString aName = pMedium->GetName(); if (aName == rName) return pShell; } pShell = static_cast(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell, false)); } return nullptr; } bool ScUcalcTestBase::insertRangeNames( ScDocument* pDoc, ScRangeName* pNames, const RangeNameDef* p, const RangeNameDef* pEnd) { ScAddress aA1(0, 0, 0); for (; p != pEnd; ++p) { ScRangeData* pNew = new ScRangeData( *pDoc, OUString::createFromAscii(p->mpName), OUString::createFromAscii(p->mpExpr), aA1, ScRangeData::Type::Name, formula::FormulaGrammar::GRAM_ENGLISH); pNew->SetIndex(p->mnIndex); bool bSuccess = pNames->insert(pNew); if (!bSuccess) { cerr << "Insertion failed." << endl; return false; } } return true; } OUString ScUcalcTestBase::getRangeByName(ScDocument* pDoc, const OUString& aRangeName) { ScRangeData* pName = pDoc->GetRangeName()->findByUpperName(aRangeName.toAsciiUpperCase()); CPPUNIT_ASSERT(pName); return pName->GetSymbol(pDoc->GetGrammar()); } #if CALC_DEBUG_OUTPUT != 0 void ScUcalcTestBase::printFormula(ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, const char* pCaption) { if (pCaption != nullptr) cout << pCaption << ", "; cout << nCol << "/" << nRow << ": " << pDoc->GetFormula(nCol, nRow, nTab); cout << endl; } #else // Avoid unused parameter warning void ScUcalcTestBase::printFormula(ScDocument*, SCCOL, SCROW, SCTAB, const char*) {} #endif void ScUcalcTestBase::printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption, const bool printFormula) { SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row(); SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col(); svl::GridPrinter printer(nRow2 - nRow1 + 1, nCol2 - nCol1 + 1, CALC_DEBUG_OUTPUT != 0); for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) { for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) { ScAddress aPos(nCol, nRow, rRange.aStart.Tab()); ScRefCellValue aCell(*pDoc, aPos); OUString aVal = printFormula ? pDoc->GetFormula(nCol, nRow, rRange.aStart.Tab()) : ScCellFormat::GetOutputString(*pDoc, aPos, aCell); printer.set(nRow - nRow1, nCol - nCol1, aVal); } } printer.print(pCaption); } void ScUcalcTestBase::printRange(ScDocument* pDoc, const ScRange& rRange, const OString& rCaption, const bool printFormula) { printRange(pDoc, rRange, rCaption.getStr(), printFormula); } void ScUcalcTestBase::clearRange(ScDocument* pDoc, const ScRange& rRange) { ScMarkData aMarkData(pDoc->GetSheetLimits()); aMarkData.SetMarkArea(rRange); pDoc->DeleteArea( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), aMarkData, InsertDeleteFlags::CONTENTS); } void ScUcalcTestBase::clearSheet(ScDocument* pDoc, SCTAB nTab) { ScRange aRange(0,0,nTab,pDoc->MaxCol(),pDoc->MaxRow(),nTab); clearRange(pDoc, aRange); } bool ScUcalcTestBase::checkFormulaPosition(ScDocument& rDoc, const ScAddress& rPos) { OUString aStr(rPos.Format(ScRefFlags::VALID)); const ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos); if (!pFC) { cerr << "Formula cell expected at " << aStr << " but not found." << endl; return false; } if (pFC->aPos != rPos) { OUString aStr2(pFC->aPos.Format(ScRefFlags::VALID)); cerr << "Formula cell at " << aStr << " has incorrect position of " << aStr2 << endl; return false; } return true; } bool ScUcalcTestBase::checkFormulaPositions( ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SCROW* pRows, size_t nRowCount) { ScAddress aPos(nCol, 0, nTab); for (size_t i = 0; i < nRowCount; ++i) { SCROW nRow = pRows[i]; aPos.SetRow(nRow); if (!checkFormulaPosition(rDoc, aPos)) { OUString aStr(aPos.Format(ScRefFlags::VALID)); cerr << "Formula cell position failed at " << aStr << "." << endl; return false; } } return true; } std::unique_ptr ScUcalcTestBase::compileFormula( ScDocument* pDoc, const OUString& rFormula, formula::FormulaGrammar::Grammar eGram ) { ScAddress aPos(0,0,0); ScCompiler aComp(*pDoc, aPos, eGram); return aComp.CompileString(rFormula); } void ScUcalcTestBase::clearFormulaCellChangedFlag( ScDocument& rDoc, const ScRange& rRange ) { const ScAddress& s = rRange.aStart; const ScAddress& e = rRange.aEnd; for (SCTAB nTab = s.Tab(); nTab <= e.Tab(); ++nTab) { for (SCCOL nCol = s.Col(); nCol <= e.Col(); ++nCol) { for (SCROW nRow = s.Row(); nRow <= e.Row(); ++nRow) { ScAddress aPos(nCol, nRow, nTab); ScFormulaCell* pFC = rDoc.GetFormulaCell(aPos); if (pFC) pFC->SetChanged(false); } } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */