diff options
Diffstat (limited to 'sc/qa/unit/helper')
-rw-r--r-- | sc/qa/unit/helper/csv_handler.hxx | 205 | ||||
-rw-r--r-- | sc/qa/unit/helper/debughelper.hxx | 37 | ||||
-rw-r--r-- | sc/qa/unit/helper/qahelper.cxx | 807 | ||||
-rw-r--r-- | sc/qa/unit/helper/qahelper.hxx | 223 | ||||
-rw-r--r-- | sc/qa/unit/helper/scqahelperdllapi.h | 23 | ||||
-rw-r--r-- | sc/qa/unit/helper/shared_test_impl.hxx | 293 | ||||
-rw-r--r-- | sc/qa/unit/helper/sorthelper.hxx | 56 | ||||
-rw-r--r-- | sc/qa/unit/helper/xpath.cxx | 45 | ||||
-rw-r--r-- | sc/qa/unit/helper/xpath.hxx | 65 |
9 files changed, 1754 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 000000000..cbd2b9bd0 --- /dev/null +++ b/sc/qa/unit/helper/csv_handler.hxx @@ -0,0 +1,205 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SC_QA_UNIT_HELPER_CSV_HANDLER_HXX +#define INCLUDED_SC_QA_UNIT_HELPER_CSV_HANDLER_HXX + +#include "qahelper.hxx" + +#include <patattr.hxx> +#include <document.hxx> +#include <cellform.hxx> +#include <cellvalue.hxx> + +#define DEBUG_CSV_HANDLER 0 + +inline OUString getConditionalFormatString(ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab) +{ + OUString aString; + 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, const OUString& rExpectedString, const OUString& 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: + csv_handler(ScDocument* pDoc, SCTAB nTab, StringType eType): + mpDoc(pDoc), + mnCol(0), + mnRow(0), + mnTab(nTab), + meStringType(eType) {} + + static void begin_parse() {} + + static void end_parse() {} + + static void begin_row() {} + + void end_row() + { + ++mnRow; + mnCol = 0; + } + + void cell(const char* p, size_t n, bool /*transient*/) + { +#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, aValue); +#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: + conditional_format_handler(ScDocument* pDoc, SCTAB nTab): + mpDoc(pDoc), + mnCol(0), + mnRow(0), + mnTab(nTab) {} + + static void begin_parse() {} + + static void end_parse() {} + + static void begin_row() {} + + void end_row() + { + ++mnRow; + mnCol = 0; + } + + void cell(const char* p, size_t n, 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(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 ); + ++mnCol; + } + +private: + ScDocument* mpDoc; + SCCOL mnCol; + SCROW mnRow; + SCTAB mnTab; +}; + +#endif + +/* 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 000000000..fedde10dd --- /dev/null +++ b/sc/qa/unit/helper/debughelper.hxx @@ -0,0 +1,37 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SC_QA_UNIT_HELPER_DEBUGHELPER_HXX +#define INCLUDED_SC_QA_UNIT_HELPER_DEBUGHELPER_HXX + +#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 + +using namespace ::com::sun::star; +using ::std::cout; +using ::std::cerr; +using ::std::endl; +using ::std::vector; + +#endif + +/* 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 000000000..cc675cdb5 --- /dev/null +++ b/sc/qa/unit/helper/qahelper.cxx @@ -0,0 +1,807 @@ +/* -*- 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 "csv_handler.hxx" +#include "debughelper.hxx" +#include <drwlayer.hxx> +#include <comphelper/sequence.hxx> +#include <compiler.hxx> +#include <conditio.hxx> +#include <stlsheet.hxx> +#include <formulacell.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdoole2.hxx> +#include <tools/urlobj.hxx> +#include <editeng/brushitem.hxx> +#include <editeng/justifyitem.hxx> +#include <formula/errorcodes.hxx> +#include <cppunit/Asserter.h> +#include <cppunit/AdditionalMessage.h> +#include <sal/log.hxx> +#include <sfx2/sfxsids.hrc> +#include <svl/stritem.hxx> +#include <svl/intitem.hxx> +#include <svl/gridprinter.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/docfile.hxx> +#include <unotools/tempfile.hxx> +#include <scitems.hxx> +#include <tokenarray.hxx> + +#include <orcus/csv_parser.hpp> + +#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; + +// 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 Color& rColor) +{ + rStrm << "Color: R:" << static_cast<int>(rColor.GetRed()) << " G:" << static_cast<int>(rColor.GetGreen()) << " B: " << static_cast<int>(rColor.GetBlue()); + return rStrm; +} + +std::ostream& operator<<(std::ostream& rStrm, const OpCode& rCode) +{ + rStrm << static_cast<sal_uInt16>(rCode); + return rStrm; +} + +const FileFormat ScBootstrapFixture::aFileFormats[] = { + { "ods" , "calc8", "", ODS_FORMAT_TYPE }, + { "xls" , "MS Excel 97", "calc_MS_EXCEL_97", XLS_FORMAT_TYPE }, + { "xlsx", "Calc Office Open XML" , "Office Open XML Spreadsheet", XLSX_FORMAT_TYPE }, + { "xlsm", "Calc Office Open XML" , "Office Open XML Spreadsheet", XLSX_FORMAT_TYPE }, + { "csv" , "Text - txt - csv (StarCalc)", "generic_Text", CSV_FORMAT_TYPE }, + { "html" , "calc_HTML_WebQuery", "generic_HTML", HTML_FORMAT_TYPE }, + { "123" , "Lotus", "calc_Lotus", LOTUS123_FORMAT_TYPE }, + { "dif", "DIF", "calc_DIF", DIF_FORMAT_TYPE }, + { "xml", "MS Excel 2003 XML Orcus", "calc_MS_Excel_2003_XML", XLS_XML_FORMAT_TYPE }, + { "xlsb", "Calc MS Excel 2007 Binary", "MS Excel 2007 Binary", XLSB_XML_FORMAT_TYPE }, + { "fods", "OpenDocument Spreadsheet Flat XML", "calc_ODS_FlatXML", FODS_FORMAT_TYPE }, + { "gnumeric", "Gnumeric Spreadsheet", "Gnumeric XML", GNUMERIC_FORMAT_TYPE }, + { "xltx", "Calc Office Open XML Template", "Office Open XML Spreadsheet Template", XLTX_FORMAT_TYPE } +}; + +bool testEqualsWithTolerance( long nVal1, long nVal2, long nTol ) +{ + return ( labs( nVal1 - nVal2 ) <= nTol ); +} + +void 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 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[0], aContent.size() , aHandler, aConfig); + try + { + parser.parse(); + } + catch (const orcus::csv::parse_error& e) + { + std::cout << "reading csv content file failed: " << e.what() << std::endl; + OStringBuffer aErrorMsg("csv parser error: "); + aErrorMsg.append(e.what()); + CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false); + } +} + +void testCondFile(const OUString& aFileName, ScDocument* pDoc, SCTAB nTab) +{ + conditional_format_handler aHandler(pDoc, nTab); + orcus::csv::parser_config aConfig; + 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[0], aContent.size() , aHandler, aConfig); + try + { + parser.parse(); + } + catch (const orcus::csv::parse_error& e) + { + std::cout << "reading csv content file failed: " << e.what() << std::endl; + OStringBuffer aErrorMsg("csv parser error: "); + aErrorMsg.append(e.what()); + CPPUNIT_ASSERT_MESSAGE(aErrorMsg.getStr(), false); + } +} + +void testFormats(ScBootstrapFixture* pTest, ScDocument* pDoc, sal_Int32 nFormat) +{ + //test Sheet1 with csv file + OUString aCSVFileName; + pTest->createCSVPath("numberFormat.", aCSVFileName); + 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; + pPattern->GetFont(aFont,SC_AUTOCOL_RAW); + CPPUNIT_ASSERT_EQUAL_MESSAGE("font size should be 10", 200l, aFont.GetFontSize().getHeight()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("font color should be black", COL_AUTO, aFont.GetColor()); + pPattern = pDoc->GetPattern(0,1,1); + pPattern->GetFont(aFont, SC_AUTOCOL_RAW); + CPPUNIT_ASSERT_EQUAL_MESSAGE("font size should be 12", 240l, aFont.GetFontSize().getHeight()); + pPattern = pDoc->GetPattern(0,2,1); + pPattern->GetFont(aFont, SC_AUTOCOL_RAW); + CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be italic", ITALIC_NORMAL, aFont.GetItalic()); + pPattern = pDoc->GetPattern(0,4,1); + pPattern->GetFont(aFont, SC_AUTOCOL_RAW); + CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, aFont.GetWeight()); + pPattern = pDoc->GetPattern(1,0,1); + pPattern->GetFont(aFont, SC_AUTOCOL_RAW); + CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be blue", COL_BLUE, aFont.GetColor()); + pPattern = pDoc->GetPattern(1,1,1); + pPattern->GetFont(aFont, SC_AUTOCOL_RAW); + 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 (nFormat == FORMAT_ODS) + { + pPattern = pDoc->GetPattern(1,2,1); + pPattern->GetFont(aFont, SC_AUTOCOL_RAW); + CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be striked out with a double line", STRIKEOUT_DOUBLE, aFont.GetStrikeout()); + pPattern = pDoc->GetPattern(1,3,1); + pPattern->GetFont(aFont, SC_AUTOCOL_RAW); + 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 ( nFormat == FORMAT_ODS || nFormat == FORMAT_XLSX ) + { + pTest->createCSVPath("conditionalFormatting.", aCSVFileName); + testCondFile(aCSVFileName, pDoc, 2); + // test parent cell style import ( fdo#55198 ) + if ( nFormat == FORMAT_XLSX ) + { + 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); +} + +const SdrOle2Obj* 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() != OBJ_OLE2) + { + cout << "This is not an OLE2 object." << endl; + return nullptr; + } + + return static_cast<const SdrOle2Obj*>(pObj); +} + +const SdrOle2Obj* 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 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; +} + +ScTokenArray* getTokens(ScDocument& rDoc, const ScAddress& rPos) +{ + ScFormulaCell* pCell = rDoc.GetFormulaCell(rPos); + if (!pCell) + { + OUString aStr = rPos.Format(ScRefFlags::VALID); + cerr << aStr << " is not a formula cell." << endl; + return nullptr; + } + + return pCell->GetCode(); +} + +bool checkFormula(ScDocument& rDoc, const ScAddress& rPos, const char* pExpected) +{ + ScTokenArray* pCode = getTokens(rDoc, rPos); + if (!pCode) + { + cerr << "Empty token array." << endl; + return false; + } + + OUString aFormula = toString(rDoc, rPos, *pCode, rDoc.GetGrammar()); + if (aFormula != OUString::createFromAscii(pExpected)) + { + cerr << "Formula '" << pExpected << "' expected, but '" << aFormula << "' found" << endl; + return false; + } + + return true; +} + +bool 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 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> compileFormula( + ScDocument* pDoc, const OUString& rFormula, + formula::FormulaGrammar::Grammar eGram ) +{ + ScAddress aPos(0,0,0); + ScCompiler aComp(pDoc, aPos, eGram); + return aComp.CompileString(rFormula); +} + +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; + 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 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); + } + } + } +} + +bool isFormulaWithoutError(ScDocument& rDoc, const ScAddress& rPos) +{ + ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos); + if (!pFC) + return false; + + return pFC->GetErrCode() == FormulaError::NONE; +} + +OUString toString( + ScDocument& rDoc, const ScAddress& rPos, ScTokenArray& rArray, formula::FormulaGrammar::Grammar eGram) +{ + ScCompiler aComp(&rDoc, rPos, rArray, eGram); + OUStringBuffer aBuf; + aComp.CreateStringFromTokenArray(aBuf); + return aBuf.makeStringAndClear(); +} + +ScDocShellRef ScBootstrapFixture::load( bool bReadWrite, + const OUString& rURL, const OUString& rFilter, const OUString &rUserData, + const OUString& rTypeName, SfxFilterFlags nFilterFlags, SotClipboardFormatId nClipboardID, + sal_uIntPtr nFilterVersion, const OUString* pPassword ) +{ + 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, bReadWrite ? StreamMode::STD_READWRITE : StreamMode::STD_READ ); + pSrcMed->SetFilter(pFilter); + pSrcMed->UseInteractionHandler(false); + SfxItemSet* pSet = pSrcMed->GetItemSet(); + if (pPassword) + { + pSet->Put(SfxStringItem(SID_PASSWORD, *pPassword)); + } + pSet->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; +} + +ScDocShellRef ScBootstrapFixture::load( + const OUString& rURL, const OUString& rFilter, const OUString &rUserData, + const OUString& rTypeName, SfxFilterFlags nFilterFlags, SotClipboardFormatId nClipboardID, + sal_uIntPtr nFilterVersion, const OUString* pPassword ) +{ + return load( false, rURL, rFilter, rUserData, rTypeName, nFilterFlags, nClipboardID, nFilterVersion, pPassword ); +} + +ScDocShellRef ScBootstrapFixture::loadDoc( + const OUString& rFileName, sal_Int32 nFormat, bool bReadWrite ) +{ + OUString aFileExtension(aFileFormats[nFormat].pName, strlen(aFileFormats[nFormat].pName), RTL_TEXTENCODING_UTF8 ); + OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ; + OUString aFileName; + createFileURL( rFileName, aFileExtension, aFileName ); + OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8); + SfxFilterFlags nFormatType = aFileFormats[nFormat].nFormatType; + SotClipboardFormatId nClipboardId = SotClipboardFormatId::NONE; + if (nFormatType != SfxFilterFlags::NONE) + nClipboardId = SotClipboardFormatId::STARCALC_8; + + return load(bReadWrite, aFileName, aFilterName, OUString(), aFilterType, nFormatType, nClipboardId, static_cast<sal_uIntPtr>(nFormatType)); +} + +ScBootstrapFixture::ScBootstrapFixture( const OUString& rsBaseString ) : m_aBaseString( rsBaseString ) {} +ScBootstrapFixture::~ScBootstrapFixture() {} + +namespace { +OUString EnsureSeparator(const OUStringBuffer& rFilePath) +{ + return (rFilePath.getLength() == 0) || (rFilePath[rFilePath.getLength() - 1] != '/') ? + OUString("/") : + OUString(); +} +} + +void ScBootstrapFixture::createFileURL( + const OUString& aFileBase, const OUString& aFileExtension, OUString& rFilePath) +{ + // m_aBaseString and aFileBase may contain multiple segments, so use + // GetNewAbsURL instead of insertName for them: + INetURLObject url(m_directories.getSrcRootURL()); + url.setFinalSlash(); + url.GetNewAbsURL(m_aBaseString, &url); + url.insertName(aFileExtension, true); + url.GetNewAbsURL(aFileBase + aFileExtension, &url); + rFilePath = url.GetMainURL(INetURLObject::DecodeMechanism::NONE); +} + +void ScBootstrapFixture::createCSVPath(const OUString& aFileBase, OUString& rCSVPath) +{ + OUStringBuffer aBuffer( m_directories.getSrcRootPath()); + aBuffer.append(EnsureSeparator(aBuffer)).append(m_aBaseString); + aBuffer.append(EnsureSeparator(aBuffer)).append("contentCSV/").append(aFileBase).append("csv"); + rCSVPath = aBuffer.makeStringAndClear(); +} + +ScDocShellRef ScBootstrapFixture::saveAndReload( + ScDocShell* pShell, const OUString &rFilter, + const OUString &rUserData, const OUString& rTypeName, SfxFilterFlags nFormatType) +{ + + utl::TempFile aTempFile; + SfxMedium aStoreMedium( aTempFile.GetURL(), StreamMode::STD_WRITE ); + SotClipboardFormatId nExportFormat = SotClipboardFormatId::NONE; + if (nFormatType == ODS_FORMAT_TYPE) + nExportFormat = SotClipboardFormatId::STARCHART_8; + auto pExportFilter = std::make_shared<SfxFilter>( + rFilter, + OUString(), nFormatType, nExportFormat, rTypeName, OUString(), + rUserData, "private:factory/scalc*" ); + pExportFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT); + aStoreMedium.SetFilter(pExportFilter); + pShell->DoSaveAs( aStoreMedium ); + pShell->DoClose(); + + //std::cout << "File: " << aTempFile.GetURL() << std::endl; + + SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; + if (nFormatType == ODS_FORMAT_TYPE) + nFormat = SotClipboardFormatId::STARCALC_8; + + ScDocShellRef xDocSh = load(aTempFile.GetURL(), rFilter, rUserData, rTypeName, nFormatType, nFormat ); + if(nFormatType == XLSX_FORMAT_TYPE) + validate(aTempFile.GetFileName(), test::OOXML); + else if (nFormatType == ODS_FORMAT_TYPE) + validate(aTempFile.GetFileName(), test::ODF); + aTempFile.EnableKillingFile(); + return xDocSh; +} + +ScDocShellRef ScBootstrapFixture::saveAndReload( ScDocShell* pShell, sal_Int32 nFormat ) +{ + OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ; + OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8); + ScDocShellRef xDocSh = saveAndReload(pShell, aFilterName, OUString(), aFilterType, aFileFormats[nFormat].nFormatType); + + CPPUNIT_ASSERT(xDocSh.is()); + return xDocSh; +} + +std::shared_ptr<utl::TempFile> ScBootstrapFixture::saveAs( ScDocShell* pShell, sal_Int32 nFormat ) +{ + OUString aFilterName(aFileFormats[nFormat].pFilterName, strlen(aFileFormats[nFormat].pFilterName), RTL_TEXTENCODING_UTF8) ; + OUString aFilterType(aFileFormats[nFormat].pTypeName, strlen(aFileFormats[nFormat].pTypeName), RTL_TEXTENCODING_UTF8); + + auto pTempFile = std::make_shared<utl::TempFile>(); + pTempFile->EnableKillingFile(); + SfxMedium aStoreMedium( pTempFile->GetURL(), StreamMode::STD_WRITE ); + SotClipboardFormatId nExportFormat = SotClipboardFormatId::NONE; + SfxFilterFlags nFormatType = aFileFormats[nFormat].nFormatType; + if (nFormatType == ODS_FORMAT_TYPE) + nExportFormat = SotClipboardFormatId::STARCHART_8; + auto pExportFilter = std::make_shared<SfxFilter>( + aFilterName, + OUString(), nFormatType, nExportFormat, aFilterType, OUString(), + OUString(), "private:factory/scalc*" ); + pExportFilter->SetVersion(SOFFICE_FILEFORMAT_CURRENT); + aStoreMedium.SetFilter(pExportFilter); + pShell->DoSaveAs( aStoreMedium ); + + return pTempFile; +} + +std::shared_ptr<utl::TempFile> ScBootstrapFixture::exportTo( ScDocShell* pShell, sal_Int32 nFormat ) +{ + std::shared_ptr<utl::TempFile> pTempFile = saveAs(pShell, nFormat); + pShell->DoClose(); + + SfxFilterFlags nFormatType = aFileFormats[nFormat].nFormatType; + if(nFormatType == XLSX_FORMAT_TYPE) + validate(pTempFile->GetFileName(), test::OOXML); + else if (nFormatType == ODS_FORMAT_TYPE) + validate(pTempFile->GetFileName(), test::ODF); + + return pTempFile; +} + +void ScBootstrapFixture::miscRowHeightsTest( TestParam const * aTestValues, unsigned int numElems ) +{ + for ( unsigned int index=0; index<numElems; ++index ) + { + OUString sFileName = OUString::createFromAscii( aTestValues[ index ].sTestDoc ); + SAL_INFO( "sc.qa", "aTestValues[" << index << "] " << sFileName ); + int nImportType = aTestValues[ index ].nImportType; + int nExportType = aTestValues[ index ].nExportType; + ScDocShellRef xShell = loadDoc( sFileName, nImportType ); + CPPUNIT_ASSERT(xShell.is()); + + if ( nExportType != -1 ) + xShell = saveAndReload(&(*xShell), nExportType ); + + CPPUNIT_ASSERT(xShell.is()); + + ScDocument& rDoc = xShell->GetDocument(); + + 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 = sc::TwipsToHMM( 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 = sc::TwipsToHMM( rDoc.GetRowHeight(nRow, nTab, false) ); + if ( bCheckOpt ) + { + bool bOpt = !(rDoc.GetRowFlags( nRow, nTab ) & CRFlags::ManualSize); + CPPUNIT_ASSERT_EQUAL(aTestValues[ index ].pData[ i ].bOptimal, bOpt); + } + CPPUNIT_ASSERT_EQUAL(nExpectedHeight, nHeight); + } + } + xShell->DoClose(); + } +} + +std::string to_std_string(const OUString& rStr) +{ + return std::string(rStr.toUtf8().getStr()); +} + +void checkFormula(ScDocument& rDoc, const ScAddress& rPos, const char* expected, const char* msg, CppUnit::SourceLine const & sourceLine) +{ + ScTokenArray* pCode = getTokens(rDoc, rPos); + if (!pCode) + { + CppUnit::Asserter::fail("empty token array", sourceLine); + } + + OUString aFormula = toString(rDoc, rPos, *pCode, rDoc.GetGrammar()); + OUString aExpectedFormula = OUString::createFromAscii(expected); + if (aFormula != aExpectedFormula) + { + CppUnit::Asserter::failNotEqual(to_std_string(aExpectedFormula), + to_std_string(aFormula), sourceLine, CppUnit::AdditionalMessage(msg)); + } +} + +/* 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 000000000..1a7212874 --- /dev/null +++ b/sc/qa/unit/helper/qahelper.hxx @@ -0,0 +1,223 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SC_QA_UNIT_HELPER_QAHELPER_HXX +#define INCLUDED_SC_QA_UNIT_HELPER_QAHELPER_HXX + +#include <docsh.hxx> +#include <address.hxx> + +#include <cppunit/SourceLine.h> + +#include <test/bootstrapfixture.hxx> +#include <comphelper/documentconstants.hxx> + +#include <comphelper/fileformat.h> +#include <formula/grammar.hxx> +#include "scqahelperdllapi.h" + +#include <string> +#include <sstream> + +#include <sal/types.h> + +#include <memory> + +namespace utl { class TempFile; } + +#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) + +#define FORMAT_ODS 0 +#define FORMAT_XLS 1 +#define FORMAT_XLSX 2 +#define FORMAT_XLSM 3 +#define FORMAT_CSV 4 +#define FORMAT_HTML 5 +#define FORMAT_LOTUS123 6 +#define FORMAT_DIF 7 +#define FORMAT_XLS_XML 8 +#define FORMAT_XLSB 9 +#define FORMAT_FODS 10 +#define FORMAT_GNUMERIC 11 +#define FORMAT_XLTX 12 + +enum class StringType { PureString, StringValue }; + +SCQAHELPER_DLLPUBLIC bool testEqualsWithTolerance( long nVal1, long nVal2, long nTol ); + +#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 char* sTestDoc; + int nImportType; + int nExportType; // -1 for import test, otherwise this is an export test + int nRowData; + RowData const * pData; +}; + +struct FileFormat { + const char* pName; const char* pFilterName; const char* pTypeName; SfxFilterFlags nFormatType; +}; + +// 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 Color& rColor); + +SCQAHELPER_DLLPUBLIC std::ostream& operator<<(std::ostream& rStrm, const OpCode& rCode); + +// 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); + +SCQAHELPER_DLLPUBLIC void testFile(const OUString& aFileName, ScDocument& rDoc, SCTAB nTab, StringType aStringFormat = StringType::StringValue); + +//need own handler because conditional formatting strings must be generated +SCQAHELPER_DLLPUBLIC void testCondFile(const OUString& aFileName, ScDocument* pDoc, SCTAB nTab); + +SCQAHELPER_DLLPUBLIC const SdrOle2Obj* getSingleOleObject(ScDocument& rDoc, sal_uInt16 nPage); + +SCQAHELPER_DLLPUBLIC const SdrOle2Obj* getSingleChartObject(ScDocument& rDoc, sal_uInt16 nPage); + +SCQAHELPER_DLLPUBLIC ScRangeList getChartRanges(ScDocument& rDoc, const SdrOle2Obj& rChartObj); + +bool checkFormula(ScDocument& rDoc, const ScAddress& rPos, const char* pExpected); + +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 ); + +SCQAHELPER_DLLPUBLIC bool checkOutput( + const ScDocument* pDoc, const ScRange& aOutRange, + const std::vector<std::vector<const char*>>& aCheck, const char* pCaption ); + +void clearFormulaCellChangedFlag( ScDocument& rDoc, const ScRange& rRange ); + +/** + * Check if the cell at specified position is a formula cell that doesn't + * have an error value. + */ +SCQAHELPER_DLLPUBLIC bool isFormulaWithoutError(ScDocument& rDoc, const ScAddress& rPos); + +/** + * Convert formula token array to a formula string. + */ +SCQAHELPER_DLLPUBLIC OUString toString( + ScDocument& rDoc, const ScAddress& rPos, ScTokenArray& rArray, + formula::FormulaGrammar::Grammar eGram); + +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(); +} + +class SCQAHELPER_DLLPUBLIC ScBootstrapFixture : public test::BootstrapFixture +{ + static const FileFormat aFileFormats[]; +protected: + OUString m_aBaseString; + + ScDocShellRef load( + bool bReadWrite, const OUString& rURL, const OUString& rFilter, const OUString &rUserData, + const OUString& rTypeName, SfxFilterFlags nFilterFlags, SotClipboardFormatId nClipboardID, + sal_uIntPtr nFilterVersion = SOFFICE_FILEFORMAT_CURRENT, const OUString* pPassword = nullptr ); + + ScDocShellRef load( + const OUString& rURL, const OUString& rFilter, const OUString &rUserData, + const OUString& rTypeName, SfxFilterFlags nFilterFlags, SotClipboardFormatId nClipboardID, + sal_uIntPtr nFilterVersion = SOFFICE_FILEFORMAT_CURRENT, const OUString* pPassword = nullptr ); + + ScDocShellRef loadDoc(const OUString& rFileName, sal_Int32 nFormat, bool bReadWrite = false ); + +public: + static const FileFormat* getFileFormats() { return aFileFormats; } + + explicit ScBootstrapFixture( const OUString& rsBaseString ); + virtual ~ScBootstrapFixture() override; + + void createFileURL(const OUString& aFileBase, const OUString& aFileExtension, OUString& rFilePath); + + void createCSVPath(const OUString& aFileBase, OUString& rCSVPath); + + ScDocShellRef saveAndReload(ScDocShell* pShell, const OUString &rFilter, + const OUString &rUserData, const OUString& rTypeName, SfxFilterFlags nFormatType); + + ScDocShellRef saveAndReload( ScDocShell* pShell, sal_Int32 nFormat ); + + std::shared_ptr<utl::TempFile> saveAs(ScDocShell* pShell, sal_Int32 nFormat); + std::shared_ptr<utl::TempFile> exportTo(ScDocShell* pShell, sal_Int32 nFormat); + + void miscRowHeightsTest( TestParam const * aTestValues, unsigned int numElems ); +}; + +#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 ) + +SCQAHELPER_DLLPUBLIC void checkFormula(ScDocument& rDoc, const ScAddress& rPos, + const char* expected, const char* msg, CppUnit::SourceLine const & sourceLine); + +#define ASSERT_FORMULA_EQUAL(doc, pos, expected, msg) \ + checkFormula(doc, pos, expected, msg, CPPUNIT_SOURCELINE()) + +SCQAHELPER_DLLPUBLIC void testFormats(ScBootstrapFixture* pTest, ScDocument* pDoc, sal_Int32 nFormat); + +SCQAHELPER_DLLPUBLIC ScTokenArray* getTokens(ScDocument& rDoc, const ScAddress& rPos); + +SCQAHELPER_DLLPUBLIC std::string to_std_string(const OUString& rStr); + +#endif + +/* 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 000000000..216ccc014 --- /dev/null +++ b/sc/qa/unit/helper/scqahelperdllapi.h @@ -0,0 +1,23 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SC_QA_UNIT_HELPER_DLLAPI_H +#define INCLUDED_SC_QA_UNIT_HELPER_DLLAPI_H + +#include <sal/types.h> + +#if defined(SCQAHELPER_DLLIMPLEMENTATION) +#define SCQAHELPER_DLLPUBLIC SAL_DLLPUBLIC_EXPORT +#else +#define SCQAHELPER_DLLPUBLIC SAL_DLLPUBLIC_IMPORT +#endif + +#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 000000000..6dbd67769 --- /dev/null +++ b/sc/qa/unit/helper/shared_test_impl.hxx @@ -0,0 +1,293 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SC_QA_UNIT_HELPER_SHARED_TEST_IMPL_HXX +#define INCLUDED_SC_QA_UNIT_HELPER_SHARED_TEST_IMPL_HXX + +#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 < SAL_N_ELEMENTS(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 < SAL_N_ELEMENTS(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 < SAL_N_ELEMENTS(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()); + } +} + +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 < SAL_N_ELEMENTS(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. + const char pORef[] = "Sheet1.K1"; + OUString aRef(pORef); + ScAddress aPos; + aPos.Parse(aRef, &rDoc); + ASSERT_FORMULA_EQUAL(rDoc, aPos, "AND(K3:K81)", "Wrong formula."); + CPPUNIT_ASSERT_MESSAGE( OString( pORef + OStringLiteral(" result is error.")).getStr(), + isFormulaWithoutError( rDoc, aPos)); + CPPUNIT_ASSERT_EQUAL(1.0, rDoc.GetValue(aPos)); +} + +#endif + +/* 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 000000000..e71045097 --- /dev/null +++ b/sc/qa/unit/helper/sorthelper.hxx @@ -0,0 +1,56 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SC_QA_SORT_HELPER_QAHELPER_HXX +#define INCLUDED_SC_QA_SORT_HELPER_QAHELPER_HXX + +// 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) {} +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/helper/xpath.cxx b/sc/qa/unit/helper/xpath.cxx new file mode 100644 index 000000000..39f9b36e9 --- /dev/null +++ b/sc/qa/unit/helper/xpath.cxx @@ -0,0 +1,45 @@ +/* -*- 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 "xpath.hxx" + +#include <cppunit/TestAssert.h> +#include "qahelper.hxx" + +#include <unotools/ucbstreamhelper.hxx> +#include <comphelper/processfactory.hxx> + +#include <test/xmltesttools.hxx> + +#include <com/sun/star/packages/zip/ZipFileAccess.hpp> + +xmlDocUniquePtr XPathHelper::parseExport2(ScBootstrapFixture & rFixture, ScDocShell& rShell, uno::Reference<lang::XMultiServiceFactory> const & xSFactory, const OUString& rFile, sal_Int32 nFormat) +{ + std::shared_ptr<utl::TempFile> pTempFile = rFixture.exportTo(&rShell, nFormat); + + return parseExport(pTempFile, xSFactory, rFile); +} + +std::unique_ptr<SvStream> XPathHelper::parseExportStream(std::shared_ptr<utl::TempFile> const & pTempFile, uno::Reference<lang::XMultiServiceFactory> const & xSFactory, const OUString& rFile) +{ + // Read the XML stream we're interested in. + uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(xSFactory), pTempFile->GetURL()); + uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName(rFile), uno::UNO_QUERY); + CPPUNIT_ASSERT(xInputStream.is()); + std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true)); + return pStream; +} + +xmlDocUniquePtr XPathHelper::parseExport(std::shared_ptr<utl::TempFile> const & pTempFile, uno::Reference<lang::XMultiServiceFactory> const & xSFactory, const OUString& rFile) +{ + std::unique_ptr<SvStream> pStream = parseExportStream(pTempFile, xSFactory, rFile); + return XmlTestTools::parseXmlStream(pStream.get()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/helper/xpath.hxx b/sc/qa/unit/helper/xpath.hxx new file mode 100644 index 000000000..26e2c2462 --- /dev/null +++ b/sc/qa/unit/helper/xpath.hxx @@ -0,0 +1,65 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_SC_QA_UNIT_HELPER_XPATH_HXX +#define INCLUDED_SC_QA_UNIT_HELPER_XPATH_HXX + +#include <libxml/parser.h> + +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include "scqahelperdllapi.h" +#include <test/xmltesttools.hxx> + +#include <memory> + +namespace com::sun::star::uno { template <class interface_type> class Reference; } +namespace com::sun::star::lang { class XMultiServiceFactory; } +namespace utl { class TempFile; } +class ScDocShell; +class SvStream; + +using namespace com::sun::star; + +class ScBootstrapFixture; + +namespace XPathHelper +{ + /** + * Given that some problem doesn't affect the result in the importer, we + * test the resulting file directly, by opening the zip file, parsing an + * xml stream, and asserting an XPath expression. This method returns the + * xml stream, so that you can do the asserting. + * + * Warning: This method saves the document and does not export it! If you need to + * test several files in the same exported xml file you need to export the file manually + * and call the parseExport method that takes a TempFile + */ + SCQAHELPER_DLLPUBLIC xmlDocUniquePtr parseExport2(ScBootstrapFixture &, ScDocShell& rShell, uno::Reference< lang::XMultiServiceFactory> const & xSFactory, + const OUString& rFile, sal_Int32 nFormat); + + /** + * Tries to parse the specified file in the temp file zip container as a binary file. + */ + SCQAHELPER_DLLPUBLIC std::unique_ptr<SvStream> parseExportStream(std::shared_ptr<utl::TempFile> const & pTempFile, + uno::Reference<lang::XMultiServiceFactory> const & xSFactory, const OUString& rFile); + + /** + * Tries to parse the specified file in the temp file zip container as an xml file. + * + * Should be used when the same exported file is used for testing different files in + * the same zip file. + */ + SCQAHELPER_DLLPUBLIC xmlDocUniquePtr parseExport(std::shared_ptr<utl::TempFile> const & pTempFile, uno::Reference< lang::XMultiServiceFactory> const & xSFactory, + const OUString& rFile); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |