summaryrefslogtreecommitdiffstats
path: root/sc/qa/unit/helper
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sc/qa/unit/helper
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/qa/unit/helper')
-rw-r--r--sc/qa/unit/helper/csv_handler.hxx198
-rw-r--r--sc/qa/unit/helper/debughelper.hxx32
-rw-r--r--sc/qa/unit/helper/qahelper.cxx999
-rw-r--r--sc/qa/unit/helper/qahelper.hxx215
-rw-r--r--sc/qa/unit/helper/scfiltertestbase.cxx67
-rw-r--r--sc/qa/unit/helper/scfiltertestbase.hxx68
-rw-r--r--sc/qa/unit/helper/scqahelperdllapi.h20
-rw-r--r--sc/qa/unit/helper/shared_test_impl.hxx298
-rw-r--r--sc/qa/unit/helper/sorthelper.hxx53
9 files changed, 1950 insertions, 0 deletions
diff --git a/sc/qa/unit/helper/csv_handler.hxx b/sc/qa/unit/helper/csv_handler.hxx
new file mode 100644
index 0000000000..4c59144c3b
--- /dev/null
+++ b/sc/qa/unit/helper/csv_handler.hxx
@@ -0,0 +1,198 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "qahelper.hxx"
+
+#include <patattr.hxx>
+#include <document.hxx>
+#include <cellform.hxx>
+#include <cellvalue.hxx>
+
+#include <orcus/csv_parser.hpp>
+
+#define DEBUG_CSV_HANDLER 0
+
+inline OUString getConditionalFormatString(ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ OUString aString;
+ const Color* pColor;
+ ScRefCellValue aCell(*pDoc, ScAddress(nCol, nRow, nTab));
+ if (aCell.isEmpty())
+ return aString;
+
+ const SfxItemSet* pCondSet = pDoc->GetCondResult( nCol, nRow, nTab );
+ const ScPatternAttr* pPattern = pDoc->GetPattern(nCol, nRow, nTab);
+ SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
+ sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
+ aString = ScCellFormat::GetString(*pDoc, ScAddress(nCol, nRow, nTab), nFormat, &pColor, *pFormatter);
+ return aString;
+}
+
+inline OString createErrorMessage(SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ return "Error in Table: " +
+ OString::number(static_cast<sal_Int32>(nTab)) +
+ " Column: " +
+ OString::number(static_cast<sal_Int32>(nCol)) +
+ " Row: " +
+ OString::number(nRow);
+}
+
+inline OString createErrorMessage(SCCOL nCol, SCROW nRow, SCTAB nTab, std::u16string_view rExpectedString, std::u16string_view rString)
+{
+ return createErrorMessage(nCol, nRow, nTab) + "; Expected: '"
+ + OUStringToOString(rExpectedString, RTL_TEXTENCODING_UTF8) + "' Found: '"
+ + OUStringToOString(rString, RTL_TEXTENCODING_UTF8) + "'";
+}
+
+inline OString createErrorMessage(SCCOL nCol, SCROW nRow, SCTAB nTab, double aExpected, double aValue)
+{
+ return createErrorMessage(nCol, nRow, nTab) + "; Expected: '" + OString::number(aExpected)
+ + "' Found: '" + OString::number(aValue) + "'";
+
+}
+
+class csv_handler : public orcus::csv_handler
+{
+public:
+ csv_handler(ScDocument* pDoc, SCTAB nTab, StringType eType):
+ mpDoc(pDoc),
+ mnCol(0),
+ mnRow(0),
+ mnTab(nTab),
+ meStringType(eType) {}
+
+ void end_row()
+ {
+ ++mnRow;
+ mnCol = 0;
+ }
+
+ void cell(std::string_view value, bool /*transient*/)
+ {
+ const char* p = value.data();
+ std::size_t n = value.size();
+
+#if DEBUG_CSV_HANDLER
+ std::cout << "Col: " << mnCol << " Row: " << mnRow << std::endl;
+#endif //DEBUG_CSV_HANDLER
+ if (n == 0)
+ {
+ // Empty cell.
+ if (!mpDoc->GetString(mnCol, mnRow, mnTab).isEmpty())
+ {
+ // cell in the document is not empty.
+ CPPUNIT_ASSERT_MESSAGE(createErrorMessage(mnCol, mnRow, mnTab).getStr(), false);
+ }
+ }
+ else if (meStringType == StringType::PureString)
+ {
+ OUString aCSVString(p, n, RTL_TEXTENCODING_UTF8);
+ OUString aString = mpDoc->GetString(mnCol, mnRow, mnTab);
+
+#if DEBUG_CSV_HANDLER
+ std::cout << "String: " << OUStringToOString(aString, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
+ std::cout << "CSVString: " << OUStringToOString(aCSVString, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
+ std::cout << "result: " << (int)(aCSVString == aString) << std::endl;
+#endif //DEBUG_CSV_HANDLER
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(createErrorMessage(mnCol, mnRow, mnTab, aCSVString, aString).getStr(), aCSVString, aString);
+ }
+ else
+ {
+ char* pRemainingChars = nullptr;
+ std::string aStr(p, n);
+ double nValue = strtod(&aStr[0], &pRemainingChars);
+ if (*pRemainingChars)
+ {
+ OUString aString;
+ switch (meStringType)
+ {
+ case StringType::StringValue:
+ aString = mpDoc->GetString(mnCol, mnRow, mnTab);
+ break;
+ default:
+ break;
+ }
+ OUString aCSVString(p, n, RTL_TEXTENCODING_UTF8);
+#if DEBUG_CSV_HANDLER
+ std::cout << "String: " << OUStringToOString(aString, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
+ std::cout << "CSVString: " << OUStringToOString(aCSVString, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
+ std::cout << "result: " << (int)(aCSVString == aString) << std::endl;
+#endif //DEBUG_CSV_HANDLER
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(createErrorMessage(mnCol, mnRow, mnTab, aCSVString, aString).getStr(), aCSVString, aString);
+ }
+ else
+ {
+ double aValue = mpDoc->GetValue(mnCol, mnRow, mnTab);
+#if DEBUG_CSV_HANDLER
+ std::cout << "Value: " << aValue << std::endl;
+ std::cout << "CSVValue: " << nValue << std::endl;
+ std::cout << "result: " << (int)(aValue == nValue) << std::endl;
+#endif //DEBUG_CSV_HANDLER
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(createErrorMessage(mnCol, mnRow, mnTab, nValue, aValue).getStr(), nValue, aValue, 1e-10);
+ }
+ }
+ ++mnCol;
+ }
+
+private:
+ ScDocument* mpDoc;
+ SCCOL mnCol;
+ SCROW mnRow;
+ SCTAB mnTab;
+ StringType meStringType;
+};
+
+class conditional_format_handler : public orcus::csv_handler
+{
+public:
+ conditional_format_handler(ScDocument* pDoc, SCTAB nTab):
+ mpDoc(pDoc),
+ mnCol(0),
+ mnRow(0),
+ mnTab(nTab) {}
+
+ void end_row()
+ {
+ ++mnRow;
+ mnCol = 0;
+ }
+
+ void cell(std::string_view value, bool /*transient*/)
+ {
+#if DEBUG_CSV_HANDLER
+ std::cout << "Col: " << mnCol << " Row: " << mnRow << std::endl;
+#endif //DEBUG_CSV_HANDLER
+ OUString aString = getConditionalFormatString(mpDoc, mnCol, mnRow, mnTab);
+ OUString aCSVString(value.data(), value.size(), RTL_TEXTENCODING_UTF8);
+#if DEBUG_CSV_HANDLER
+ std::cout << "String: " << OUStringToOString(aString, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
+ std::cout << "CSVString: " << OUStringToOString(aCSVString, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
+ std::cout << "result: " << (int)(aCSVString == aString) << std::endl;
+#endif //DEBUG_CSV_HANDLER
+ CPPUNIT_ASSERT_EQUAL_MESSAGE(createErrorMessage(mnCol, mnRow, mnTab, aCSVString, aString).getStr(), aCSVString, aString );
+ ++mnCol;
+ }
+
+private:
+ ScDocument* mpDoc;
+ SCCOL mnCol;
+ SCROW mnRow;
+ SCTAB mnTab;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/helper/debughelper.hxx b/sc/qa/unit/helper/debughelper.hxx
new file mode 100644
index 0000000000..47d0e25e08
--- /dev/null
+++ b/sc/qa/unit/helper/debughelper.hxx
@@ -0,0 +1,32 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#ifdef _WIN32
+#if !defined NOMINMAX
+#define NOMINMAX
+#endif
+#include <prewin.h>
+#include <postwin.h>
+#undef NOMINMAX
+#undef RGB
+#endif
+
+#include <iostream>
+#include <vector>
+
+#define CALC_DEBUG_OUTPUT 0
+#define CALC_TEST_PERF 0
+
+#include <sal/types.h>
+
+using namespace ::com::sun::star;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/helper/qahelper.cxx b/sc/qa/unit/helper/qahelper.cxx
new file mode 100644
index 0000000000..4dc09a65de
--- /dev/null
+++ b/sc/qa/unit/helper/qahelper.cxx
@@ -0,0 +1,999 @@
+/* -*- 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 <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/propertysequence.hxx>
+#include "csv_handler.hxx"
+#include "debughelper.hxx"
+#include <drwlayer.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <compiler.hxx>
+#include <conditio.hxx>
+#include <stlsheet.hxx>
+#include <formulacell.hxx>
+#include <formulagroup.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdoole2.hxx>
+#include <tools/UnitConversion.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <clipcontext.hxx>
+#include <clipparam.hxx>
+#include <refundo.hxx>
+#include <sal/log.hxx>
+#include <svl/gridprinter.hxx>
+#include <sfx2/docfile.hxx>
+#include <undoblk.hxx>
+#include <scdll.hxx>
+#include <scitems.hxx>
+#include <stringutil.hxx>
+#include <tokenarray.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/scheduler.hxx>
+#include <o3tl/safeint.hxx>
+
+#include <orcus/csv_parser.hpp>
+
+#include <cstdlib>
+#include <fstream>
+
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/chart2/data/XDataReceiver.hpp>
+#include <com/sun/star/document/MacroExecMode.hpp>
+
+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<sal_uInt16>(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<csv_handler> 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<conditional_format_handler> 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<sal_uInt16>(256), pDoc->GetRowHeight(0,1) ); //0.178in
+ //CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(304), pDoc->GetRowHeight(1,1) ); //0.211in
+ //CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(477), pDoc->GetRowHeight(5,1) ); //0.3311in
+ //check column width import
+ CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(555), pDoc->GetColWidth(4,1) ); //0.3854in
+ CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(1280), pDoc->GetColWidth(5,1) ); //0.889in
+ CPPUNIT_ASSERT_EQUAL( static_cast<sal_uInt16>(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<ScStyleSheet*>(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<beans::PropertyValue> aArgs
+ = comphelper::InitPropertySequence({ { "ToPoint", uno::Any(rCell) } });
+ dispatchCommand(mxComponent, ".uno:GoToCell", aArgs);
+}
+
+void ScModelTestBase::typeString(const std::u16string_view& rStr)
+{
+ ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(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<ScModelObj>(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<ScModelObj>(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<sal_Int32>(rDoc.GetTableCount());
+
+ uno::Sequence<beans::PropertyValue> aArgs(comphelper::InitPropertySequence(
+ { { "Name", uno::Any(OUString("NewTab")) }, { "Index", uno::Any(nTabs + 1) } }));
+ dispatchCommand(mxComponent, ".uno:Insert", aArgs);
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(nTabs + 1), rDoc.GetTableCount());
+}
+
+void ScModelTestBase::executeAutoSum()
+{
+ dispatchCommand(mxComponent, ".uno:AutoSum", {});
+
+ // Use RETURN key to exit autosum edit view
+ ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(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<const SdrOle2Obj*>(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<OUString> getChartRangeRepresentations(const SdrOle2Obj& rChartObj)
+{
+ std::vector<OUString> aRangeReps;
+
+ // Make sure the chart object has correct range references.
+ Reference<frame::XModel> xModel = rChartObj.getXModel();
+ if (!xModel.is())
+ {
+ cout << "Failed to get the embedded object interface." << endl;
+ return aRangeReps;
+ }
+
+ Reference<chart2::XChartDocument> xChartDoc(xModel, UNO_QUERY);
+ if (!xChartDoc.is())
+ {
+ cout << "Failed to get the chart document interface." << endl;
+ return aRangeReps;
+ }
+
+ Reference<chart2::data::XDataSource> xDataSource(xChartDoc, UNO_QUERY);
+ if (!xDataSource.is())
+ {
+ cout << "Failed to get the data source interface." << endl;
+ return aRangeReps;
+ }
+
+ Sequence<Reference<chart2::data::XLabeledDataSequence> > xDataSeqs = xDataSource->getDataSequences();
+ if (!xDataSeqs.hasElements())
+ {
+ cout << "There should be at least one data sequences." << endl;
+ return aRangeReps;
+ }
+
+ Reference<chart2::data::XDataReceiver> xDataRec(xChartDoc, UNO_QUERY);
+ if (!xDataRec.is())
+ {
+ cout << "Failed to get the data receiver interface." << endl;
+ return aRangeReps;
+ }
+
+ Sequence<OUString> aRangeRepSeqs = xDataRec->getUsedRangeRepresentations();
+ comphelper::sequenceToContainer(aRangeReps, aRangeRepSeqs);
+
+ return aRangeReps;
+}
+
+ScRangeList ScModelTestBase::getChartRanges(ScDocument& rDoc, const SdrOle2Obj& rChartObj)
+{
+ std::vector<OUString> 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<std::vector<const char*>>& 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<lang::XServiceInfo> 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<ScModelObj>(mxComponent);
+ CPPUNIT_ASSERT(pModelObj);
+ return pModelObj->GetDocument();
+}
+
+ScDocument* ScModelTestBase::getScDoc2()
+{
+ ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>(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<ScDocShell*>(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; index<numElems; ++index )
+ {
+ const std::u16string_view sFileName = aTestValues[ index ].sTestDoc;
+ const OUString sExportType = aTestValues[ index ].sExportType;
+ loadFromFile(sFileName);
+
+ if ( !sExportType.isEmpty() )
+ saveAndReload(sExportType);
+
+ ScDocument* pDoc = getScDoc();
+
+ for (int i=0; i<aTestValues[ index ].nRowData; ++i)
+ {
+ SCROW nRow = aTestValues[ index ].pData[ i].nStartRow;
+ SCROW nEndRow = aTestValues[ index ].pData[ i ].nEndRow;
+ SCTAB nTab = aTestValues[ index ].pData[ i ].nTab;
+ int nExpectedHeight = aTestValues[ index ].pData[ i ].nExpectedHeight;
+ if ( nExpectedHeight == -1 )
+ nExpectedHeight = convertTwipToMm100(ScGlobal::GetStandardRowHeight());
+ bool bCheckOpt = ( ( aTestValues[ index ].pData[ i ].nCheck & CHECK_OPTIMAL ) == CHECK_OPTIMAL );
+ for ( ; nRow <= nEndRow; ++nRow )
+ {
+ SAL_INFO( "sc.qa", " checking row " << nRow << " for height " << nExpectedHeight );
+ int nHeight = convertTwipToMm100(pDoc->GetRowHeight(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<beans::PropertyValue> 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<frame::XFrames> xFrames = mxDesktop->getFrames();
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), xFrames->getCount());
+}
+
+ScRange ScUcalcTestBase::insertRangeData(
+ ScDocument* pDoc, const ScAddress& rPos, const std::vector<std::vector<const char*>>& rData )
+{
+ if (rData.empty())
+ return ScRange(ScAddress::INITIALIZE_INVALID);
+
+ ScAddress aPos = rPos;
+
+ SCCOL nColWidth = 1;
+ for (const std::vector<const char*>& rRow : rData)
+ nColWidth = std::max<SCCOL>(nColWidth, rRow.size());
+
+ ScRange aRange(aPos);
+ aRange.aEnd.IncCol(nColWidth-1);
+ aRange.aEnd.IncRow(rData.size()-1);
+
+ clearRange(pDoc, aRange);
+
+ for (const std::vector<const char*>& 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<ScRefUndoData> 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<ScDocShell*>(SfxObjectShell::GetFirst(checkSfxObjectShell<ScDocShell>, false));
+ while (pShell)
+ {
+ SfxMedium* pMedium = pShell->GetMedium();
+ if (pMedium)
+ {
+ OUString aName = pMedium->GetName();
+ if (aName == rName)
+ return pShell;
+ }
+ pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, checkSfxObjectShell<ScDocShell>, 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<ScTokenArray> 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: */
diff --git a/sc/qa/unit/helper/qahelper.hxx b/sc/qa/unit/helper/qahelper.hxx
new file mode 100644
index 0000000000..4b95451c96
--- /dev/null
+++ b/sc/qa/unit/helper/qahelper.hxx
@@ -0,0 +1,215 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <docsh.hxx>
+#include <address.hxx>
+
+#include <cppunit/SourceLine.h>
+
+#include <test/unoapixml_test.hxx>
+#include <test/bootstrapfixture.hxx>
+#include <comphelper/documentconstants.hxx>
+
+#include <comphelper/fileformat.h>
+#include <formula/grammar.hxx>
+#include "scqahelperdllapi.h"
+
+#include <string>
+#include <string_view>
+#include <sstream>
+#include <undoblk.hxx>
+
+#include <sal/types.h>
+
+#include <memory>
+#include <tuple>
+
+enum class StringType { PureString, StringValue };
+
+#define CHECK_OPTIMAL 0x1
+
+class SdrOle2Obj;
+class ScRangeList;
+class ScTokenArray;
+
+// data format for row height tests
+struct TestParam
+{
+ struct RowData
+ {
+ SCROW nStartRow;
+ SCROW nEndRow;
+ SCTAB nTab;
+ int nExpectedHeight; // -1 for default height
+ int nCheck; // currently only CHECK_OPTIMAL ( we could add CHECK_MANUAL etc.)
+ bool bOptimal;
+ };
+ const std::u16string_view sTestDoc;
+ const OUString sExportType; // empty for import test, otherwise this is an export test
+ int nRowData;
+ RowData const * pData;
+};
+
+struct RangeNameDef
+{
+ const char* mpName;
+ const char* mpExpr;
+ sal_uInt16 mnIndex;
+};
+
+// Printers for the calc data structures. Needed for the EQUAL assertion
+// macros from CPPUNIT.
+
+SCQAHELPER_DLLPUBLIC std::ostream& operator<<(std::ostream& rStrm, const ScAddress& rAddr);
+
+SCQAHELPER_DLLPUBLIC std::ostream& operator<<(std::ostream& rStrm, const ScRange& rRange);
+
+SCQAHELPER_DLLPUBLIC std::ostream& operator<<(std::ostream& rStrm, const ScRangeList& rList);
+
+SCQAHELPER_DLLPUBLIC std::ostream& operator<<(std::ostream& rStrm, const OpCode& rCode);
+
+SCQAHELPER_DLLPUBLIC bool checkOutput(
+ const ScDocument* pDoc, const ScRange& aOutRange,
+ const std::vector<std::vector<const char*>>& aCheck, const char* pCaption );
+
+inline std::string print(const ScAddress& rAddr)
+{
+ std::ostringstream str;
+ str << "Col: " << rAddr.Col();
+ str << " Row: " << rAddr.Row();
+ str << " Tab: " << rAddr.Tab();
+ return str.str();
+}
+
+/**
+ * Temporarily set formula grammar.
+ */
+class SCQAHELPER_DLLPUBLIC FormulaGrammarSwitch
+{
+ ScDocument* mpDoc;
+ formula::FormulaGrammar::Grammar meOldGrammar;
+
+public:
+ FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar);
+ ~FormulaGrammarSwitch();
+};
+
+class SCQAHELPER_DLLPUBLIC ScUcalcTestBase : public test::BootstrapFixture
+{
+public:
+ virtual void setUp() override;
+ virtual void tearDown() override;
+
+ ScRange insertRangeData(ScDocument* pDoc, const ScAddress& rPos,
+ const std::vector<std::vector<const char*>>& rData);
+ void copyToClip(ScDocument* pSrcDoc, const ScRange& rRange, ScDocument* pClipDoc);
+ void pasteFromClip(ScDocument* pDestDoc, const ScRange& rDestRange,
+ ScDocument* pClipDoc);
+ ScUndoPaste* createUndoPaste(ScDocShell& rDocSh, const ScRange& rRange,
+ ScDocumentUniquePtr pUndoDoc);
+ void pasteOneCellFromClip(ScDocument* pDestDoc, const ScRange& rDestRange,
+ ScDocument* pClipDoc,
+ InsertDeleteFlags eFlags = InsertDeleteFlags::ALL);
+ void setCalcAsShown(ScDocument* pDoc, bool bCalcAsShown);
+ ScDocShell* findLoadedDocShellByName(std::u16string_view rName);
+ ScUndoCut* cutToClip(ScDocShell& rDocSh, const ScRange& rRange, ScDocument* pClipDoc,
+ bool bCreateUndo);
+ bool insertRangeNames(ScDocument* pDoc, ScRangeName* pNames, const RangeNameDef* p,
+ const RangeNameDef* pEnd);
+ OUString getRangeByName(ScDocument* pDoc, const OUString& aRangeName);
+ void printFormula(ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const char* pCaption = nullptr);
+ void printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption,
+ const bool printFormula = false);
+ void printRange(ScDocument* pDoc, const ScRange& rRange,
+ const OString& rCaption, const bool printFormula = false);
+ void clearRange(ScDocument* pDoc, const ScRange& rRange);
+ void clearSheet(ScDocument* pDoc, SCTAB nTab);
+ bool checkFormulaPosition(ScDocument& rDoc, const ScAddress& rPos);
+ bool checkFormulaPositions(
+ ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SCROW* pRows, size_t nRowCount);
+ std::unique_ptr<ScTokenArray> compileFormula(
+ ScDocument* pDoc, const OUString& rFormula,
+ formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_NATIVE );
+ void clearFormulaCellChangedFlag( ScDocument& rDoc, const ScRange& rRange );
+
+protected:
+ ScDocShellRef m_xDocShell;
+ ScDocument* m_pDoc;
+};
+
+class SCQAHELPER_DLLPUBLIC ScModelTestBase : public UnoApiXmlTest
+{
+public:
+ ScModelTestBase(OUString path)
+ : UnoApiXmlTest(path)
+ {
+ }
+
+ void createScDoc(const char* pName = nullptr, const char* pPassword = nullptr, bool bCheckErrorCode = true);
+ ScDocument* getScDoc();
+ ScDocument* getScDoc2();
+ ScDocShell* getScDocShell();
+ ScTabViewShell* getViewShell();
+ void miscRowHeightsTest( TestParam const * aTestValues, unsigned int numElems);
+
+ void enableOpenCL();
+ void disableOpenCL();
+ void initTestEnv(std::u16string_view fileName);
+
+ void testFile(const OUString& aFileName, ScDocument& rDoc, SCTAB nTab, StringType aStringFormat = StringType::StringValue);
+
+ //need own handler because conditional formatting strings must be generated
+ void testCondFile(const OUString& aFileName, ScDocument* pDoc, SCTAB nTab, bool bCommaAsDelimiter = true);
+
+ const SdrOle2Obj* getSingleOleObject(ScDocument& rDoc, sal_uInt16 nPage);
+
+ const SdrOle2Obj* getSingleChartObject(ScDocument& rDoc, sal_uInt16 nPage);
+
+ ScRangeList getChartRanges(ScDocument& rDoc, const SdrOle2Obj& rChartObj);
+
+ void testFormats(ScDocument* pDoc,std::u16string_view sFormat);
+
+ void goToCell(const OUString& rCell);
+ void typeString(const std::u16string_view& rStr);
+ void insertStringToCell(const OUString& rCell, const std::u16string_view& rStr);
+ void insertArrayToCell(const OUString& rCell, const std::u16string_view& rStr);
+ void insertNewSheet(ScDocument& rDoc);
+ void executeAutoSum();
+
+private:
+ // Why is this here and not in osl, and using the already existing file
+ // handling APIs? Do we really want to add arbitrary new file handling
+ // wrappers here and there (and then having to handle the Android (and
+ // eventually perhaps iOS) special cases here, too)? Please move this to osl,
+ // it sure looks generally useful. Or am I missing something?
+
+ void loadFile(const OUString& aFileName, std::string& aContent);
+};
+
+#define ASSERT_DOUBLES_EQUAL( expected, result ) \
+ CPPUNIT_ASSERT_DOUBLES_EQUAL( (expected), (result), 1e-14 )
+
+#define ASSERT_DOUBLES_EQUAL_MESSAGE( message, expected, result ) \
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( (message), (expected), (result), 1e-14 )
+
+inline void assertPointEqual(
+ const Point& rExpected, const Point& rActual, const sal_Int32 nTolerance,
+ const CppUnit::SourceLine& rSourceLine )
+{
+ CPPUNIT_NS::assertDoubleEquals( rExpected.X(), rActual.X(), nTolerance, rSourceLine, "different X" );
+ CPPUNIT_NS::assertDoubleEquals( rExpected.Y(), rActual.Y(), nTolerance, rSourceLine, "different Y" );
+}
+
+#define CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aExpected, aActual, aTolerance) \
+ assertPointEqual( aExpected, aActual, aTolerance, CPPUNIT_SOURCELINE() )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/helper/scfiltertestbase.cxx b/sc/qa/unit/helper/scfiltertestbase.cxx
new file mode 100644
index 0000000000..0e8f4525ef
--- /dev/null
+++ b/sc/qa/unit/helper/scfiltertestbase.cxx
@@ -0,0 +1,67 @@
+/* -*- 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 "scfiltertestbase.hxx"
+
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/intitem.hxx>
+
+#include <com/sun/star/document/MacroExecMode.hpp>
+
+using namespace com::sun::star;
+using namespace ::com::sun::star::uno;
+
+ScDocShellRef ScFilterTestBase::loadDoc(const OUString& rURL, const OUString& rFilter,
+ const OUString& rUserData, const OUString& rTypeName,
+ SfxFilterFlags nFilterFlags,
+ SotClipboardFormatId nClipboardID, sal_Int32 nFilterVersion)
+{
+ auto pFilter
+ = std::make_shared<SfxFilter>(rFilter, OUString(), nFilterFlags, nClipboardID, rTypeName,
+ OUString(), rUserData, "private:factory/scalc");
+ pFilter->SetVersion(nFilterVersion);
+
+ ScDocShellRef xDocShRef = new ScDocShell;
+ xDocShRef->GetDocument().EnableUserInteraction(false);
+ SfxMedium* pSrcMed = new SfxMedium(rURL, StreamMode::STD_READ);
+ pSrcMed->SetFilter(pFilter);
+ pSrcMed->UseInteractionHandler(false);
+ pSrcMed->GetItemSet().Put(
+ SfxUInt16Item(SID_MACROEXECMODE, css::document::MacroExecMode::ALWAYS_EXECUTE_NO_WARN));
+ SAL_INFO("sc.qa", "about to load " << rURL);
+ if (!xDocShRef->DoLoad(pSrcMed))
+ {
+ xDocShRef->DoClose();
+ // load failed.
+ xDocShRef.clear();
+ }
+
+ return xDocShRef;
+}
+
+void ScFilterTestBase::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ // This is a bit of a fudge, we do this to ensure that ScGlobals::ensure,
+ // which is a private symbol to us, gets called
+ m_xCalcComponent
+ = getMultiServiceFactory()->createInstance("com.sun.star.comp.Calc.SpreadsheetDocument");
+ CPPUNIT_ASSERT_MESSAGE("no calc component!", m_xCalcComponent.is());
+}
+
+void ScFilterTestBase::tearDown()
+{
+ uno::Reference<lang::XComponent>(m_xCalcComponent, UNO_QUERY_THROW)->dispose();
+ test::BootstrapFixture::tearDown();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/helper/scfiltertestbase.hxx b/sc/qa/unit/helper/scfiltertestbase.hxx
new file mode 100644
index 0000000000..a0a12e47e2
--- /dev/null
+++ b/sc/qa/unit/helper/scfiltertestbase.hxx
@@ -0,0 +1,68 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <unotest/filters-test.hxx>
+#include <test/bootstrapfixture.hxx>
+
+#include <docsh.hxx>
+#include <address.hxx>
+
+#include <comphelper/fileformat.h>
+#include "scqahelperdllapi.h"
+
+#define ODS_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::TEMPLATE \
+ | SfxFilterFlags::OWN | SfxFilterFlags::DEFAULT | SfxFilterFlags::ENCRYPTION \
+ | SfxFilterFlags::PASSWORDTOMODIFY)
+#define XLS_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::ALIEN \
+ | SfxFilterFlags::ENCRYPTION | SfxFilterFlags::PASSWORDTOMODIFY | SfxFilterFlags::PREFERED)
+#define XLSX_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::ALIEN \
+ | SfxFilterFlags::STARONEFILTER | SfxFilterFlags::PREFERED)
+#define LOTUS123_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::ALIEN | SfxFilterFlags::PREFERED)
+#define CSV_FORMAT_TYPE (SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::ALIEN)
+#define HTML_FORMAT_TYPE (SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::ALIEN)
+#define DIF_FORMAT_TYPE (SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::ALIEN)
+#define XLS_XML_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::ALIEN | SfxFilterFlags::PREFERED)
+#define XLSB_XML_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::ALIEN | SfxFilterFlags::STARONEFILTER \
+ | SfxFilterFlags::PREFERED)
+#define FODS_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::OWN \
+ | SfxFilterFlags::STARONEFILTER)
+#define GNUMERIC_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::ALIEN | SfxFilterFlags::PREFERED)
+#define XLTX_FORMAT_TYPE \
+ (SfxFilterFlags::IMPORT | SfxFilterFlags::EXPORT | SfxFilterFlags::TEMPLATE \
+ | SfxFilterFlags::ALIEN | SfxFilterFlags::STARONEFILTER | SfxFilterFlags::PREFERED)
+
+class SCQAHELPER_DLLPUBLIC ScFilterTestBase : public test::BootstrapFixture,
+ public test::FiltersTest
+{
+protected:
+ ScDocShellRef loadDoc(const OUString& rURL, const OUString& rFilter, const OUString& rUserData,
+ const OUString& rTypeName, SfxFilterFlags nFilterFlags,
+ SotClipboardFormatId nClipboardID,
+ sal_Int32 nFilterVersion = SOFFICE_FILEFORMAT_CURRENT);
+
+private:
+ // reference to document interface that we are testing
+ css::uno::Reference<css::uno::XInterface> m_xCalcComponent;
+
+public:
+ virtual void setUp() override;
+ virtual void tearDown() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/helper/scqahelperdllapi.h b/sc/qa/unit/helper/scqahelperdllapi.h
new file mode 100644
index 0000000000..3b72847214
--- /dev/null
+++ b/sc/qa/unit/helper/scqahelperdllapi.h
@@ -0,0 +1,20 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+#if defined(SCQAHELPER_DLLIMPLEMENTATION)
+#define SCQAHELPER_DLLPUBLIC SAL_DLLPUBLIC_EXPORT
+#else
+#define SCQAHELPER_DLLPUBLIC SAL_DLLPUBLIC_IMPORT
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/helper/shared_test_impl.hxx b/sc/qa/unit/helper/shared_test_impl.hxx
new file mode 100644
index 0000000000..4d792cd378
--- /dev/null
+++ b/sc/qa/unit/helper/shared_test_impl.hxx
@@ -0,0 +1,298 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <memory>
+#include <colorscale.hxx>
+#include <conditio.hxx>
+#include <document.hxx>
+#include <formulacell.hxx>
+#include "qahelper.hxx"
+#include <formula/errorcodes.hxx>
+
+struct FindCondFormatByEnclosingRange
+{
+ explicit FindCondFormatByEnclosingRange(const ScRange& rRange):
+ mrRange(rRange) {}
+
+ bool operator()(const std::unique_ptr<ScConditionalFormat>& pFormat)
+ {
+ if (pFormat->GetRange().Combine() == mrRange)
+ return true;
+
+ return false;
+ }
+
+private:
+ const ScRange& mrRange;
+};
+
+struct DataBarData
+{
+ ScRange aRange;
+ ScColorScaleEntryType eLowerLimitType;
+ ScColorScaleEntryType eUpperLimitType;
+ databar::ScAxisPosition eAxisPosition;
+};
+
+DataBarData const aData[] = {
+ { ScRange(1,2,0,1,5,0), COLORSCALE_AUTO, COLORSCALE_AUTO, databar::AUTOMATIC },
+ { ScRange(3,2,0,3,5,0), COLORSCALE_MIN, COLORSCALE_MAX, databar::AUTOMATIC },
+ { ScRange(5,2,0,5,5,0), COLORSCALE_PERCENTILE, COLORSCALE_PERCENT, databar::AUTOMATIC },
+ { ScRange(7,2,0,7,5,0), COLORSCALE_VALUE, COLORSCALE_FORMULA, databar::AUTOMATIC },
+ { ScRange(1,9,0,1,12,0), COLORSCALE_AUTO, COLORSCALE_AUTO, databar::MIDDLE }
+};
+
+void testDataBar_Impl(const ScDocument& rDoc)
+{
+ ScConditionalFormatList* pList = rDoc.GetCondFormList(0);
+ CPPUNIT_ASSERT(pList);
+
+ for(size_t i = 0; i < std::size(aData); ++i)
+ {
+ ScConditionalFormatList::const_iterator itr = std::find_if(pList->begin(),
+ pList->end(), FindCondFormatByEnclosingRange(aData[i].aRange));
+ CPPUNIT_ASSERT(itr != pList->end());
+ CPPUNIT_ASSERT_EQUAL(size_t(1), (*itr)->size());
+
+ const ScFormatEntry* pFormatEntry = (*itr)->GetEntry(0);
+ CPPUNIT_ASSERT_EQUAL(ScFormatEntry::Type::Databar, pFormatEntry->GetType());
+ const ScDataBarFormat* pDataBar = static_cast<const ScDataBarFormat*>(pFormatEntry);
+ CPPUNIT_ASSERT(pDataBar);
+ const ScDataBarFormatData* pDataBarData = pDataBar->GetDataBarData();
+ CPPUNIT_ASSERT_EQUAL(aData[i].eLowerLimitType, pDataBarData->mpLowerLimit->GetType());
+ CPPUNIT_ASSERT_EQUAL(aData[i].eUpperLimitType, pDataBarData->mpUpperLimit->GetType());
+
+ CPPUNIT_ASSERT_EQUAL(aData[i].eAxisPosition, pDataBarData->meAxisPosition);
+ }
+}
+
+struct ColorScale2EntryData
+{
+ ScRange aRange;
+ ScColorScaleEntryType eLowerType;
+ ScColorScaleEntryType eUpperType;
+};
+
+ColorScale2EntryData const aData2Entry[] = {
+ { ScRange(1,2,0,1,5,0), COLORSCALE_MIN, COLORSCALE_MAX },
+ { ScRange(3,2,0,3,5,0), COLORSCALE_PERCENTILE, COLORSCALE_PERCENT },
+ { ScRange(5,2,0,5,5,0), COLORSCALE_VALUE, COLORSCALE_FORMULA }
+};
+
+void testColorScale2Entry_Impl(const ScDocument& rDoc)
+{
+ const ScConditionalFormatList* pList = rDoc.GetCondFormList(0);
+ CPPUNIT_ASSERT(pList);
+
+ for(size_t i = 0; i < std::size(aData2Entry); ++i)
+ {
+ ScConditionalFormatList::const_iterator itr = std::find_if(pList->begin(),
+ pList->end(), FindCondFormatByEnclosingRange(aData2Entry[i].aRange));
+ CPPUNIT_ASSERT(itr != pList->end());
+ CPPUNIT_ASSERT_EQUAL(size_t(1), (*itr)->size());
+
+ const ScFormatEntry* pFormatEntry = (*itr)->GetEntry(0);
+ CPPUNIT_ASSERT_EQUAL(ScFormatEntry::Type::Colorscale, pFormatEntry->GetType());
+ const ScColorScaleFormat* pColFormat = static_cast<const ScColorScaleFormat*>(pFormatEntry);
+ CPPUNIT_ASSERT_EQUAL(size_t(2), pColFormat->size());
+
+ ScColorScaleEntries::const_iterator format_itr = pColFormat->begin();
+ CPPUNIT_ASSERT_EQUAL(aData2Entry[i].eLowerType, (*format_itr)->GetType());
+ ++format_itr;
+ CPPUNIT_ASSERT(format_itr != pColFormat->end());
+ CPPUNIT_ASSERT_EQUAL(aData2Entry[i].eUpperType, (*format_itr)->GetType());
+ }
+}
+
+struct ColorScale3EntryData
+{
+ ScRange aRange;
+ ScColorScaleEntryType eLowerType;
+ ScColorScaleEntryType eMiddleType;
+ ScColorScaleEntryType eUpperType;
+};
+
+ColorScale3EntryData const aData3Entry[] = {
+ { ScRange(1,1,1,1,6,1), COLORSCALE_MIN, COLORSCALE_PERCENTILE, COLORSCALE_MAX },
+ { ScRange(3,1,1,3,6,1), COLORSCALE_PERCENTILE, COLORSCALE_VALUE, COLORSCALE_PERCENT },
+ { ScRange(5,1,1,5,6,1), COLORSCALE_VALUE, COLORSCALE_VALUE, COLORSCALE_FORMULA }
+};
+
+void testColorScale3Entry_Impl(const ScDocument& rDoc)
+{
+ ScConditionalFormatList* pList = rDoc.GetCondFormList(1);
+ CPPUNIT_ASSERT(pList);
+
+ for(size_t i = 0; i < std::size(aData3Entry); ++i)
+ {
+ ScConditionalFormatList::const_iterator itr = std::find_if(pList->begin(),
+ pList->end(), FindCondFormatByEnclosingRange(aData3Entry[i].aRange));
+ CPPUNIT_ASSERT(itr != pList->end());
+ CPPUNIT_ASSERT_EQUAL(size_t(1), (*itr)->size());
+
+ const ScFormatEntry* pFormatEntry = (*itr)->GetEntry(0);
+ CPPUNIT_ASSERT_EQUAL(ScFormatEntry::Type::Colorscale, pFormatEntry->GetType());
+ const ScColorScaleFormat* pColFormat = static_cast<const ScColorScaleFormat*>(pFormatEntry);
+ CPPUNIT_ASSERT_EQUAL(size_t(3), pColFormat->size());
+
+ ScColorScaleEntries::const_iterator format_itr = pColFormat->begin();
+ CPPUNIT_ASSERT_EQUAL(aData3Entry[i].eLowerType, (*format_itr)->GetType());
+ ++format_itr;
+ CPPUNIT_ASSERT(format_itr != pColFormat->end());
+ CPPUNIT_ASSERT_EQUAL(aData3Entry[i].eMiddleType, (*format_itr)->GetType());
+ ++format_itr;
+ CPPUNIT_ASSERT(format_itr != pColFormat->end());
+ CPPUNIT_ASSERT_EQUAL(aData3Entry[i].eUpperType, (*format_itr)->GetType());
+ }
+}
+
+bool isFormulaWithoutError(ScDocument& rDoc, const ScAddress& rPos)
+{
+ ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
+ if (!pFC)
+ return false;
+
+ return pFC->GetErrCode() == FormulaError::NONE;
+}
+
+void testFunctionsExcel2010_Impl( ScDocument& rDoc )
+{
+ // Original test case document is functions-excel-2010.xlsx
+ // Which test rows to evaluate, 1-based as in UI to ease maintenance.
+ static struct
+ {
+ SCROW nRow;
+ bool bEvaluate;
+ } const aTests[] = {
+ { 2, false }, // name=[ AGGREGATE ], result=0, expected=1
+ { 3, true },
+ { 4, true },
+ { 5, true },
+ { 6, true },
+ { 7, true },
+ { 8, true },
+ { 9, true },
+ { 10, true },
+ { 11, true },
+ { 12, true },
+ { 13, true },
+ { 14, true },
+ { 15, true },
+ { 16, true },
+ { 17, true },
+ { 18, true },
+ { 19, true },
+ { 20, true },
+ { 21, true },
+ { 22, true },
+ { 23, true },
+ { 24, true },
+ { 25, true },
+ { 26, true },
+ { 27, true },
+ { 28, true },
+ { 29, true },
+ { 30, true },
+ { 31, true },
+ { 32, true },
+ { 33, true },
+ { 34, true },
+ { 35, true },
+ { 36, true },
+ { 37, true },
+ { 38, true },
+ { 39, true },
+ { 40, true },
+ { 41, true },
+ { 42, true },
+ { 43, true },
+ { 44, true },
+ { 45, false }, // name=[ NETWORKDAYS.INTL ], result=18, expected=19
+ { 46, true },
+ { 47, true },
+ { 48, true },
+ { 49, true },
+ { 50, true },
+ { 51, true },
+ { 52, true },
+ { 53, true },
+ { 54, true },
+ { 55, true },
+ { 56, true },
+ { 57, true },
+ { 58, true },
+ { 59, true },
+ { 60, true },
+ { 61, true },
+ { 62, true },
+ { 63, true },
+ { 64, true },
+ { 65, true },
+ { 66, true },
+ { 67, true },
+ { 68, true },
+ { 69, true },
+ { 70, true },
+ { 71, true },
+ { 72, true },
+ { 73, true },
+ { 74, true },
+ { 75, true },
+ { 76, true },
+ { 77, true },
+ { 78, true },
+ { 79, false }, // name=[ WORKDAY.INTL ], result=41755 , expected=41754
+ { 80, true }
+ };
+
+ for (size_t i=0; i < std::size(aTests); ++i)
+ {
+ if (aTests[i].bEvaluate)
+ {
+ // Column 0 is description, 1 is formula, 2 is Excel result, 3 is
+ // comparison.
+ SCROW nRow = aTests[i].nRow - 1; // 0-based
+
+ OString aStr = OString::number( aTests[i].nRow) +
+ ", function name=[ " +
+ OUStringToOString( rDoc.GetString( ScAddress( 0, nRow, 0)), RTL_TEXTENCODING_UTF8 ) +
+ " ], result=" +
+ OString::number( rDoc.GetValue( ScAddress( 1, nRow, 0)) ) +
+ ", expected=" +
+ OString::number( rDoc.GetValue( ScAddress( 2, nRow, 0)) );
+
+ ScFormulaCell* pFC = rDoc.GetFormulaCell( ScAddress( 1, nRow, 0) );
+ if ( pFC && pFC->GetErrCode() != FormulaError::NONE )
+ aStr += ", error code =" + OString::number( static_cast<int>(pFC->GetErrCode()) );
+
+ CPPUNIT_ASSERT_MESSAGE( OString( "Expected a formula cell without error at row " +
+ aStr ).getStr(), isFormulaWithoutError( rDoc, ScAddress( 1, nRow, 0)));
+ CPPUNIT_ASSERT_MESSAGE( OString( "Expected a TRUE value at row " +
+ aStr ).getStr(), 0 != rDoc.GetValue( ScAddress( 3, nRow, 0)));
+
+ }
+ }
+}
+
+void testCeilingFloor_Impl( ScDocument& rDoc )
+{
+ // Original test case document is ceiling-floor.xlsx
+ // Sheet1.K1 has =AND(K3:K81) to evaluate all results.
+ static constexpr OUString pORef = u"Sheet1.K1"_ustr;
+ ScAddress aPos;
+ aPos.Parse(pORef, rDoc);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong formula.", OUString("=AND(K3:K81)"), rDoc.GetFormula(aPos.Col(), aPos.Row(), aPos.Tab()));
+ CPPUNIT_ASSERT_MESSAGE( OUString( pORef + " result is error.").toUtf8().getStr(),
+ isFormulaWithoutError( rDoc, aPos));
+ CPPUNIT_ASSERT_EQUAL(1.0, rDoc.GetValue(aPos));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/helper/sorthelper.hxx b/sc/qa/unit/helper/sorthelper.hxx
new file mode 100644
index 0000000000..5a37c29ddf
--- /dev/null
+++ b/sc/qa/unit/helper/sorthelper.hxx
@@ -0,0 +1,53 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+// Unfortunately requires linkage to sc/ internals so
+// can't live in qahelper itself.
+#include <inputopt.hxx>
+#include <scmod.hxx>
+
+/**
+ * Temporarily set the sorting type.
+ */
+class SortTypeSetter {
+ bool mbSortRefUpdate;
+public:
+ explicit SortTypeSetter(bool bSortRefUpdate)
+ {
+ mbSortRefUpdate = changeTo(bSortRefUpdate);
+ }
+ static bool changeTo(bool bSortRefUpdate)
+ {
+ ScInputOptions aInputOptions = SC_MOD()->GetInputOptions();
+ bool bRet = aInputOptions.GetSortRefUpdate();
+ aInputOptions.SetSortRefUpdate(bSortRefUpdate);
+ SC_MOD()->SetInputOptions(aInputOptions);
+ return bRet;
+ }
+ virtual ~SortTypeSetter() COVERITY_NOEXCEPT_FALSE
+ {
+ changeTo(mbSortRefUpdate);
+ }
+};
+
+class SortRefNoUpdateSetter : private SortTypeSetter
+{
+public:
+ SortRefNoUpdateSetter() : SortTypeSetter(false) {}
+};
+
+class SortRefUpdateSetter : private SortTypeSetter
+{
+public:
+ SortRefUpdateSetter() : SortTypeSetter(true) {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */