LCOV - code coverage report
Current view: top level - usr/local/src/libreoffice/sc/qa/unit - ucalc.cxx (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 3409 3640 93.7 %
Date: 2013-07-09 Functions: 130 135 96.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  */
       9             : #include <sal/config.h>
      10             : #include <test/bootstrapfixture.hxx>
      11             : 
      12             : #include <rtl/strbuf.hxx>
      13             : #include <osl/file.hxx>
      14             : #include <osl/time.h>
      15             : 
      16             : #include "scdll.hxx"
      17             : #include "formulacell.hxx"
      18             : #include "document.hxx"
      19             : #include "stringutil.hxx"
      20             : #include "scmatrix.hxx"
      21             : #include "drwlayer.hxx"
      22             : #include "scitems.hxx"
      23             : #include "reffind.hxx"
      24             : #include "markdata.hxx"
      25             : #include "clipparam.hxx"
      26             : #include "refundo.hxx"
      27             : #include "undoblk.hxx"
      28             : #include "undotab.hxx"
      29             : #include "queryentry.hxx"
      30             : #include "postit.hxx"
      31             : #include "attrib.hxx"
      32             : #include "dbdata.hxx"
      33             : #include "reftokenhelper.hxx"
      34             : #include "userdat.hxx"
      35             : 
      36             : #include "docsh.hxx"
      37             : #include "docfunc.hxx"
      38             : #include "dbdocfun.hxx"
      39             : #include "funcdesc.hxx"
      40             : #include "externalrefmgr.hxx"
      41             : 
      42             : #include "dpshttab.hxx"
      43             : #include "dpobject.hxx"
      44             : #include "dpsave.hxx"
      45             : #include "dpdimsave.hxx"
      46             : #include "dpcache.hxx"
      47             : #include "dpfilteredcache.hxx"
      48             : #include "calcconfig.hxx"
      49             : #include "interpre.hxx"
      50             : #include "columniterator.hxx"
      51             : #include "types.hxx"
      52             : #include "conditio.hxx"
      53             : #include "globstr.hrc"
      54             : #include "tokenarray.hxx"
      55             : #include "scopetools.hxx"
      56             : #include "dociter.hxx"
      57             : 
      58             : #include "formula/IFunctionDescription.hxx"
      59             : 
      60             : #include <basegfx/polygon/b2dpolygon.hxx>
      61             : #include <editeng/boxitem.hxx>
      62             : 
      63             : #include <svx/svdograf.hxx>
      64             : #include <svx/svdpage.hxx>
      65             : #include <svx/svdocirc.hxx>
      66             : #include <svx/svdopath.hxx>
      67             : #include "svl/srchitem.hxx"
      68             : 
      69             : #include <sfx2/docfile.hxx>
      70             : 
      71             : #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
      72             : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
      73             : #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
      74             : #include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp>
      75             : #include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp>
      76             : #include <com/sun/star/sheet/GeneralFunction.hpp>
      77             : 
      78             : #include <iostream>
      79             : #include <sstream>
      80             : #include <vector>
      81             : 
      82             : #define CALC_DEBUG_OUTPUT 0
      83             : #define CALC_TEST_PERF 0
      84             : 
      85             : #include "helper/debughelper.hxx"
      86             : #include "helper/qahelper.hxx"
      87             : 
      88             : const int indeterminate = 2;
      89             : 
      90             : using namespace ::com::sun::star;
      91             : 
      92             : using ::std::cout;
      93             : using ::std::cerr;
      94             : using ::std::endl;
      95             : using ::std::vector;
      96             : 
      97             : namespace {
      98             : 
      99         156 : class Test : public test::BootstrapFixture {
     100             : public:
     101             :     Test();
     102             : 
     103             :     virtual void setUp();
     104             :     virtual void tearDown();
     105             : 
     106             :     /**
     107             :      * Basic performance regression test. Pick some actions that *should* take
     108             :      * only a fraction of a second to complete, and make sure they stay that
     109             :      * way. We set the threshold to 1 second for each action which should be
     110             :      * large enough to accommodate slower machines or machines with high load.
     111             :      */
     112             :     void testPerf();
     113             :     void testCollator();
     114             :     void testRangeList();
     115             :     void testInput();
     116             :     void testFormulaHashAndTag();
     117             :     void testFuncSUM();
     118             :     void testFuncPRODUCT();
     119             :     void testFuncN();
     120             :     void testFuncCOUNTIF();
     121             :     void testFuncNUMBERVALUE();
     122             :     void testFuncVLOOKUP();
     123             :     void testFuncMATCH();
     124             :     void testFuncCELL();
     125             :     void testFuncDATEDIF();
     126             :     void testFuncINDIRECT();
     127             :     void testFuncIFERROR();
     128             :     void testCopyToDocument();
     129             :     /**
     130             :      * Make sure the SHEETS function gets properly updated during sheet
     131             :      * insertion and removal.
     132             :      */
     133             :     void testSheetsFunc();
     134             :     void testVolatileFunc();
     135             : 
     136             :     void testHorizontalIterator();
     137             : 
     138             :     /**
     139             :      * Basic test for formula dependency tracking.
     140             :      */
     141             :     void testFormulaDepTracking();
     142             : 
     143             :     /**
     144             :      * Another test for formula dependency tracking, inspired by fdo#56278.
     145             :      */
     146             :     void testFormulaDepTracking2();
     147             : 
     148             :     /**
     149             :      * More direct test for cell broadcaster management, used to track formula
     150             :      * dependencies.
     151             :      */
     152             :     void testCellBroadcaster();
     153             : 
     154             :     void testFuncParam();
     155             :     void testNamedRange();
     156             :     void testCSV();
     157             :     void testMatrix();
     158             :     void testEnterMixedMatrix();
     159             : 
     160             :     /**
     161             :      * Basic test for pivot tables.
     162             :      */
     163             :     void testPivotTable();
     164             : 
     165             :     /**
     166             :      * Test against unwanted automatic format detection on field names and
     167             :      * field members in pivot tables.
     168             :      */
     169             :     void testPivotTableLabels();
     170             : 
     171             :     /**
     172             :      * Make sure that we set cells displaying date values numeric cells,
     173             :      * rather than text cells.  Grouping by date or number functionality
     174             :      * depends on this.
     175             :      */
     176             :     void testPivotTableDateLabels();
     177             : 
     178             :     /**
     179             :      * Test for pivot table's filtering functionality by page fields.
     180             :      */
     181             :     void testPivotTableFilters();
     182             : 
     183             :     /**
     184             :      * Test for pivot table's named source range.
     185             :      */
     186             :     void testPivotTableNamedSource();
     187             : 
     188             :     /**
     189             :      * Test for pivot table cache.  Each dimension in the pivot cache stores
     190             :      * only unique values that are sorted in ascending order.
     191             :      */
     192             :     void testPivotTableCache();
     193             : 
     194             :     /**
     195             :      * Test for pivot table containing data fields that reference the same
     196             :      * source field but different functions.
     197             :      */
     198             :     void testPivotTableDuplicateDataFields();
     199             : 
     200             :     void testPivotTableNormalGrouping();
     201             :     void testPivotTableNumberGrouping();
     202             :     void testPivotTableDateGrouping();
     203             :     void testPivotTableEmptyRows();
     204             :     void testPivotTableTextNumber();
     205             : 
     206             :     /**
     207             :      * Test for checking that pivot table treats strings in a case insensitive
     208             :      * manner.
     209             :      */
     210             :     void testPivotTableCaseInsensitiveStrings();
     211             : 
     212             :     /**
     213             :      * Test for pivot table's handling of double-precision numbers that are
     214             :      * very close together.
     215             :      */
     216             :     void testPivotTableNumStability();
     217             : 
     218             :     /**
     219             :      * Test for pivot table that include field with various non-default field
     220             :      * refrences.
     221             :      */
     222             :     void testPivotTableFieldReference();
     223             : 
     224             :     /**
     225             :      * Test pivot table functionality performed via ScDBDocFunc.
     226             :      */
     227             :     void testPivotTableDocFunc();
     228             : 
     229             :     void testSheetCopy();
     230             :     void testSheetMove();
     231             :     void testExternalRef();
     232             :     void testExternalRefFunctions();
     233             :     void testDataArea();
     234             :     void testAutofilter();
     235             :     void testCopyPaste();
     236             :     void testMergedCells();
     237             :     void testUpdateReference();
     238             :     void testSearchCells();
     239             :     void testSharedFormulas();
     240             :     void testFormulaPosition();
     241             : 
     242             :     /**
     243             :      * Make sure the sheet streams are invalidated properly.
     244             :      */
     245             :     void testStreamValid();
     246             : 
     247             :     /**
     248             :      * Test built-in cell functions to make sure their categories and order
     249             :      * are correct.
     250             :      */
     251             :     void testFunctionLists();
     252             : 
     253             :     void testGraphicsInGroup();
     254             :     void testGraphicsOnSheetMove();
     255             : 
     256             :     void testPostIts();
     257             : 
     258             :     /**
     259             :      * Test toggling relative/absolute flag of cell and cell range references.
     260             :      * This corresponds with hitting Shift-F4 while the cursor is on a formula
     261             :      * cell.
     262             :      */
     263             :     void testToggleRefFlag();
     264             : 
     265             :     /**
     266             :      * Test to make sure correct precedent / dependent cells are obtained when
     267             :      * preparing to jump to them.
     268             :      */
     269             :     void testJumpToPrecedentsDependents();
     270             : 
     271             :     void testSetBackgroundColor();
     272             :     void testRenameTable();
     273             : 
     274             :     void testAutoFill();
     275             :     void testCopyPasteFormulas();
     276             :     void testCopyPasteFormulasExternalDoc();
     277             : 
     278             :     void testFindAreaPosVertical();
     279             :     void testFindAreaPosColRight();
     280             :     void testSort();
     281             :     void testSortWithFormulaRefs();
     282             :     void testShiftCells();
     283             :     void testDeleteRow();
     284             :     void testDeleteCol();
     285             :     void testAnchoredRotatedShape();
     286             :     void testCellTextWidth();
     287             : 
     288             :     /**
     289             :      * Test formula & formula grouping
     290             :      */
     291             :     void testFormulaGrouping();
     292             :     void testCondFormatINSDEL();
     293             : 
     294           2 :     CPPUNIT_TEST_SUITE(Test);
     295             : #if CALC_TEST_PERF
     296             :     CPPUNIT_TEST(testPerf);
     297             : #endif
     298           1 :     CPPUNIT_TEST(testCollator);
     299           1 :     CPPUNIT_TEST(testRangeList);
     300           1 :     CPPUNIT_TEST(testInput);
     301           1 :     CPPUNIT_TEST(testFormulaHashAndTag);
     302           1 :     CPPUNIT_TEST(testFuncSUM);
     303           1 :     CPPUNIT_TEST(testFuncPRODUCT);
     304           1 :     CPPUNIT_TEST(testFuncN);
     305           1 :     CPPUNIT_TEST(testFuncCOUNTIF);
     306           1 :     CPPUNIT_TEST(testFuncNUMBERVALUE);
     307           1 :     CPPUNIT_TEST(testFuncVLOOKUP);
     308           1 :     CPPUNIT_TEST(testFuncMATCH);
     309           1 :     CPPUNIT_TEST(testFuncCELL);
     310           1 :     CPPUNIT_TEST(testFuncDATEDIF);
     311           1 :     CPPUNIT_TEST(testFuncINDIRECT);
     312           1 :     CPPUNIT_TEST(testFuncIFERROR);
     313           1 :     CPPUNIT_TEST(testCopyToDocument);
     314           1 :     CPPUNIT_TEST(testSheetsFunc);
     315           1 :     CPPUNIT_TEST(testVolatileFunc);
     316           1 :     CPPUNIT_TEST(testHorizontalIterator);
     317           1 :     CPPUNIT_TEST(testFormulaDepTracking);
     318           1 :     CPPUNIT_TEST(testFormulaDepTracking2);
     319           1 :     CPPUNIT_TEST(testCellBroadcaster);
     320           1 :     CPPUNIT_TEST(testFuncParam);
     321           1 :     CPPUNIT_TEST(testNamedRange);
     322           1 :     CPPUNIT_TEST(testCSV);
     323           1 :     CPPUNIT_TEST(testMatrix);
     324           1 :     CPPUNIT_TEST(testEnterMixedMatrix);
     325           1 :     CPPUNIT_TEST(testPivotTable);
     326           1 :     CPPUNIT_TEST(testPivotTableLabels);
     327           1 :     CPPUNIT_TEST(testPivotTableDateLabels);
     328           1 :     CPPUNIT_TEST(testPivotTableFilters);
     329           1 :     CPPUNIT_TEST(testPivotTableNamedSource);
     330           1 :     CPPUNIT_TEST(testPivotTableCache);
     331           1 :     CPPUNIT_TEST(testPivotTableDuplicateDataFields);
     332           1 :     CPPUNIT_TEST(testPivotTableNormalGrouping);
     333           1 :     CPPUNIT_TEST(testPivotTableNumberGrouping);
     334           1 :     CPPUNIT_TEST(testPivotTableDateGrouping);
     335           1 :     CPPUNIT_TEST(testPivotTableEmptyRows);
     336           1 :     CPPUNIT_TEST(testPivotTableTextNumber);
     337           1 :     CPPUNIT_TEST(testPivotTableCaseInsensitiveStrings);
     338           1 :     CPPUNIT_TEST(testPivotTableNumStability);
     339           1 :     CPPUNIT_TEST(testPivotTableFieldReference);
     340           1 :     CPPUNIT_TEST(testPivotTableDocFunc);
     341           1 :     CPPUNIT_TEST(testSheetCopy);
     342           1 :     CPPUNIT_TEST(testSheetMove);
     343           1 :     CPPUNIT_TEST(testExternalRef);
     344           1 :     CPPUNIT_TEST(testExternalRefFunctions);
     345           1 :     CPPUNIT_TEST(testDataArea);
     346           1 :     CPPUNIT_TEST(testGraphicsInGroup);
     347           1 :     CPPUNIT_TEST(testGraphicsOnSheetMove);
     348           1 :     CPPUNIT_TEST(testPostIts);
     349           1 :     CPPUNIT_TEST(testStreamValid);
     350           1 :     CPPUNIT_TEST(testFunctionLists);
     351           1 :     CPPUNIT_TEST(testToggleRefFlag);
     352           1 :     CPPUNIT_TEST(testAutofilter);
     353           1 :     CPPUNIT_TEST(testCopyPaste);
     354           1 :     CPPUNIT_TEST(testMergedCells);
     355           1 :     CPPUNIT_TEST(testUpdateReference);
     356           1 :     CPPUNIT_TEST(testSearchCells);
     357           1 :     CPPUNIT_TEST(testSharedFormulas);
     358           1 :     CPPUNIT_TEST(testFormulaPosition);
     359           1 :     CPPUNIT_TEST(testJumpToPrecedentsDependents);
     360           1 :     CPPUNIT_TEST(testSetBackgroundColor);
     361           1 :     CPPUNIT_TEST(testRenameTable);
     362           1 :     CPPUNIT_TEST(testAutoFill);
     363           1 :     CPPUNIT_TEST(testCopyPasteFormulas);
     364           1 :     CPPUNIT_TEST(testCopyPasteFormulasExternalDoc);
     365           1 :     CPPUNIT_TEST(testFindAreaPosVertical);
     366           1 :     CPPUNIT_TEST(testFindAreaPosColRight);
     367           1 :     CPPUNIT_TEST(testSort);
     368           1 :     CPPUNIT_TEST(testSortWithFormulaRefs);
     369           1 :     CPPUNIT_TEST(testShiftCells);
     370           1 :     CPPUNIT_TEST(testDeleteRow);
     371           1 :     CPPUNIT_TEST(testDeleteCol);
     372           1 :     CPPUNIT_TEST(testAnchoredRotatedShape);
     373           1 :     CPPUNIT_TEST(testCellTextWidth);
     374           1 :     CPPUNIT_TEST(testFormulaGrouping);
     375           1 :     CPPUNIT_TEST(testCondFormatINSDEL);
     376           2 :     CPPUNIT_TEST_SUITE_END();
     377             : 
     378             : private:
     379             :     ScDocument *m_pDoc;
     380             :     ScDocShellRef m_xDocShRef;
     381             : };
     382             : 
     383          42 : void clearRange(ScDocument* pDoc, const ScRange& rRange)
     384             : {
     385          42 :     ScMarkData aMarkData;
     386          42 :     aMarkData.SetMarkArea(rRange);
     387             :     pDoc->DeleteArea(
     388          42 :         rRange.aStart.Col(), rRange.aStart.Row(),
     389          84 :         rRange.aEnd.Col(), rRange.aEnd.Row(), aMarkData, IDF_CONTENTS);
     390          42 : }
     391             : 
     392          30 : void printRange(ScDocument* pDoc, const ScRange& rRange, const char* pCaption)
     393             : {
     394          30 :     SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
     395          30 :     SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
     396          30 :     SheetPrinter printer(nRow2 - nRow1 + 1, nCol2 - nCol1 + 1);
     397         299 :     for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
     398             :     {
     399         976 :         for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
     400             :         {
     401         707 :             OUString aVal = pDoc->GetString(nCol, nRow, rRange.aStart.Tab());
     402         707 :             printer.set(nRow-nRow1, nCol-nCol1, aVal);
     403         707 :         }
     404             :     }
     405          30 :     printer.print(pCaption);
     406          30 : }
     407             : 
     408             : template<size_t _Size>
     409          17 : ScRange insertRangeData(ScDocument* pDoc, const ScAddress& rPos, const char* aData[][_Size], size_t nRowCount)
     410             : {
     411          17 :     ScRange aRange(rPos);
     412          17 :     aRange.aEnd.SetCol(rPos.Col()+_Size-1);
     413          17 :     aRange.aEnd.SetRow(rPos.Row()+nRowCount-1);
     414             : 
     415          17 :     clearRange(pDoc, aRange);
     416             : 
     417          62 :     for (size_t i = 0; i < _Size; ++i)
     418             :     {
     419         436 :         for (size_t j = 0; j < nRowCount; ++j)
     420             :         {
     421         391 :             if (!aData[j][i])
     422           7 :                 continue;
     423             : 
     424         384 :             SCCOL nCol = i + rPos.Col();
     425         384 :             SCROW nRow = j + rPos.Row();
     426         384 :             pDoc->SetString(nCol, nRow, rPos.Tab(), OUString(aData[j][i], strlen(aData[j][i]), RTL_TEXTENCODING_UTF8));
     427             :         }
     428             :     }
     429             : 
     430          17 :     printRange(pDoc, aRange, "Range data content");
     431          17 :     return aRange;
     432             : }
     433             : 
     434             : /**
     435             :  * Temporarily set formula grammar.
     436             :  */
     437             : class FormulaGrammarSwitch
     438             : {
     439             :     ScDocument* mpDoc;
     440             :     formula::FormulaGrammar::Grammar meOldGrammar;
     441             : public:
     442           3 :     FormulaGrammarSwitch(ScDocument* pDoc, formula::FormulaGrammar::Grammar eGrammar) :
     443           3 :         mpDoc(pDoc), meOldGrammar(pDoc->GetGrammar())
     444             :     {
     445           3 :         mpDoc->SetGrammar(eGrammar);
     446           3 :     }
     447             : 
     448           3 :     ~FormulaGrammarSwitch()
     449             :     {
     450           3 :         mpDoc->SetGrammar(meOldGrammar);
     451           3 :     }
     452             : };
     453             : 
     454             : class MeasureTimeSwitch
     455             : {
     456             :     double& mrDiff;
     457             :     TimeValue maTimeBefore;
     458             : public:
     459           0 :     MeasureTimeSwitch(double& rDiff) : mrDiff(rDiff)
     460             :     {
     461           0 :         mrDiff = 9999.0;
     462           0 :         osl_getSystemTime(&maTimeBefore);
     463           0 :     }
     464             : 
     465           0 :     ~MeasureTimeSwitch()
     466             :     {
     467             :         TimeValue aTimeAfter;
     468           0 :         osl_getSystemTime(&aTimeAfter);
     469           0 :         mrDiff = getTimeDiff(aTimeAfter, maTimeBefore);
     470           0 :     }
     471             : 
     472           0 :     double getTimeDiff(const TimeValue& t1, const TimeValue& t2) const
     473             :     {
     474           0 :         double tv1 = t1.Seconds;
     475           0 :         double tv2 = t2.Seconds;
     476           0 :         tv1 += t1.Nanosec / 1000000000.0;
     477           0 :         tv2 += t2.Nanosec / 1000000000.0;
     478             : 
     479           0 :         return tv1 - tv2;
     480             :     }
     481             : };
     482             : 
     483          78 : Test::Test()
     484          78 :     : m_pDoc(0)
     485             : {
     486          78 : }
     487             : 
     488          78 : void Test::setUp()
     489             : {
     490          78 :     BootstrapFixture::setUp();
     491             : 
     492          78 :     ScDLL::Init();
     493         156 :     m_xDocShRef = new ScDocShell(
     494             :         SFXMODEL_STANDARD |
     495             :         SFXMODEL_DISABLE_EMBEDDED_SCRIPTS |
     496         156 :         SFXMODEL_DISABLE_DOCUMENT_RECOVERY);
     497             : 
     498          78 :     m_xDocShRef->DoInitUnitTest();
     499          78 :     m_pDoc = m_xDocShRef->GetDocument();
     500          78 : }
     501             : 
     502          78 : void Test::tearDown()
     503             : {
     504          78 :     m_xDocShRef.Clear();
     505          78 :     BootstrapFixture::tearDown();
     506          78 : }
     507             : 
     508             : #define PERF_ASSERT(df,scale,time,message) \
     509             :     do { \
     510             :         double dfscaled = df / scale; \
     511             :         if ((dfscaled) >= (time)) \
     512             :         { \
     513             :             std::ostringstream os; \
     514             :             os << message << " took " << dfscaled << " pseudo-cycles (" << df << " real-time seconds), expected: " << time << " pseudo-cycles."; \
     515             :             CPPUNIT_FAIL(os.str().c_str()); \
     516             :         } \
     517             :     } while (0)
     518             : 
     519           0 : void Test::testPerf()
     520             : {
     521           0 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
     522             : 
     523             :     // First do a set of simple operations to try to work out
     524             :     // how fast (or not) this particular machine is:
     525             :     double scale;
     526             :     {
     527           0 :         MeasureTimeSwitch aTime(scale);
     528           0 :         for (int i = 0; i < 10000000; ++i)
     529             :         {
     530             :             // Bang on the allocator
     531           0 :             volatile ScRange *pRange = new ScRange (ScAddress (0,0,0));
     532             :             // Calc does quite a bit of string conversion
     533           0 :             volatile double it = OUString::number ((double)i/253.0).toDouble();
     534             :             // Do we have floating point math ?
     535           0 :             volatile double another = rtl::math::sin (it);
     536           0 :             (void)another;
     537           0 :             delete pRange;
     538           0 :         }
     539             :     }
     540           0 :     printf("CPU scale factor %g\n", scale);
     541             : 
     542             :     // FIXME: we should check if this already took too long
     543             :     // and if so not run the perf. tests to have pity on some
     544             :     // slow ARM machines - I think.
     545             : 
     546             :     // to make the numbers more round and helpful,
     547             :     // but the calculation of scale reasonably precise.
     548           0 :     scale /= 100000.0;
     549             : 
     550             :     double diff;
     551             : 
     552             :     // Clearing an already empty sheet should finish in a fraction of a
     553             :     // second.  Flag failure if it takes more than one second.  Clearing 100
     554             :     // columns should be large enough to flag if something goes wrong.
     555             :     {
     556           0 :         MeasureTimeSwitch aTime(diff);
     557           0 :         clearRange(m_pDoc, ScRange(0,0,0,99,MAXROW,0));
     558             :     }
     559           0 :     PERF_ASSERT(diff, scale, 1.0, "Clearing an empty sheet");
     560             : 
     561             :     {
     562             :         // Switch to R1C1 to make it easier to input relative references in multiple cells.
     563           0 :         FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
     564             : 
     565             :         // Insert formulas in B1:B100000. This shouldn't take long, but may take
     566             :         // close to a second on a slower machine. We don't measure this yet, for
     567             :         // now.
     568           0 :         for (SCROW i = 0; i < 100000; ++i)
     569           0 :             m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
     570             : 
     571             :         // Now, Delete B2:B100000. This should complete in a fraction of a second
     572             :         // (0.06 sec on my machine).
     573             :         {
     574           0 :             MeasureTimeSwitch aTime(diff);
     575           0 :             clearRange(m_pDoc, ScRange(1,1,0,1,99999,0));
     576             :         }
     577           0 :         PERF_ASSERT(diff, scale, 2000, "Removal of a large array of formula cells");
     578             :     }
     579             : 
     580           0 :     clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
     581           0 :     CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
     582           0 :     CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
     583             : 
     584             :     {
     585             :         // Measure the performance of repeat-pasting a single cell to a large
     586             :         // cell range, undoing it, and redoing it.
     587             : 
     588           0 :         ScAddress aPos(0,0,0);
     589           0 :         m_pDoc->SetString(aPos, "test");
     590           0 :         ScMarkData aMark;
     591           0 :         aMark.SelectOneTable(0);
     592             : 
     593             :         // Copy cell A1 to clipboard.
     594           0 :         ScDocument aClipDoc(SCDOCMODE_CLIP);
     595           0 :         ScClipParam aParam(aPos, false);
     596           0 :         m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
     597           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
     598             : 
     599           0 :         ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
     600           0 :         pUndoDoc->InitUndo(m_pDoc, 0, 0);
     601           0 :         m_pDoc->CopyToDocument(ScRange(aPos), IDF_CONTENTS, false, pUndoDoc, &aMark);
     602             : 
     603             :         // Paste it to A2:A100000, and measure its duration.
     604           0 :         ScRange aPasteRange(0,1,0,0,99999,0);
     605           0 :         aMark.SetMarkArea(aPasteRange);
     606             : 
     607             :         {
     608           0 :             MeasureTimeSwitch aTime(diff);
     609           0 :             m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
     610             :         }
     611           0 :         PERF_ASSERT(diff, scale, 1500.0, "Pasting a single cell to A2:A100000");
     612             : 
     613           0 :         ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
     614           0 :         pRedoDoc->InitUndo(m_pDoc, 0, 0);
     615           0 :         m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
     616             : 
     617             :         // Create an undo object for this.
     618           0 :         ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
     619           0 :         ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
     620             : 
     621             :         // Make sure it did what it's supposed to do.
     622           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
     623           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
     624             : 
     625             :         {
     626           0 :             MeasureTimeSwitch aTime(diff);
     627           0 :             aUndo.Undo();
     628             :         }
     629           0 :         PERF_ASSERT(diff, scale, 500.0, "Undoing a pasting of a cell to A2:A100000");
     630             : 
     631             :         // Make sure it's really undone.
     632           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_STRING, m_pDoc->GetCellType(aPos));
     633           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
     634           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
     635             : 
     636             :         // Now redo.
     637             :         {
     638           0 :             MeasureTimeSwitch aTime(diff);
     639           0 :             aUndo.Redo();
     640             :         }
     641           0 :         PERF_ASSERT(diff, scale, 1000.0, "Redoing a pasting of a cell to A2:A100000");
     642             : 
     643             :         // Make sure it's really redone.
     644           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
     645           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
     646             :     }
     647             : 
     648           0 :     clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
     649           0 :     CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
     650           0 :     CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
     651             : 
     652             :     {
     653             :         // Measure the performance of repeat-pasting 2 adjacent cells to a
     654             :         // large cell range, undoing it, and redoing it.  The bottom one of
     655             :         // the two source cells is empty.
     656             : 
     657           0 :         ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
     658             : 
     659           0 :         ScAddress aPos(0,0,0);
     660           0 :         m_pDoc->SetValue(aPos, 12);
     661           0 :         ScMarkData aMark;
     662           0 :         aMark.SetMarkArea(aSrcRange);
     663             : 
     664             :         // Copy to clipboard.
     665           0 :         ScDocument aClipDoc(SCDOCMODE_CLIP);
     666           0 :         ScClipParam aParam(aSrcRange, false);
     667           0 :         m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
     668           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
     669             : 
     670           0 :         ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
     671           0 :         pUndoDoc->InitUndo(m_pDoc, 0, 0);
     672           0 :         m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
     673             : 
     674             :         // Paste it to A3:A100001, and measure its duration.
     675           0 :         ScRange aPasteRange(0,2,0,0,100000,0);
     676           0 :         aMark.SetMarkArea(aPasteRange);
     677             : 
     678             :         {
     679           0 :             MeasureTimeSwitch aTime(diff);
     680           0 :             m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
     681             :         }
     682           0 :         PERF_ASSERT(diff, scale, 1000.0, "Pasting A1:A2 to A3:A100001");
     683             : 
     684           0 :         ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
     685           0 :         pRedoDoc->InitUndo(m_pDoc, 0, 0);
     686           0 :         m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
     687             : 
     688             :         // Create an undo object for this.
     689           0 :         ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
     690           0 :         ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
     691             : 
     692             :         // Make sure it did what it's supposed to do.
     693           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
     694           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
     695           0 :         ScAddress aTmp = aPasteRange.aStart;
     696           0 :         aTmp.IncRow();
     697           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
     698             : 
     699             :         {
     700           0 :             MeasureTimeSwitch aTime(diff);
     701           0 :             aUndo.Undo();
     702             :         }
     703           0 :         PERF_ASSERT(diff, scale, 500.0, "Undoing");
     704             : 
     705             :         // Make sure it's really undone.
     706           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(aPos));
     707           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
     708           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
     709             : 
     710             :         // Now redo.
     711             :         {
     712           0 :             MeasureTimeSwitch aTime(diff);
     713           0 :             aUndo.Redo();
     714             :         }
     715           0 :         PERF_ASSERT(diff, scale, 800.0, "Redoing");
     716             : 
     717             :         // Make sure it's really redone.
     718           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
     719           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
     720             :     }
     721             : 
     722           0 :     clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
     723           0 :     CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
     724           0 :     CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
     725             : 
     726             :     {
     727             :         // Measure the performance of repeat-pasting 2 adjacent cells to a
     728             :         // large cell range, undoing it, and redoing it.  The bottom one of
     729             :         // the two source cells is empty.  In this scenario, the non-empty
     730             :         // cell is a formula cell referencing a cell to the right, which
     731             :         // inserts a broadcaster to cell it references. So it has a higher
     732             :         // overhead than the previous scenario.
     733             : 
     734           0 :         ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
     735             : 
     736           0 :         ScAddress aPos(0,0,0);
     737           0 :         m_pDoc->SetString(aPos, "=B1");
     738           0 :         ScMarkData aMark;
     739           0 :         aMark.SetMarkArea(aSrcRange);
     740             : 
     741             :         // Copy to clipboard.
     742           0 :         ScDocument aClipDoc(SCDOCMODE_CLIP);
     743           0 :         ScClipParam aParam(aSrcRange, false);
     744           0 :         m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
     745           0 :         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
     746             : 
     747           0 :         ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
     748           0 :         pUndoDoc->InitUndo(m_pDoc, 0, 0);
     749           0 :         m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
     750             : 
     751             :         // Paste it to A3:A50001, and measure its duration.
     752           0 :         ScRange aPasteRange(0,2,0,0,50000,0);
     753           0 :         aMark.SetMarkArea(aPasteRange);
     754             : 
     755             :         {
     756           0 :             MeasureTimeSwitch aTime(diff);
     757           0 :             m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
     758             :         }
     759           0 :         PERF_ASSERT(diff, scale, 2000.0, "Pasting");
     760             : 
     761           0 :         ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
     762           0 :         pRedoDoc->InitUndo(m_pDoc, 0, 0);
     763           0 :         m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
     764             : 
     765             :         // Create an undo object for this.
     766           0 :         ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
     767           0 :         ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
     768             : 
     769             :         // Make sure it did what it's supposed to do.
     770           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
     771           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
     772           0 :         ScAddress aTmp = aPasteRange.aStart;
     773           0 :         aTmp.IncRow();
     774           0 :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
     775             : 
     776             : #if 0 // TODO: Undo and redo of this scenario is currently not fast enough to be tested reliably.
     777             :         {
     778             :             MeasureTimeSwitch aTime(diff);
     779             :             aUndo.Undo();
     780             :         }
     781             :         PERF_ASSERT(diff, scale, 1.0, "Undoing");
     782             : 
     783             :         // Make sure it's really undone.
     784             :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPos));
     785             :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
     786             :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
     787             : 
     788             :         // Now redo.
     789             :         {
     790             :             MeasureTimeSwitch aTime(diff);
     791             :             aUndo.Redo();
     792             :         }
     793             :         PERF_ASSERT(diff, scale, 1.0, "Redoing");
     794             : 
     795             :         // Make sure it's really redone.
     796             :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
     797             :         CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
     798             : #endif
     799             :     }
     800             : 
     801           0 :     m_pDoc->DeleteTab(0);
     802           0 : }
     803             : 
     804           1 : void Test::testCollator()
     805             : {
     806           1 :     OUString s1("A");
     807           2 :     OUString s2("B");
     808           1 :     CollatorWrapper* p = ScGlobal::GetCollator();
     809           1 :     sal_Int32 nRes = p->compareString(s1, s2);
     810           2 :     CPPUNIT_ASSERT_MESSAGE("these strings are supposed to be different!", nRes != 0);
     811           1 : }
     812             : 
     813           1 : void Test::testRangeList()
     814             : {
     815           1 :     m_pDoc->InsertTab(0, "foo");
     816             : 
     817           1 :     ScRangeList aRL;
     818           1 :     aRL.Append(ScRange(1,1,0,3,10,0));
     819           1 :     CPPUNIT_ASSERT_MESSAGE("List should have one range.", aRL.size() == 1);
     820           1 :     const ScRange* p = aRL[0];
     821           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to get the range object.", p);
     822           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong range.", p->aStart == ScAddress(1,1,0) && p->aEnd == ScAddress(3,10,0));
     823             : 
     824             :     // TODO: Add more tests here.
     825             : 
     826           1 :     m_pDoc->DeleteTab(0);
     827           1 : }
     828             : 
     829           1 : void Test::testInput()
     830             : {
     831           1 :     OUString aTabName("foo");
     832           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
     833           1 :                             m_pDoc->InsertTab (0, aTabName));
     834             : 
     835           2 :     OUString numstr("'10.5");
     836           2 :     OUString str("'apple'");
     837           2 :     OUString test;
     838             : 
     839           1 :     m_pDoc->SetString(0, 0, 0, numstr);
     840           1 :     test = m_pDoc->GetString(0, 0, 0);
     841           1 :     bool bTest = test == "10.5";
     842           1 :     CPPUNIT_ASSERT_MESSAGE("String number should have the first apostrophe stripped.", bTest);
     843           1 :     m_pDoc->SetString(0, 0, 0, str);
     844           1 :     test = m_pDoc->GetString(0, 0, 0);
     845           1 :     bTest = test == "'apple'";
     846           1 :     CPPUNIT_ASSERT_MESSAGE("Text content should have retained the first apostrophe.", bTest);
     847             : 
     848             :     // Customized string handling policy.
     849           1 :     ScSetStringParam aParam;
     850           1 :     aParam.setTextInput();
     851           1 :     m_pDoc->SetString(0, 0, 0, "000123", &aParam);
     852           1 :     test = m_pDoc->GetString(0, 0, 0);
     853           1 :     CPPUNIT_ASSERT_MESSAGE("Text content should have been treated as string, not number.", test == "000123");
     854             : 
     855           2 :     m_pDoc->DeleteTab(0);
     856           1 : }
     857             : 
     858           1 : void Test::testFuncSUM()
     859             : {
     860           1 :     OUString aTabName("foo");
     861           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
     862           1 :                             m_pDoc->InsertTab (0, aTabName));
     863             : 
     864           1 :     double val = 1;
     865             :     double result;
     866           1 :     m_pDoc->SetValue (0, 0, 0, val);
     867           1 :     m_pDoc->SetValue (0, 1, 0, val);
     868           1 :     m_pDoc->SetString (0, 2, 0, OUString("=SUM(A1:A2)"));
     869           1 :     m_pDoc->CalcAll();
     870           1 :     m_pDoc->GetValue (0, 2, 0, result);
     871           1 :     CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 2.0);
     872             : 
     873           1 :     m_pDoc->DeleteTab(0);
     874           1 : }
     875             : 
     876           1 : void Test::testFuncPRODUCT()
     877             : {
     878           1 :     OUString aTabName("foo");
     879           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
     880           1 :                             m_pDoc->InsertTab (0, aTabName));
     881             : 
     882           1 :     double val = 1;
     883             :     double result;
     884           1 :     m_pDoc->SetValue(0, 0, 0, val);
     885           1 :     val = 2;
     886           1 :     m_pDoc->SetValue(0, 1, 0, val);
     887           1 :     val = 3;
     888           1 :     m_pDoc->SetValue(0, 2, 0, val);
     889           1 :     m_pDoc->SetString(0, 3, 0, OUString("=PRODUCT(A1:A3)"));
     890           1 :     m_pDoc->CalcAll();
     891           1 :     m_pDoc->GetValue(0, 3, 0, result);
     892           1 :     CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT failed", result == 6.0);
     893             : 
     894           1 :     m_pDoc->SetString(0, 4, 0, OUString("=PRODUCT({1;2;3})"));
     895           1 :     m_pDoc->CalcAll();
     896           1 :     m_pDoc->GetValue(0, 4, 0, result);
     897           1 :     CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT with inline array failed", result == 6.0);
     898             : 
     899           1 :     m_pDoc->DeleteTab(0);
     900           1 : }
     901             : 
     902           1 : void Test::testFuncN()
     903             : {
     904           1 :     OUString aTabName("foo");
     905           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
     906           1 :                             m_pDoc->InsertTab (0, aTabName));
     907             : 
     908             :     double result;
     909             : 
     910             :     // Clear the area first.
     911           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
     912             : 
     913             :     // Put values to reference.
     914           1 :     double val = 0;
     915           1 :     m_pDoc->SetValue(0, 0, 0, val);
     916           1 :     m_pDoc->SetString(0, 2, 0, OUString("Text"));
     917           1 :     val = 1;
     918           1 :     m_pDoc->SetValue(0, 3, 0, val);
     919           1 :     val = -1;
     920           1 :     m_pDoc->SetValue(0, 4, 0, val);
     921           1 :     val = 12.3;
     922           1 :     m_pDoc->SetValue(0, 5, 0, val);
     923           1 :     m_pDoc->SetString(0, 6, 0, OUString("'12.3"));
     924             : 
     925             :     // Cell references
     926           1 :     m_pDoc->SetString(1, 0, 0, OUString("=N(A1)"));
     927           1 :     m_pDoc->SetString(1, 1, 0, OUString("=N(A2)"));
     928           1 :     m_pDoc->SetString(1, 2, 0, OUString("=N(A3)"));
     929           1 :     m_pDoc->SetString(1, 3, 0, OUString("=N(A4)"));
     930           1 :     m_pDoc->SetString(1, 4, 0, OUString("=N(A5)"));
     931           1 :     m_pDoc->SetString(1, 5, 0, OUString("=N(A6)"));
     932           1 :     m_pDoc->SetString(1, 6, 0, OUString("=N(A9)"));
     933             : 
     934             :     // In-line values
     935           1 :     m_pDoc->SetString(1, 7, 0, OUString("=N(0)"));
     936           1 :     m_pDoc->SetString(1, 8, 0, OUString("=N(1)"));
     937           1 :     m_pDoc->SetString(1, 9, 0, OUString("=N(-1)"));
     938           1 :     m_pDoc->SetString(1, 10, 0, OUString("=N(123)"));
     939           1 :     m_pDoc->SetString(1, 11, 0, OUString("=N(\"\")"));
     940           1 :     m_pDoc->SetString(1, 12, 0, OUString("=N(\"12\")"));
     941           1 :     m_pDoc->SetString(1, 13, 0, OUString("=N(\"foo\")"));
     942             : 
     943             :     // Range references
     944           1 :     m_pDoc->SetString(2, 2, 0, OUString("=N(A1:A8)"));
     945           1 :     m_pDoc->SetString(2, 3, 0, OUString("=N(A1:A8)"));
     946           1 :     m_pDoc->SetString(2, 4, 0, OUString("=N(A1:A8)"));
     947           1 :     m_pDoc->SetString(2, 5, 0, OUString("=N(A1:A8)"));
     948             : 
     949             :     // Calculate and check the results.
     950           1 :     m_pDoc->CalcAll();
     951             :     double checks1[] = {
     952             :         0, 0,  0,    1, -1, 12.3, 0, // cell reference
     953             :         0, 1, -1, 123,  0,    0, 0   // in-line values
     954           1 :     };
     955          15 :     for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
     956             :     {
     957          14 :         m_pDoc->GetValue(1, i, 0, result);
     958          14 :         bool bGood = result == checks1[i];
     959          14 :         if (!bGood)
     960             :         {
     961           0 :             cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
     962           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
     963             :         }
     964             :     }
     965             :     double checks2[] = {
     966             :         0, 1, -1, 12.3               // range references
     967           1 :     };
     968           5 :     for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
     969             :     {
     970           4 :         m_pDoc->GetValue(1, i+2, 0, result);
     971           4 :         bool bGood = result == checks2[i];
     972           4 :         if (!bGood)
     973             :         {
     974           0 :             cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
     975           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
     976             :         }
     977             :     }
     978             : 
     979           1 :     m_pDoc->DeleteTab(0);
     980           1 : }
     981             : 
     982           1 : void Test::testFuncCOUNTIF()
     983             : {
     984             :     // COUNTIF (test case adopted from OOo i#36381)
     985             : 
     986           1 :     OUString aTabName("foo");
     987           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
     988           1 :                             m_pDoc->InsertTab (0, aTabName));
     989             : 
     990             :     // Empty A1:A39 first.
     991           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
     992             : 
     993             :     // Raw data (rows 1 through 9)
     994             :     const char* aData[] = {
     995             :         "1999",
     996             :         "2000",
     997             :         "0",
     998             :         "0",
     999             :         "0",
    1000             :         "2002",
    1001             :         "2001",
    1002             :         "X",
    1003             :         "2002"
    1004           1 :     };
    1005             : 
    1006           1 :     SCROW nRows = SAL_N_ELEMENTS(aData);
    1007          10 :     for (SCROW i = 0; i < nRows; ++i)
    1008           9 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
    1009             : 
    1010           1 :     printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
    1011             : 
    1012             :     // formulas and results
    1013             :     struct {
    1014             :         const char* pFormula; double fResult;
    1015             :     } aChecks[] = {
    1016             :         { "=COUNTIF(A1:A12;1999)",       1 },
    1017             :         { "=COUNTIF(A1:A12;2002)",       2 },
    1018             :         { "=COUNTIF(A1:A12;1998)",       0 },
    1019             :         { "=COUNTIF(A1:A12;\">=1999\")", 5 },
    1020             :         { "=COUNTIF(A1:A12;\">1999\")",  4 },
    1021             :         { "=COUNTIF(A1:A12;\"<2001\")",  5 },
    1022             :         { "=COUNTIF(A1:A12;\">0\")",     5 },
    1023             :         { "=COUNTIF(A1:A12;\">=0\")",    8 },
    1024             :         { "=COUNTIF(A1:A12;0)",          3 },
    1025             :         { "=COUNTIF(A1:A12;\"X\")",      1 },
    1026             :         { "=COUNTIF(A1:A12;)",           3 }
    1027           1 :     };
    1028             : 
    1029           1 :     nRows = SAL_N_ELEMENTS(aChecks);
    1030          12 :     for (SCROW i = 0; i < nRows; ++i)
    1031             :     {
    1032          11 :         SCROW nRow = 20 + i;
    1033          11 :         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
    1034             :     }
    1035           1 :     m_pDoc->CalcAll();
    1036             : 
    1037          12 :     for (SCROW i = 0; i < nRows; ++i)
    1038             :     {
    1039             :         double result;
    1040          11 :         SCROW nRow = 20 + i;
    1041          11 :         m_pDoc->GetValue(0, nRow, 0, result);
    1042          11 :         bool bGood = result == aChecks[i].fResult;
    1043          11 :         if (!bGood)
    1044             :         {
    1045           0 :             cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
    1046           0 :                 << "  expected=" << aChecks[i].fResult << "  actual=" << result << endl;
    1047           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
    1048             :         }
    1049             :     }
    1050             : 
    1051             :     // Don't count empty strings when searching for a number.
    1052             : 
    1053             :     // Clear A1:A2.
    1054           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
    1055             : 
    1056           1 :     m_pDoc->SetString(0, 0, 0, OUString("=\"\""));
    1057           1 :     m_pDoc->SetString(0, 1, 0, OUString("=COUNTIF(A1;1)"));
    1058           1 :     m_pDoc->CalcAll();
    1059             : 
    1060           1 :     double result = m_pDoc->GetValue(0, 1, 0);
    1061           1 :     CPPUNIT_ASSERT_MESSAGE("We shouldn't count empty string as valid number.", result == 0.0);
    1062             : 
    1063           1 :     m_pDoc->DeleteTab(0);
    1064           1 : }
    1065             : 
    1066           1 : void Test::testFuncIFERROR()
    1067             : {
    1068             :     // IFERROR/IFNA (fdo#56124)
    1069             : 
    1070           1 :     OUString aTabName("foo");
    1071           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1072           1 :                             m_pDoc->InsertTab (0, aTabName));
    1073             : 
    1074             :     // Empty A1:A39 first.
    1075           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
    1076             : 
    1077             :     // Raw data (rows 1 through 12)
    1078             :     const char* aData[] = {
    1079             :         "1",
    1080             :         "e",
    1081             :         "=SQRT(4)",
    1082             :         "=SQRT(-2)",
    1083             :         "=A4",
    1084             :         "=1/0",
    1085             :         "=NA()",
    1086             :         "bar",
    1087             :         "4",
    1088             :         "gee",
    1089             :         "=1/0",
    1090             :         "23"
    1091           1 :     };
    1092             : 
    1093           1 :     SCROW nRows = SAL_N_ELEMENTS(aData);
    1094          13 :     for (SCROW i = 0; i < nRows; ++i)
    1095          12 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
    1096             : 
    1097           1 :     printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
    1098             : 
    1099             :     // formulas and results
    1100             :     struct {
    1101             :         const char* pFormula; const char* pResult;
    1102             :     } aChecks[] = {
    1103             :         { "=IFERROR(A1;9)",                         "1" },
    1104             :         { "=IFERROR(A2;9)",                         "e" },
    1105             :         { "=IFERROR(A3;9)",                         "2" },
    1106             :         { "=IFERROR(A4;-7)",                       "-7" },
    1107             :         { "=IFERROR(A5;-7)",                       "-7" },
    1108             :         { "=IFERROR(A6;-7)",                       "-7" },
    1109             :         { "=IFERROR(A7;-7)",                       "-7" },
    1110             :         { "=IFNA(A6;9)",                      "#DIV/0!" },
    1111             :         { "=IFNA(A7;-7)",                          "-7" },
    1112             :         { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)",    "4" },
    1113             :         { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
    1114             :         { "{=IFERROR(3*A11:A12;1998)}[0]",       "1998" },  // um.. this is not the correct way to insert a
    1115             :         { "{=IFERROR(3*A11:A12;1998)}[1]",         "69" }   // matrix formula, just a place holder, see below
    1116           1 :     };
    1117             : 
    1118           1 :     nRows = SAL_N_ELEMENTS(aChecks);
    1119          12 :     for (SCROW i = 0; i < nRows-2; ++i)
    1120             :     {
    1121          11 :         SCROW nRow = 20 + i;
    1122          11 :         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
    1123             :     }
    1124             : 
    1125             :     // Create a matrix range in last two rows of the range above, actual data
    1126             :     // of the placeholders.
    1127           2 :     ScMarkData aMark;
    1128           1 :     aMark.SelectOneTable(0);
    1129           1 :     m_pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)", NULL);
    1130             : 
    1131           1 :     m_pDoc->CalcAll();
    1132             : 
    1133          14 :     for (SCROW i = 0; i < nRows; ++i)
    1134             :     {
    1135          13 :         SCROW nRow = 20 + i;
    1136          13 :         OUString aResult = m_pDoc->GetString(0, nRow, 0);
    1137          26 :         CPPUNIT_ASSERT_EQUAL_MESSAGE(
    1138          13 :             aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
    1139          13 :     }
    1140             : 
    1141           2 :     m_pDoc->DeleteTab(0);
    1142           1 : }
    1143             : 
    1144           1 : void Test::testFuncNUMBERVALUE()
    1145             : {
    1146             :     // NUMBERVALUE fdo#57180
    1147             : 
    1148           1 :     OUString aTabName("foo");
    1149           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1150           1 :                             m_pDoc->InsertTab (0, aTabName));
    1151             : 
    1152             :     // Empty A1:A39 first.
    1153           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
    1154             : 
    1155             :     // Raw data (rows 1 through 6)
    1156             :     const char* aData[] = {
    1157             :         "1ag9a9b9",
    1158             :         "1ag34 5g g6  78b9%%",
    1159             :         "1 234d56E-2",
    1160             :         "d4",
    1161             :         "54.4",
    1162             :         "1a2b3e1%"
    1163           1 :     };
    1164             : 
    1165           1 :     SCROW nRows = SAL_N_ELEMENTS(aData);
    1166           7 :     for (SCROW i = 0; i < nRows; ++i)
    1167           6 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
    1168             : 
    1169           1 :     printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
    1170             : 
    1171             :     // formulas and results
    1172             :     struct {
    1173             :         const char* pFormula; const char* pResult;
    1174             :     } aChecks[] = {
    1175             :         { "=NUMBERVALUE(A1;\"b\";\"ag\")",  "199.9" },
    1176             :         { "=NUMBERVALUE(A2;\"b\";\"ag\")",  "134.56789" },
    1177             :         { "=NUMBERVALUE(A2;\"b\";\"g\")",   "#VALUE!" },
    1178             :         { "=NUMBERVALUE(A3;\"d\")",         "12.3456" },
    1179             :         { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
    1180             :         { "=NUMBERVALUE(A4;)",              "Err:502" },
    1181             :         { "=NUMBERVALUE(A5;)",              "Err:502" },
    1182             :         { "=NUMBERVALUE(A6;\"b\";\"a\")",   "1.23" }
    1183           1 :     };
    1184             : 
    1185           1 :     nRows = SAL_N_ELEMENTS(aChecks);
    1186           9 :     for (SCROW i = 0; i < nRows; ++i)
    1187             :     {
    1188           8 :         SCROW nRow = 20 + i;
    1189           8 :         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
    1190             :     }
    1191           1 :     m_pDoc->CalcAll();
    1192             : 
    1193           9 :     for (SCROW i = 0; i < nRows; ++i)
    1194             :     {
    1195           8 :         SCROW nRow = 20 + i;
    1196           8 :         OUString aResult = m_pDoc->GetString(0, nRow, 0);
    1197          16 :         CPPUNIT_ASSERT_EQUAL_MESSAGE(
    1198           8 :             aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
    1199           8 :     }
    1200             : 
    1201           1 :     m_pDoc->DeleteTab(0);
    1202           1 : }
    1203             : 
    1204           1 : void Test::testFuncVLOOKUP()
    1205             : {
    1206             :     // VLOOKUP
    1207             : 
    1208           1 :     OUString aTabName("foo");
    1209           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1210           1 :                             m_pDoc->InsertTab (0, aTabName));
    1211             : 
    1212             :     // Clear A1:F40.
    1213           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 5, 39, 0));
    1214             : 
    1215             :     // Raw data
    1216             :     const char* aData[][2] = {
    1217             :         { "Key", "Val" },
    1218             :         {  "10",   "3" },
    1219             :         {  "20",   "4" },
    1220             :         {  "30",   "5" },
    1221             :         {  "40",   "6" },
    1222             :         {  "50",   "7" },
    1223             :         {  "60",   "8" },
    1224             :         {  "70",   "9" },
    1225             :         {   "B",  "10" },
    1226             :         {   "B",  "11" },
    1227             :         {   "C",  "12" },
    1228             :         {   "D",  "13" },
    1229             :         {   "E",  "14" },
    1230             :         {   "F",  "15" },
    1231             :         { 0, 0 } // terminator
    1232           1 :     };
    1233             : 
    1234             :     // Insert raw data into A1:B14.
    1235          15 :     for (SCROW i = 0; aData[i][0]; ++i)
    1236             :     {
    1237          14 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
    1238          14 :         m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
    1239             :     }
    1240             : 
    1241           1 :     printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
    1242             : 
    1243             :     // Formula data
    1244             :     struct {
    1245             :         const char* pLookup; const char* pFormula; const char* pRes;
    1246             :     } aChecks[] = {
    1247             :         { "Lookup",  "Formula", 0 },
    1248             :         { "12",      "=VLOOKUP(D2;A2:B14;2;1)",     "3" },
    1249             :         { "29",      "=VLOOKUP(D3;A2:B14;2;1)",     "4" },
    1250             :         { "31",      "=VLOOKUP(D4;A2:B14;2;1)",     "5" },
    1251             :         { "45",      "=VLOOKUP(D5;A2:B14;2;1)",     "6" },
    1252             :         { "56",      "=VLOOKUP(D6;A2:B14;2;1)",     "7" },
    1253             :         { "65",      "=VLOOKUP(D7;A2:B14;2;1)",     "8" },
    1254             :         { "78",      "=VLOOKUP(D8;A2:B14;2;1)",     "9" },
    1255             :         { "Andy",    "=VLOOKUP(D9;A2:B14;2;1)",  "#N/A" },
    1256             :         { "Bruce",   "=VLOOKUP(D10;A2:B14;2;1)",   "11" },
    1257             :         { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)",   "12" },
    1258             :         { "David",   "=VLOOKUP(D12;A2:B14;2;1)",   "13" },
    1259             :         { "Edward",  "=VLOOKUP(D13;A2:B14;2;1)",   "14" },
    1260             :         { "Frank",   "=VLOOKUP(D14;A2:B14;2;1)",   "15" },
    1261             :         { "Henry",   "=VLOOKUP(D15;A2:B14;2;1)",   "15" },
    1262             :         { "100",     "=VLOOKUP(D16;A2:B14;2;1)",    "9" },
    1263             :         { "1000",    "=VLOOKUP(D17;A2:B14;2;1)",    "9" },
    1264             :         { "Zena",    "=VLOOKUP(D18;A2:B14;2;1)",   "15" }
    1265           1 :     };
    1266             : 
    1267             :     // Insert formula data into D1:E18.
    1268          19 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    1269             :     {
    1270          18 :         m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
    1271          18 :         m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
    1272             :     }
    1273           1 :     m_pDoc->CalcAll();
    1274           1 :     printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
    1275             : 
    1276             :     // Verify results.
    1277          19 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    1278             :     {
    1279          18 :         if (i == 0)
    1280             :             // Skip the header row.
    1281           1 :             continue;
    1282             : 
    1283          17 :         OUString aRes = m_pDoc->GetString(4, i, 0);
    1284          17 :         bool bGood = aRes.equalsAscii(aChecks[i].pRes);
    1285          17 :         if (!bGood)
    1286             :         {
    1287           0 :             cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
    1288           0 :                 << "'  expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
    1289           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
    1290             :         }
    1291          17 :     }
    1292             : 
    1293           1 :     m_pDoc->DeleteTab(0);
    1294           1 : }
    1295             : 
    1296             : struct NumStrCheck {
    1297             :     double fVal;
    1298             :     const char* pRes;
    1299             : };
    1300             : 
    1301             : struct StrStrCheck {
    1302             :     const char* pVal;
    1303             :     const char* pRes;
    1304             : };
    1305             : 
    1306             : template<size_t _DataSize, size_t _FormulaSize, int _Type>
    1307           2 : void runTestMATCH(ScDocument* pDoc, const char* aData[_DataSize], StrStrCheck aChecks[_FormulaSize])
    1308             : {
    1309           2 :     size_t nDataSize = _DataSize;
    1310          26 :     for (size_t i = 0; i < nDataSize; ++i)
    1311          24 :         pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
    1312             : 
    1313          33 :     for (size_t i = 0; i < _FormulaSize; ++i)
    1314             :     {
    1315          31 :         pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
    1316             : 
    1317          31 :         OUStringBuffer aBuf;
    1318          31 :         aBuf.appendAscii("=MATCH(B");
    1319          31 :         aBuf.append(static_cast<sal_Int32>(i+1));
    1320          31 :         aBuf.appendAscii(";A1:A");
    1321          31 :         aBuf.append(static_cast<sal_Int32>(nDataSize));
    1322          31 :         aBuf.appendAscii(";");
    1323          31 :         aBuf.append(static_cast<sal_Int32>(_Type));
    1324          31 :         aBuf.appendAscii(")");
    1325          62 :         OUString aFormula = aBuf.makeStringAndClear();
    1326          31 :         pDoc->SetString(2, i, 0, aFormula);
    1327             :     }
    1328             : 
    1329           2 :     pDoc->CalcAll();
    1330           2 :     printRange(pDoc, ScRange(0, 0, 0, 2, _FormulaSize-1, 0), "MATCH");
    1331             : 
    1332             :     // verify the results.
    1333          33 :     for (size_t i = 0; i < _FormulaSize; ++i)
    1334             :     {
    1335          31 :         OUString aStr = pDoc->GetString(2, i, 0);
    1336          31 :         if (!aStr.equalsAscii(aChecks[i].pRes))
    1337             :         {
    1338           0 :             cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
    1339           0 :                 << " criterion='" << aChecks[i].pVal << "'" << endl;
    1340           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
    1341             :         }
    1342             :     }
    1343           2 : }
    1344             : 
    1345           1 : void Test::testFuncMATCH()
    1346             : {
    1347           1 :     OUString aTabName("foo");
    1348           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1349           1 :                             m_pDoc->InsertTab (0, aTabName));
    1350             : 
    1351           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
    1352             :     {
    1353             :         // Ascending in-exact match
    1354             : 
    1355             :         // data range (A1:A9)
    1356             :         const char* aData[] = {
    1357             :             "1",
    1358             :             "2",
    1359             :             "3",
    1360             :             "4",
    1361             :             "5",
    1362             :             "6",
    1363             :             "7",
    1364             :             "8",
    1365             :             "9",
    1366             :             "B",
    1367             :             "B",
    1368             :             "C",
    1369           1 :         };
    1370             : 
    1371             :         // formula (B1:C12)
    1372             :         StrStrCheck aChecks[] = {
    1373             :             { "0.8",   "#N/A" },
    1374             :             { "1.2",      "1" },
    1375             :             { "2.3",      "2" },
    1376             :             { "3.9",      "3" },
    1377             :             { "4.1",      "4" },
    1378             :             { "5.99",     "5" },
    1379             :             { "6.1",      "6" },
    1380             :             { "7.2",      "7" },
    1381             :             { "8.569",    "8" },
    1382             :             { "9.59",     "9" },
    1383             :             { "10",       "9" },
    1384             :             { "100",      "9" },
    1385             :             { "Andy",  "#N/A" },
    1386             :             { "Bruce",   "11" },
    1387             :             { "Charlie", "12" }
    1388           1 :         };
    1389             : 
    1390           1 :         runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
    1391             :     }
    1392             : 
    1393             :     {
    1394             :         // Descending in-exact match
    1395             : 
    1396             :         // data range (A1:A9)
    1397             :         const char* aData[] = {
    1398             :             "D",
    1399             :             "C",
    1400             :             "B",
    1401             :             "9",
    1402             :             "8",
    1403             :             "7",
    1404             :             "6",
    1405             :             "5",
    1406             :             "4",
    1407             :             "3",
    1408             :             "2",
    1409             :             "1"
    1410           1 :         };
    1411             : 
    1412             :         // formula (B1:C12)
    1413             :         StrStrCheck aChecks[] = {
    1414             :             { "10",      "#N/A" },
    1415             :             { "8.9",     "4" },
    1416             :             { "7.8",     "5" },
    1417             :             { "6.7",     "6" },
    1418             :             { "5.5",     "7" },
    1419             :             { "4.6",     "8" },
    1420             :             { "3.3",     "9" },
    1421             :             { "2.2",     "10" },
    1422             :             { "1.1",     "11" },
    1423             :             { "0.8",     "12" },
    1424             :             { "0",       "12" },
    1425             :             { "-2",      "12" },
    1426             :             { "Andy",    "3" },
    1427             :             { "Bruce",   "2" },
    1428             :             { "Charlie", "1" },
    1429             :             { "David", "#N/A" }
    1430           1 :         };
    1431             : 
    1432           1 :         runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
    1433             :     }
    1434             : 
    1435           1 :     m_pDoc->DeleteTab(0);
    1436           1 : }
    1437             : 
    1438           1 : void Test::testFuncCELL()
    1439             : {
    1440           1 :     OUString aTabName("foo");
    1441           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1442           1 :                             m_pDoc->InsertTab (0, aTabName));
    1443             : 
    1444           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
    1445             : 
    1446             :     {
    1447           1 :         const char* pContent = "Some random text";
    1448           1 :         m_pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
    1449           1 :         double val = 1.2;
    1450           1 :         m_pDoc->SetValue(2, 0, 0, val); // Set numeric value to C1;
    1451             : 
    1452             :         // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
    1453             :         StrStrCheck aChecks[] = {
    1454             :             { "=CELL(\"COL\";C10)",           "3" },
    1455             :             { "=CELL(\"ROW\";C10)",          "10" },
    1456             :             { "=CELL(\"SHEET\";C10)",         "1" },
    1457             :             { "=CELL(\"ADDRESS\";C10)",   "$C$10" },
    1458             :             { "=CELL(\"CONTENTS\";C10)", pContent },
    1459             :             { "=CELL(\"COLOR\";C10)",         "0" },
    1460             :             { "=CELL(\"TYPE\";C9)",           "b" },
    1461             :             { "=CELL(\"TYPE\";C10)",          "l" },
    1462             :             { "=CELL(\"TYPE\";C1)",           "v" },
    1463             :             { "=CELL(\"PARENTHESES\";C10)",   "0" }
    1464           1 :         };
    1465             : 
    1466          11 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    1467          10 :             m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
    1468           1 :         m_pDoc->CalcAll();
    1469             : 
    1470          11 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    1471             :         {
    1472          10 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    1473          10 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
    1474          10 :         }
    1475             :     }
    1476             : 
    1477           1 :     m_pDoc->DeleteTab(0);
    1478           1 : }
    1479             : 
    1480             : /** See also test case document fdo#44456 sheet cpearson */
    1481           1 : void Test::testFuncDATEDIF()
    1482             : {
    1483           1 :     OUString aTabName("foo");
    1484           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1485           1 :                             m_pDoc->InsertTab (0, aTabName));
    1486             : 
    1487             :     const char* aData[][5] = {
    1488             :         { "2007-01-01", "2007-01-10",  "d",   "9", "=DATEDIF(A1;B1;C1)" } ,
    1489             :         { "2007-01-01", "2007-01-31",  "m",   "0", "=DATEDIF(A2;B2;C2)" } ,
    1490             :         { "2007-01-01", "2007-02-01",  "m",   "1", "=DATEDIF(A3;B3;C3)" } ,
    1491             :         { "2007-01-01", "2007-02-28",  "m",   "1", "=DATEDIF(A4;B4;C4)" } ,
    1492             :         { "2007-01-01", "2007-12-31",  "d", "364", "=DATEDIF(A5;B5;C5)" } ,
    1493             :         { "2007-01-01", "2007-01-31",  "y",   "0", "=DATEDIF(A6;B6;C6)" } ,
    1494             :         { "2007-01-01", "2008-07-01",  "d", "547", "=DATEDIF(A7;B7;C7)" } ,
    1495             :         { "2007-01-01", "2008-07-01",  "m",  "18", "=DATEDIF(A8;B8;C8)" } ,
    1496             :         { "2007-01-01", "2008-07-01", "ym",   "6", "=DATEDIF(A9;B9;C9)" } ,
    1497             :         { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
    1498             :         { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
    1499             :         { "2007-01-01", "2007-01-31", "md",  "30", "=DATEDIF(A12;B12;C12)" } ,
    1500             :         { "2007-02-01", "2009-03-01", "md",   "0", "=DATEDIF(A13;B13;C13)" } ,
    1501             :         { "2008-02-01", "2009-03-01", "md",   "0", "=DATEDIF(A14;B14;C14)" } ,
    1502             :         { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" }    // fail date1 > date2
    1503           1 :     };
    1504             : 
    1505           1 :     clearRange( m_pDoc, ScRange(0, 0, 0, 4, SAL_N_ELEMENTS(aData), 0));
    1506           1 :     ScAddress aPos(0,0,0);
    1507           1 :     ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    1508           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    1509             : 
    1510           1 :     m_pDoc->CalcAll();
    1511             : 
    1512          16 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aData); ++i)
    1513             :     {
    1514          15 :         OUString aVal = m_pDoc->GetString( 4, i, 0);
    1515             :         //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
    1516          15 :         CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
    1517          15 :     }
    1518             : 
    1519           1 :     m_pDoc->DeleteTab(0);
    1520           1 : }
    1521             : 
    1522           1 : void Test::testFuncINDIRECT()
    1523             : {
    1524           1 :     OUString aTabName("foo");
    1525           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1526           1 :                             m_pDoc->InsertTab (0, aTabName));
    1527           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
    1528             : 
    1529           1 :     bool bGood = m_pDoc->GetName(0, aTabName);
    1530           1 :     CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
    1531             : 
    1532           2 :     OUString aTest = "Test", aRefErr = "#REF!";
    1533           1 :     m_pDoc->SetString(0, 10, 0, aTest);
    1534           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", m_pDoc->GetString(0,10,0) == aTest);
    1535             : 
    1536           2 :     OUString aPrefix = "=INDIRECT(\"";
    1537             : 
    1538           2 :     OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
    1539           1 :     m_pDoc->SetString(0, 0, 0, aFormula);
    1540           1 :     aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
    1541           1 :     m_pDoc->SetString(0, 1, 0, aFormula);
    1542           1 :     aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
    1543           1 :     m_pDoc->SetString(0, 2, 0, aFormula);
    1544           1 :     aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
    1545           1 :     m_pDoc->SetString(0, 3, 0, aFormula);
    1546             : 
    1547           1 :     m_pDoc->CalcAll();
    1548             :     {
    1549             :         // Default is to use the current formula syntax, which is Calc A1.
    1550             :         const OUString* aChecks[] = {
    1551             :             &aTest, &aRefErr, &aRefErr, &aTest
    1552           1 :         };
    1553             : 
    1554           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    1555             :         {
    1556           4 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    1557           4 :             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
    1558           4 :         }
    1559             :     }
    1560             : 
    1561           1 :     ScCalcConfig aConfig;
    1562           1 :     aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_OOO;
    1563           1 :     ScInterpreter::SetGlobalConfig(aConfig);
    1564           1 :     m_pDoc->CalcAll();
    1565             :     {
    1566             :         // Explicit Calc A1 syntax
    1567             :         const OUString* aChecks[] = {
    1568             :             &aTest, &aRefErr, &aRefErr, &aTest
    1569           1 :         };
    1570             : 
    1571           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    1572             :         {
    1573           4 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    1574           4 :             CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
    1575           4 :         }
    1576             :     }
    1577             : 
    1578           1 :     aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_A1;
    1579           1 :     ScInterpreter::SetGlobalConfig(aConfig);
    1580           1 :     m_pDoc->CalcAll();
    1581             :     {
    1582             :         // Excel A1 syntax
    1583             :         const OUString* aChecks[] = {
    1584             :             &aRefErr, &aTest, &aRefErr, &aTest
    1585           1 :         };
    1586             : 
    1587           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    1588             :         {
    1589           4 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    1590           4 :             CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
    1591           4 :         }
    1592             :     }
    1593             : 
    1594           1 :     aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_R1C1;
    1595           1 :     ScInterpreter::SetGlobalConfig(aConfig);
    1596           1 :     m_pDoc->CalcAll();
    1597             :     {
    1598             :         // Excel R1C1 syntax
    1599             :         const OUString* aChecks[] = {
    1600             :             &aRefErr, &aRefErr, &aTest, &aTest
    1601           1 :         };
    1602             : 
    1603           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    1604             :         {
    1605           4 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    1606           4 :             CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
    1607           4 :         }
    1608             :     }
    1609             : 
    1610           2 :     m_pDoc->DeleteTab(0);
    1611           1 : }
    1612             : 
    1613           1 : void Test::testFormulaHashAndTag()
    1614             : {
    1615           1 :     m_pDoc->InsertTab(0, "Test");
    1616             : 
    1617           1 :     ScAddress aPos1(0,0,0), aPos2(1,0,0);
    1618             : 
    1619             :     // Test formula hashing.
    1620             : 
    1621             :     struct {
    1622             :         const char* pFormula1; const char* pFormula2; bool bEqual;
    1623             :     } aHashTests[] = {
    1624             :         { "=1", "=2", false }, // different constants
    1625             :         { "=SUM(1;2;3;4;5)", "=AVERAGE(1;2;3;4;5)", false }, // different functions
    1626             :         { "=C2*3", "=D2*3", true },  // relative references
    1627             :         { "=C2*3", "=D2*4", false }, // different constants
    1628             :         { "=C2*4", "=D2*4", true },  // relative references
    1629             :         { "=3*4*5", "=3*4*\"foo\"", false }, // numeric vs string constants
    1630             :         { "=$C3/2", "=$C3/2", true }, // absolute column references
    1631             :         { "=C$3/2", "=D$3/2", true }, // absolute row references
    1632             :         { "=$E$30/2", "=$E$30/2", true }, // absolute references
    1633             :         { "=X20", "=$X$20", false }, // absolute vs relative
    1634             :         { "=X20", "=X$20", false }, // absolute vs relative
    1635             :         { "=X20", "=$X20", false }, // absolute vs relative
    1636             :         { "=X$20", "=$X20", false }, // column absolute vs row absolute
    1637             :         // similar enough for merging ...
    1638             :         { "=A1", "=B1", true },
    1639             :         { "=$A$1", "=$B$1", true },
    1640             :         { "=A1", "=C2", true },
    1641             :         { "=SUM(A1)", "=SUM(B1)", true },
    1642             :         { "=A1+3", "=B1+3", true },
    1643             :         { "=A1+7", "=B1+42", false },
    1644           1 :     };
    1645             : 
    1646          20 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aHashTests); ++i)
    1647             :     {
    1648          19 :         m_pDoc->SetString(aPos1, OUString::createFromAscii(aHashTests[i].pFormula1));
    1649          19 :         m_pDoc->SetString(aPos2, OUString::createFromAscii(aHashTests[i].pFormula2));
    1650          19 :         size_t nHashVal1 = m_pDoc->GetFormulaHash(aPos1);
    1651          19 :         size_t nHashVal2 = m_pDoc->GetFormulaHash(aPos2);
    1652             : 
    1653          19 :         std::ostringstream os;
    1654          19 :         os << "(expr1:" << aHashTests[i].pFormula1 << "; expr2:" << aHashTests[i].pFormula2 << ")";
    1655          19 :         if (aHashTests[i].bEqual)
    1656             :         {
    1657          10 :             os << " Error: these hashes should be equal." << endl;
    1658          10 :             CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 == nHashVal2);
    1659             :         }
    1660             :         else
    1661             :         {
    1662           9 :             os << " Error: these hashes should differ." << endl;
    1663           9 :             CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 != nHashVal2);
    1664             :         }
    1665             : 
    1666          19 :         aPos1.IncRow();
    1667          19 :         aPos2.IncRow();
    1668          19 :     }
    1669             : 
    1670             :     // Go back to row 1.
    1671           1 :     aPos1.SetRow(0);
    1672           1 :     aPos2.SetRow(0);
    1673             : 
    1674             :     // Test formula vectorization state.
    1675             : 
    1676             :     struct {
    1677             :         const char* pFormula; ScFormulaVectorState eState;
    1678             :     } aVectorTests[] = {
    1679             :         { "=SUM(1;2;3;4;5)", FormulaVectorEnabled },
    1680             :         { "=NOW()", FormulaVectorDisabled },
    1681             :         { "=AVERAGE(X1:Y200)", FormulaVectorCheckReference },
    1682             :         { "=MAX(X1:Y200;10;20)", FormulaVectorCheckReference },
    1683             :         { "=MIN(10;11;22)", FormulaVectorEnabled },
    1684             :         { "=H4", FormulaVectorCheckReference },
    1685           1 :     };
    1686             : 
    1687           7 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aVectorTests); ++i)
    1688             :     {
    1689           6 :         m_pDoc->SetString(aPos1, OUString::createFromAscii(aVectorTests[i].pFormula));
    1690           6 :         ScFormulaVectorState eState = m_pDoc->GetFormulaVectorState(aPos1);
    1691             : 
    1692           6 :         if (eState != aVectorTests[i].eState)
    1693             :         {
    1694           0 :             std::ostringstream os;
    1695           0 :             os << "Unexpected vectorization state: expr:" << aVectorTests[i].pFormula;
    1696           0 :             CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
    1697             :         }
    1698           6 :         aPos1.IncRow();
    1699             :     }
    1700             : 
    1701           1 :     m_pDoc->DeleteTab(0);
    1702           1 : }
    1703             : 
    1704           1 : void Test::testCopyToDocument()
    1705             : {
    1706           1 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "src"));
    1707             : 
    1708           1 :     m_pDoc->SetString(0, 0, 0, "Header");
    1709           1 :     m_pDoc->SetString(0, 1, 0, "1");
    1710           1 :     m_pDoc->SetString(0, 2, 0, "2");
    1711           1 :     m_pDoc->SetString(0, 3, 0, "3");
    1712           1 :     m_pDoc->SetString(0, 4, 0, "=4/2");
    1713           1 :     m_pDoc->CalcAll();
    1714             : 
    1715             :     // Copy statically to another document.
    1716             : 
    1717           1 :     ScDocument aDestDoc(SCDOCMODE_DOCUMENT);
    1718           1 :     aDestDoc.InsertTab(0, "src");
    1719           1 :     m_pDoc->CopyStaticToDocument(ScRange(0,1,0,0,3,0), 0, &aDestDoc); // Copy A2:A4
    1720           1 :     m_pDoc->CopyStaticToDocument(ScAddress(0,0,0), 0, &aDestDoc); // Copy A1
    1721           1 :     m_pDoc->CopyStaticToDocument(ScRange(0,4,0,0,7,0), 0, &aDestDoc); // Copy A5:A8
    1722             : 
    1723           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), aDestDoc.GetString(0,0,0));
    1724           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,1,0), aDestDoc.GetString(0,1,0));
    1725           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,2,0), aDestDoc.GetString(0,2,0));
    1726           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,3,0), aDestDoc.GetString(0,3,0));
    1727           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,4,0), aDestDoc.GetString(0,4,0));
    1728             : 
    1729           1 :     m_pDoc->DeleteTab(0);
    1730           1 : }
    1731             : 
    1732           1 : void Test::testSheetsFunc()
    1733             : {
    1734           1 :     OUString aTabName1("test1");
    1735           2 :     OUString aTabName2("test2");
    1736           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1737           1 :                             m_pDoc->InsertTab (SC_TAB_APPEND, aTabName1));
    1738             : 
    1739           1 :     m_pDoc->SetString(0, 0, 0, OUString("=SHEETS()"));
    1740           1 :     m_pDoc->CalcFormulaTree(false, false);
    1741             :     double original;
    1742           1 :     m_pDoc->GetValue(0, 0, 0, original);
    1743             : 
    1744           2 :     CPPUNIT_ASSERT_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
    1745           1 :                            static_cast<SCTAB>(original) == m_pDoc->GetTableCount());
    1746             : 
    1747           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1748           1 :                             m_pDoc->InsertTab (SC_TAB_APPEND, aTabName2));
    1749             : 
    1750             :     double modified;
    1751           1 :     m_pDoc->GetValue(0, 0, 0, modified);
    1752           2 :     CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
    1753           1 :                            modified - original == 1.0);
    1754             : 
    1755           1 :     SCTAB nTabCount = m_pDoc->GetTableCount();
    1756           1 :     m_pDoc->DeleteTab(--nTabCount);
    1757             : 
    1758           1 :     m_pDoc->GetValue(0, 0, 0, modified);
    1759           2 :     CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
    1760           1 :                            modified - original == 0.0);
    1761             : 
    1762           2 :     m_pDoc->DeleteTab(--nTabCount);
    1763           1 : }
    1764             : 
    1765           1 : void Test::testVolatileFunc()
    1766             : {
    1767           1 :     OUString aTabName("foo");
    1768           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    1769           1 :                             m_pDoc->InsertTab (0, aTabName));
    1770             : 
    1771           1 :     double val = 1;
    1772           1 :     m_pDoc->SetValue(0, 0, 0, val);
    1773           1 :     m_pDoc->SetString(0, 1, 0, OUString("=IF(A1>0;NOW();0"));
    1774             :     double now1;
    1775           1 :     m_pDoc->GetValue(0, 1, 0, now1);
    1776           1 :     CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
    1777             : 
    1778           1 :     val = 0;
    1779           1 :     m_pDoc->SetValue(0, 0, 0, val);
    1780           1 :     m_pDoc->CalcFormulaTree(false, false);
    1781             :     double zero;
    1782           1 :     m_pDoc->GetValue(0, 1, 0, zero);
    1783           1 :     CPPUNIT_ASSERT_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", zero == 0.0);
    1784             : 
    1785           1 :     val = 1;
    1786           1 :     m_pDoc->SetValue(0, 0, 0, val);
    1787           1 :     m_pDoc->CalcFormulaTree(false, false);
    1788             :     double now2;
    1789           1 :     m_pDoc->GetValue(0, 1, 0, now2);
    1790           1 :     CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
    1791             : 
    1792           1 :     m_pDoc->DeleteTab(0);
    1793           1 : }
    1794             : 
    1795             : namespace {
    1796             : 
    1797             : struct HoriIterCheck
    1798             : {
    1799             :     SCCOL nCol;
    1800             :     SCROW nRow;
    1801             :     const char* pVal;
    1802             : };
    1803             : 
    1804             : template<size_t _Size>
    1805           2 : bool checkHorizontalIterator(ScDocument* pDoc, const char* pData[][_Size], size_t nDataCount, const HoriIterCheck* pChecks, size_t nCheckCount)
    1806             : {
    1807           2 :     ScAddress aPos(0,0,0);
    1808           2 :     insertRangeData(pDoc, aPos, pData, nDataCount);
    1809           2 :     ScHorizontalCellIterator aIter(pDoc, 0, 0, 0, 1, nDataCount-1);
    1810             : 
    1811             :     SCCOL nCol;
    1812             :     SCROW nRow;
    1813           2 :     size_t i = 0;
    1814          15 :     for (ScRefCellValue* pCell = aIter.GetNext(nCol, nRow); pCell; pCell = aIter.GetNext(nCol, nRow), ++i)
    1815             :     {
    1816          13 :         if (i >= nCheckCount)
    1817           0 :             CPPUNIT_FAIL("Iterator claims there is more data than there should be.");
    1818             : 
    1819          13 :         if (pChecks[i].nCol != nCol)
    1820           0 :             return false;
    1821             : 
    1822          13 :         if (pChecks[i].nRow != nRow)
    1823           0 :             return false;
    1824             : 
    1825          13 :         if (OUString::createFromAscii(pChecks[i].pVal) != pCell->getString())
    1826           0 :             return false;
    1827             :     }
    1828             : 
    1829           2 :     return true;
    1830             : }
    1831             : 
    1832             : }
    1833             : 
    1834           1 : void Test::testHorizontalIterator()
    1835             : {
    1836           1 :     m_pDoc->InsertTab(0, "test");
    1837             : 
    1838             :     {
    1839             :         // Raw data
    1840             :         const char* aData[][2] = {
    1841             :             { "A", "B" },
    1842             :             { "C", "1" },
    1843             :             { "D", "2" },
    1844             :             { "E", "3" }
    1845           1 :         };
    1846             : 
    1847             :         HoriIterCheck aChecks[] = {
    1848             :             { 0, 0, "A" },
    1849             :             { 1, 0, "B" },
    1850             :             { 0, 1, "C" },
    1851             :             { 1, 1, "1" },
    1852             :             { 0, 2, "D" },
    1853             :             { 1, 2, "2" },
    1854             :             { 0, 3, "E" },
    1855             :             { 1, 3, "3" },
    1856           1 :         };
    1857             : 
    1858             :         bool bRes = checkHorizontalIterator(
    1859           1 :             m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
    1860             : 
    1861           1 :         if (!bRes)
    1862           0 :             CPPUNIT_FAIL("Failed on test 1.");
    1863             :     }
    1864             : 
    1865             :     {
    1866             :         // Raw data
    1867             :         const char* aData[][2] = {
    1868             :             { "A", "B" },
    1869             :             { "C",  0  },
    1870             :             { "D", "E" },
    1871           1 :         };
    1872             : 
    1873             :         HoriIterCheck aChecks[] = {
    1874             :             { 0, 0, "A" },
    1875             :             { 1, 0, "B" },
    1876             :             { 0, 1, "C" },
    1877             :             { 0, 2, "D" },
    1878             :             { 1, 2, "E" },
    1879           1 :         };
    1880             : 
    1881             :         bool bRes = checkHorizontalIterator(
    1882           1 :             m_pDoc, aData, SAL_N_ELEMENTS(aData), aChecks, SAL_N_ELEMENTS(aChecks));
    1883             : 
    1884           1 :         if (!bRes)
    1885           0 :             CPPUNIT_FAIL("Failed on test 2.");
    1886             :     }
    1887             : 
    1888           1 :     m_pDoc->DeleteTab(0);
    1889           1 : }
    1890             : 
    1891           1 : void Test::testFormulaDepTracking()
    1892             : {
    1893           1 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
    1894             : 
    1895           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    1896             : 
    1897             :     // B2 listens on D2.
    1898           1 :     m_pDoc->SetString(1, 1, 0, "=D2");
    1899           1 :     double val = -999.0; // dummy initial value
    1900           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1901           1 :     CPPUNIT_ASSERT_MESSAGE("Referencing an empty cell should yield zero.", val == 0.0);
    1902             : 
    1903             :     // Changing the value of D2 should trigger recalculation of B2.
    1904           1 :     m_pDoc->SetValue(3, 1, 0, 1.1);
    1905           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1906           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 1.1);
    1907             : 
    1908             :     // And again.
    1909           1 :     m_pDoc->SetValue(3, 1, 0, 2.2);
    1910           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1911           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 2.2);
    1912             : 
    1913           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
    1914             : 
    1915             :     // Now, let's test the range dependency tracking.
    1916             : 
    1917             :     // B2 listens on D2:E6.
    1918           1 :     m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
    1919           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1920           1 :     CPPUNIT_ASSERT_MESSAGE("Summing an empty range should yield zero.", val == 0.0);
    1921             : 
    1922             :     // Set value to E3. This should trigger recalc on B2.
    1923           1 :     m_pDoc->SetValue(4, 2, 0, 2.4);
    1924           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1925           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", val == 2.4);
    1926             : 
    1927             :     // Set value to D5 to trigger recalc again.  Note that this causes an
    1928             :     // addition of 1.2 + 2.4 which is subject to binary floating point
    1929             :     // rounding error.  We need to use approxEqual to assess its value.
    1930             : 
    1931           1 :     m_pDoc->SetValue(3, 4, 0, 1.2);
    1932           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1933           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
    1934             : 
    1935             :     // Change the value of D2 (boundary case).
    1936           1 :     m_pDoc->SetValue(3, 1, 0, 1.0);
    1937           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1938           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
    1939             : 
    1940             :     // Change the value of E6 (another boundary case).
    1941           1 :     m_pDoc->SetValue(4, 5, 0, 2.0);
    1942           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1943           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
    1944             : 
    1945             :     // Change the value of D6 (another boundary case).
    1946           1 :     m_pDoc->SetValue(3, 5, 0, 3.0);
    1947           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1948           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
    1949             : 
    1950             :     // Change the value of E2 (another boundary case).
    1951           1 :     m_pDoc->SetValue(4, 1, 0, 0.4);
    1952           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1953           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
    1954             : 
    1955             :     // Change the existing non-empty value cell (E2).
    1956           1 :     m_pDoc->SetValue(4, 1, 0, 2.4);
    1957           1 :     m_pDoc->GetValue(1, 1, 0, val);
    1958           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
    1959             : 
    1960           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
    1961             : 
    1962             :     // Now, column-based dependency tracking.  We now switch to the R1C1
    1963             :     // syntax which is easier to use for repeated relative references.
    1964             : 
    1965           2 :     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
    1966             : 
    1967           1 :     val = 0.0;
    1968          10 :     for (SCROW nRow = 1; nRow <= 9; ++nRow)
    1969             :     {
    1970             :         // Static value in column 1.
    1971           9 :         m_pDoc->SetValue(0, nRow, 0, ++val);
    1972             : 
    1973             :         // Formula in column 2 that references cell to the left.
    1974           9 :         m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
    1975             : 
    1976             :         // Formula in column 3 that references cell to the left.
    1977           9 :         m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
    1978             :     }
    1979             : 
    1980             :     // Check formula values.
    1981           1 :     val = 0.0;
    1982          10 :     for (SCROW nRow = 1; nRow <= 9; ++nRow)
    1983             :     {
    1984           9 :         ++val;
    1985           9 :         CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, nRow, 0) == val);
    1986           9 :         CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, nRow, 0) == val*2.0);
    1987             :     }
    1988             : 
    1989             :     // Intentionally insert a formula in column 1. This will break column 1's
    1990             :     // uniformity of consisting only of static value cells.
    1991           1 :     m_pDoc->SetString(0, 4, 0, "=R2C3");
    1992           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(0, 4, 0) == 2.0);
    1993           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, 4, 0) == 2.0);
    1994           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, 4, 0) == 4.0);
    1995             : 
    1996           2 :     m_pDoc->DeleteTab(0);
    1997           1 : }
    1998             : 
    1999           1 : void Test::testFormulaDepTracking2()
    2000             : {
    2001           1 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
    2002             : 
    2003           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    2004             : 
    2005           1 :     double val = 2.0;
    2006           1 :     m_pDoc->SetValue(0, 0, 0, val);
    2007           1 :     val = 4.0;
    2008           1 :     m_pDoc->SetValue(1, 0, 0, val);
    2009           1 :     val = 5.0;
    2010           1 :     m_pDoc->SetValue(0, 1, 0, val);
    2011           1 :     m_pDoc->SetString(2, 0, 0, "=A1/B1");
    2012           1 :     m_pDoc->SetString(1, 1, 0, "=B1*C1");
    2013             : 
    2014           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
    2015             : 
    2016           1 :     clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
    2017             : 
    2018           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
    2019             : 
    2020           1 :     m_pDoc->DeleteTab(0);
    2021           1 : }
    2022             : 
    2023             : namespace {
    2024             : 
    2025           7 : bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const ScAddress& rTo)
    2026             : {
    2027           7 :     const SvtBroadcaster* pBC = rDoc.GetBroadcaster(rFrom);
    2028           7 :     if (pBC)
    2029             :     {
    2030           0 :         cerr << "Broadcaster shouldn't be here." << endl;
    2031           0 :         return false;
    2032             :     }
    2033             : 
    2034           7 :     pBC = rDoc.GetBroadcaster(rTo);
    2035           7 :     if (!pBC)
    2036             :     {
    2037           0 :         cerr << "Broadcaster should be here." << endl;
    2038           0 :         return false;
    2039             :     }
    2040           7 :     return true;
    2041             : }
    2042             : 
    2043          11 : ScToken* getSingleRefToken(ScDocument& rDoc, const ScAddress& rPos)
    2044             : {
    2045          11 :     ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
    2046          11 :     if (!pFC)
    2047             :     {
    2048           0 :         cerr << "Formula cell expected, but not found." << endl;
    2049           0 :         return NULL;
    2050             :     }
    2051             : 
    2052          11 :     ScTokenArray* pTokens = pFC->GetCode();
    2053          11 :     if (!pTokens)
    2054             :     {
    2055           0 :         cerr << "Token array is not present." << endl;
    2056           0 :         return NULL;
    2057             :     }
    2058             : 
    2059          11 :     ScToken* pToken = static_cast<ScToken*>(pTokens->First());
    2060          11 :     if (!pToken || pToken->GetType() != formula::svSingleRef)
    2061             :     {
    2062           0 :         cerr << "Not a single reference token." << endl;
    2063           0 :         return NULL;
    2064             :     }
    2065             : 
    2066          11 :     return pToken;
    2067             : }
    2068             : 
    2069           9 : bool checkRelativeRefToken(ScDocument& rDoc, const ScAddress& rPos, SCsCOL nRelCol, SCsROW nRelRow)
    2070             : {
    2071           9 :     ScToken* pToken = getSingleRefToken(rDoc, rPos);
    2072           9 :     if (!pToken)
    2073           0 :         return false;
    2074             : 
    2075           9 :     ScSingleRefData& rRef = pToken->GetSingleRef();
    2076           9 :     if (!rRef.IsColRel() || rRef.nRelCol != nRelCol)
    2077             :     {
    2078           0 :         cerr << "Unexpected relative column address." << endl;
    2079           0 :         return false;
    2080             :     }
    2081             : 
    2082           9 :     if (!rRef.IsRowRel() || rRef.nRelRow != nRelRow)
    2083             :     {
    2084           0 :         cerr << "Unexpected relative row address." << endl;
    2085           0 :         return false;
    2086             :     }
    2087             : 
    2088           9 :     return true;
    2089             : }
    2090             : 
    2091           2 : bool checkDeletedRefToken(ScDocument& rDoc, const ScAddress& rPos)
    2092             : {
    2093           2 :     ScToken* pToken = getSingleRefToken(rDoc, rPos);
    2094           2 :     if (!pToken)
    2095           0 :         return false;
    2096             : 
    2097           2 :     ScSingleRefData& rRef = pToken->GetSingleRef();
    2098           2 :     if (!rRef.IsDeleted())
    2099             :     {
    2100           0 :         cerr << "Deleted reference is expected, but it's still a valid reference." << endl;
    2101           0 :         return false;
    2102             :     }
    2103             : 
    2104           2 :     return true;
    2105             : }
    2106             : 
    2107             : }
    2108             : 
    2109           1 : void Test::testCellBroadcaster()
    2110             : {
    2111           1 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
    2112             : 
    2113           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    2114           1 :     m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
    2115           1 :     double val = m_pDoc->GetValue(ScAddress(1,0,0)); // A1 is empty, so the result should be 0.
    2116           1 :     CPPUNIT_ASSERT_EQUAL(0.0, val);
    2117             : 
    2118           1 :     const SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
    2119           1 :     CPPUNIT_ASSERT_MESSAGE("Cell A1 should have a broadcaster.", pBC);
    2120             : 
    2121             :     // Change the value of A1 and make sure that B1 follows.
    2122           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1.23);
    2123           1 :     val = m_pDoc->GetValue(ScAddress(1,0,0));
    2124           1 :     CPPUNIT_ASSERT_EQUAL(1.23, val);
    2125             : 
    2126             :     // Move column A down 5 cells. Make sure B1 now references A6, not A1.
    2127           1 :     m_pDoc->InsertRow(0, 0, 0, 0, 0, 5);
    2128           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
    2129           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 5));
    2130             : 
    2131             :     // Make sure the broadcaster has also moved.
    2132           2 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
    2133           1 :                            broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,5,0)));
    2134             : 
    2135             :     // Set new value to A6 and make sure B1 gets updated.
    2136           1 :     m_pDoc->SetValue(ScAddress(0,5,0), 45.6);
    2137           1 :     val = m_pDoc->GetValue(ScAddress(1,0,0));
    2138           1 :     CPPUNIT_ASSERT_EQUAL(45.6, val);
    2139             : 
    2140             :     // Move column A up 3 cells, and make sure B1 now references A3, not A6.
    2141           1 :     m_pDoc->DeleteRow(0, 0, 0, 0, 0, 3);
    2142           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
    2143           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 2));
    2144             : 
    2145             :     // The broadcaster should also have been relocated from A6 to A3.
    2146           2 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
    2147           1 :                            broadcasterShifted(*m_pDoc, ScAddress(0,5,0), ScAddress(0,2,0)));
    2148             : 
    2149             :     // Insert cells over A1:A10 and shift cells to right.
    2150           1 :     m_pDoc->InsertCol(ScRange(0, 0, 0, 0, 10, 0));
    2151           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check failed.",
    2152           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(2,0,0), -1, 2));
    2153           2 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
    2154           1 :                            broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(1,2,0)));
    2155             : 
    2156             :     // Delete formula in C2, which should remove the broadcaster in B3.
    2157           1 :     pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
    2158           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should still exist.", pBC);
    2159           1 :     clearRange(m_pDoc, ScAddress(2,0,0));
    2160           1 :     CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(2,0,0))); // C2 should be empty.
    2161           1 :     pBC = m_pDoc->GetBroadcaster(ScAddress(1,2,0));
    2162           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster in B3 should have been removed.", !pBC);
    2163             : 
    2164             :     // Clear everything and start over.
    2165           1 :     clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
    2166             : 
    2167           1 :     m_pDoc->SetString(ScAddress(1,0,0), "=A1"); // B1 depends on A1.
    2168           1 :     pBC = m_pDoc->GetBroadcaster(ScAddress(0,0,0));
    2169           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", pBC);
    2170             : 
    2171             :     // While column A is still empty, move column A down 2 cells. This should
    2172             :     // move the broadcaster from A1 to A3.
    2173           1 :     m_pDoc->InsertRow(0, 0, 0, 0, 0, 2);
    2174           2 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
    2175           1 :                            broadcasterShifted(*m_pDoc, ScAddress(0,0,0), ScAddress(0,2,0)));
    2176             : 
    2177             :     // Move it back while column A is still empty.
    2178           1 :     m_pDoc->DeleteRow(0, 0, 0, 0, 0, 2);
    2179           2 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
    2180           1 :                            broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,0,0)));
    2181             : 
    2182             :     // Clear everything again
    2183           1 :     clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
    2184             : 
    2185             :     // B1:B3 depends on A1:A3
    2186           1 :     m_pDoc->SetString(ScAddress(1,0,0), "=A1");
    2187           1 :     m_pDoc->SetString(ScAddress(1,1,0), "=A2");
    2188           1 :     m_pDoc->SetString(ScAddress(1,2,0), "=A3");
    2189           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
    2190           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
    2191           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
    2192           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 0));
    2193           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check in B3 failed.",
    2194           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 0));
    2195           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
    2196           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A2.", m_pDoc->GetBroadcaster(ScAddress(0,1,0)));
    2197           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A3.", m_pDoc->GetBroadcaster(ScAddress(0,2,0)));
    2198             : 
    2199             :     // Insert Rows at row 2, down 5 rows.
    2200           1 :     m_pDoc->InsertRow(0, 0, 0, 0, 1, 5);
    2201           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster should exist in A1.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
    2202           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check in B1 failed.",
    2203           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(1,0,0), -1, 0));
    2204             : 
    2205             :     // Broadcasters in A2 and A3 should shift down by 5 rows.
    2206           2 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
    2207           1 :                            broadcasterShifted(*m_pDoc, ScAddress(0,1,0), ScAddress(0,6,0)));
    2208           2 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster relocation failed.",
    2209           1 :                            broadcasterShifted(*m_pDoc, ScAddress(0,2,0), ScAddress(0,7,0)));
    2210             : 
    2211             :     // B2 and B3 should reference shifted cells.
    2212           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
    2213           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(1,1,0), -1, 5));
    2214           2 :     CPPUNIT_ASSERT_MESSAGE("Relative reference check in B2 failed.",
    2215           1 :                            checkRelativeRefToken(*m_pDoc, ScAddress(1,2,0), -1, 5));
    2216             : 
    2217             :     // Delete cells with broadcasters.
    2218           1 :     m_pDoc->DeleteRow(0, 0, 0, 0, 4, 6);
    2219           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A7.", !m_pDoc->GetBroadcaster(ScAddress(0,6,0)));
    2220           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster should NOT exist in A8.", !m_pDoc->GetBroadcaster(ScAddress(0,7,0)));
    2221             : 
    2222             :     // References in B2 and B3 should be invalid.
    2223           2 :     CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B2 failed.",
    2224           1 :                            checkDeletedRefToken(*m_pDoc, ScAddress(1,1,0)));
    2225           2 :     CPPUNIT_ASSERT_MESSAGE("Deleted reference check in B3 failed.",
    2226           1 :                            checkDeletedRefToken(*m_pDoc, ScAddress(1,2,0)));
    2227             : 
    2228             :     // Clear everything again
    2229           1 :     clearRange(m_pDoc, ScRange(0,0,0,10,100,0));
    2230             : 
    2231             :     // Switch to R1C1 to make it easier to input relative references in multiple cells.
    2232           2 :     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
    2233             : 
    2234             :     // Have B1:B20 reference A1:A20.
    2235           1 :     val = 0.0;
    2236          21 :     for (SCROW i = 0; i < 20; ++i)
    2237             :     {
    2238          20 :         m_pDoc->SetValue(ScAddress(0,i,0), val++);
    2239          20 :         m_pDoc->SetString(ScAddress(1,i,0), "=RC[-1]");
    2240             :     }
    2241             : 
    2242             :     // Ensure that the formula cells show correct values, and the referenced
    2243             :     // cells have broadcasters.
    2244           1 :     val = 0.0;
    2245          21 :     for (SCROW i = 0; i < 20; ++i)
    2246             :     {
    2247          20 :         CPPUNIT_ASSERT_EQUAL(val++, m_pDoc->GetValue(ScAddress(1,i,0)));
    2248          20 :         pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
    2249          20 :         CPPUNIT_ASSERT_MESSAGE("Broadcast should exist here.", pBC);
    2250             :     }
    2251             : 
    2252             :     // Delete formula cells in B2:B19.
    2253           1 :     clearRange(m_pDoc, ScRange(1,1,0,1,18,0));
    2254             :     // Ensure that A2:A19 no longer have broadcasters, but A1 and A20 still do.
    2255           1 :     CPPUNIT_ASSERT_MESSAGE("A1 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,0,0)));
    2256           1 :     CPPUNIT_ASSERT_MESSAGE("A20 should still have broadcaster.", m_pDoc->GetBroadcaster(ScAddress(0,19,0)));
    2257          19 :     for (SCROW i = 1; i <= 18; ++i)
    2258             :     {
    2259          18 :         pBC = m_pDoc->GetBroadcaster(ScAddress(0,i,0));
    2260          18 :         CPPUNIT_ASSERT_MESSAGE("Broadcaster should have been deleted.", !pBC);
    2261             :     }
    2262             : 
    2263           2 :     m_pDoc->DeleteTab(0);
    2264           1 : }
    2265             : 
    2266           1 : void Test::testFuncParam()
    2267             : {
    2268           1 :     OUString aTabName("foo");
    2269           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    2270           1 :                             m_pDoc->InsertTab (0, aTabName));
    2271             : 
    2272             :     // First, the normal case, with no missing parameters.
    2273           1 :     m_pDoc->SetString(0, 0, 0, OUString("=AVERAGE(1;2;3)"));
    2274           1 :     m_pDoc->CalcFormulaTree(false, false);
    2275             :     double val;
    2276           1 :     m_pDoc->GetValue(0, 0, 0, val);
    2277           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 2);
    2278             : 
    2279             :     // Now function with missing parameters.  Missing values should be treated
    2280             :     // as zeros.
    2281           1 :     m_pDoc->SetString(0, 0, 0, OUString("=AVERAGE(1;;;)"));
    2282           1 :     m_pDoc->CalcFormulaTree(false, false);
    2283           1 :     m_pDoc->GetValue(0, 0, 0, val);
    2284           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 0.25);
    2285             : 
    2286             :     // Conversion of string to numeric argument.
    2287           1 :     m_pDoc->SetString(0, 0, 0, OUString("=\"\"+3"));    // empty string
    2288           1 :     m_pDoc->SetString(0, 1, 0, OUString("=\" \"+3"));   // only blank
    2289           1 :     m_pDoc->SetString(0, 2, 0, OUString("=\" 4 \"+3")); // number in blanks
    2290           1 :     m_pDoc->SetString(0, 3, 0, OUString("=\" x \"+3")); // non-numeric => #VALUE! error
    2291             : 
    2292           2 :     OUString aVal;
    2293           1 :     ScCalcConfig aConfig;
    2294             : 
    2295             :     // With "Empty string as zero" option.
    2296           1 :     aConfig.mbEmptyStringAsZero = true;
    2297           1 :     ScInterpreter::SetGlobalConfig(aConfig);
    2298           1 :     m_pDoc->CalcAll();
    2299           1 :     m_pDoc->GetValue(0, 0, 0, val);
    2300           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
    2301           1 :     m_pDoc->GetValue(0, 1, 0, val);
    2302           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 3);
    2303           1 :     m_pDoc->GetValue(0, 2, 0, val);
    2304           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
    2305           1 :     aVal = m_pDoc->GetString( 0, 3, 0);
    2306           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
    2307             : 
    2308             :     // Without "Empty string as zero" option.
    2309           1 :     aConfig.mbEmptyStringAsZero = false;
    2310           1 :     ScInterpreter::SetGlobalConfig(aConfig);
    2311           1 :     m_pDoc->CalcAll();
    2312           1 :     aVal = m_pDoc->GetString( 0, 0, 0);
    2313           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
    2314           1 :     aVal = m_pDoc->GetString( 0, 1, 0);
    2315           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
    2316           1 :     m_pDoc->GetValue(0, 2, 0, val);
    2317           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", val == 7);
    2318           1 :     aVal = m_pDoc->GetString( 0, 3, 0);
    2319           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect result", aVal == "#VALUE!");
    2320             : 
    2321           2 :     m_pDoc->DeleteTab(0);
    2322           1 : }
    2323             : 
    2324           1 : void Test::testNamedRange()
    2325             : {
    2326             :     struct {
    2327             :         const char* pName; const char* pExpr; sal_uInt16 nIndex;
    2328             :     } aNames[] = {
    2329             :         { "Divisor",  "$Sheet1.$A$1:$A$1048576", 1 },
    2330             :         { "MyRange1", "$Sheet1.$A$1:$A$100",     2 },
    2331             :         { "MyRange2", "$Sheet1.$B$1:$B$100",     3 },
    2332             :         { "MyRange3", "$Sheet1.$C$1:$C$100",     4 }
    2333           1 :     };
    2334             : 
    2335           1 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "Sheet1"));
    2336             : 
    2337           1 :     m_pDoc->SetValue (0, 0, 0, 101);
    2338             : 
    2339           1 :     ScAddress aA1(0, 0, 0);
    2340           1 :     ScRangeName* pNewRanges = new ScRangeName();
    2341           5 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
    2342             :     {
    2343             :         ScRangeData* pNew = new ScRangeData(
    2344             :             m_pDoc,
    2345             :             OUString::createFromAscii(aNames[i].pName),
    2346             :             OUString::createFromAscii(aNames[i].pExpr),
    2347           4 :             aA1, 0, formula::FormulaGrammar::GRAM_ENGLISH);
    2348           4 :         pNew->SetIndex(aNames[i].nIndex);
    2349           4 :         bool bSuccess = pNewRanges->insert(pNew);
    2350           4 :         CPPUNIT_ASSERT_MESSAGE ("insertion failed", bSuccess);
    2351             :     }
    2352             : 
    2353             :     // Make sure the index lookup does the right thing.
    2354           5 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
    2355             :     {
    2356           4 :         const ScRangeData* p = pNewRanges->findByIndex(aNames[i].nIndex);
    2357           4 :         CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed.", p);
    2358           4 :         OUString aName = p->GetName();
    2359           4 :         CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved.", aName.equalsAscii(aNames[i].pName));
    2360           4 :     }
    2361             : 
    2362             :     // Test usage in formula expression.
    2363           1 :     m_pDoc->SetRangeName(pNewRanges);
    2364           1 :     m_pDoc->SetString (1, 0, 0, OUString("=A1/Divisor"));
    2365           1 :     m_pDoc->CalcAll();
    2366             : 
    2367             :     double result;
    2368           1 :     m_pDoc->GetValue (1, 0, 0, result);
    2369           1 :     CPPUNIT_ASSERT_MESSAGE ("calculation failed", result == 1.0);
    2370             : 
    2371             :     // Test copy-ability of range names.
    2372           1 :     ScRangeName* pCopiedRanges = new ScRangeName(*pNewRanges);
    2373           1 :     m_pDoc->SetRangeName(pCopiedRanges);
    2374             :     // Make sure the index lookup still works.
    2375           5 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aNames); ++i)
    2376             :     {
    2377           4 :         const ScRangeData* p = pCopiedRanges->findByIndex(aNames[i].nIndex);
    2378           4 :         CPPUNIT_ASSERT_MESSAGE("lookup of range name by index failed with the copied instance.", p);
    2379           4 :         OUString aName = p->GetName();
    2380           4 :         CPPUNIT_ASSERT_MESSAGE("wrong range name is retrieved with the copied instance.", aName.equalsAscii(aNames[i].pName));
    2381           4 :     }
    2382             : 
    2383           1 :     m_pDoc->SetRangeName(NULL); // Delete the names.
    2384           1 :     m_pDoc->DeleteTab(0);
    2385           1 : }
    2386             : 
    2387           1 : void Test::testCSV()
    2388             : {
    2389           1 :     const int English = 0, European = 1;
    2390             :     struct {
    2391             :         const char *pStr; int eSep; bool bResult; double nValue;
    2392             :     } aTests[] = {
    2393             :         { "foo",       English,  false, 0.0 },
    2394             :         { "1.0",       English,  true,  1.0 },
    2395             :         { "1,0",       English,  false, 0.0 },
    2396             :         { "1.0",       European, false, 0.0 },
    2397             :         { "1.000",     European, true,  1000.0 },
    2398             :         { "1,000",     European, true,  1.0 },
    2399             :         { "1.000",     English,  true,  1.0 },
    2400             :         { "1,000",     English,  true,  1000.0 },
    2401             :         { " 1.0",      English,  true,  1.0 },
    2402             :         { " 1.0  ",    English,  true,  1.0 },
    2403             :         { "1.0 ",      European, false, 0.0 },
    2404             :         { "1.000",     European, true,  1000.0 },
    2405             :         { "1137.999",  English,  true,  1137.999 },
    2406             :         { "1.000.00",  European, false, 0.0 }
    2407           1 :     };
    2408          15 :     for (sal_uInt32 i = 0; i < SAL_N_ELEMENTS(aTests); i++) {
    2409          14 :         OUString aStr(aTests[i].pStr, strlen (aTests[i].pStr), RTL_TEXTENCODING_UTF8);
    2410          14 :         double nValue = 0.0;
    2411             :         bool bResult = ScStringUtil::parseSimpleNumber
    2412          14 :                 (aStr, aTests[i].eSep == English ? '.' : ',',
    2413          14 :                  aTests[i].eSep == English ? ',' : '.',
    2414          28 :                  nValue);
    2415          14 :         CPPUNIT_ASSERT_MESSAGE ("CSV numeric detection failure", bResult == aTests[i].bResult);
    2416          14 :         CPPUNIT_ASSERT_MESSAGE ("CSV numeric value failure", nValue == aTests[i].nValue);
    2417          14 :     }
    2418           1 : }
    2419             : 
    2420             : template<typename Evaluator>
    2421           4 : void checkMatrixElements(const ScMatrix& rMat)
    2422             : {
    2423             :     SCSIZE nC, nR;
    2424           4 :     rMat.GetDimensions(nC, nR);
    2425             :     Evaluator aEval;
    2426          32 :     for (SCSIZE i = 0; i < nC; ++i)
    2427             :     {
    2428         508 :         for (SCSIZE j = 0; j < nR; ++j)
    2429             :         {
    2430         480 :             aEval(i, j, rMat.Get(i, j));
    2431             :         }
    2432             :     }
    2433           4 : }
    2434             : 
    2435             : struct AllZeroMatrix
    2436             : {
    2437          40 :     void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
    2438             :     {
    2439          40 :         CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
    2440          40 :         CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
    2441          40 :     }
    2442             : };
    2443             : 
    2444             : struct PartiallyFilledZeroMatrix
    2445             : {
    2446          40 :     void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
    2447             :     {
    2448          40 :         CPPUNIT_ASSERT_MESSAGE("element is not of numeric type", rVal.nType == SC_MATVAL_VALUE);
    2449          40 :         if (1 <= nCol && nCol <= 2 && 2 <= nRow && nRow <= 8)
    2450             :         {
    2451          14 :             CPPUNIT_ASSERT_MESSAGE("element value must be 3.0", rVal.fVal == 3.0);
    2452             :         }
    2453             :         else
    2454             :         {
    2455          26 :             CPPUNIT_ASSERT_MESSAGE("element value must be zero", rVal.fVal == 0.0);
    2456             :         }
    2457          40 :     }
    2458             : };
    2459             : 
    2460             : struct AllEmptyMatrix
    2461             : {
    2462         200 :     void operator() (SCSIZE /*nCol*/, SCSIZE /*nRow*/, const ScMatrixValue& rVal) const
    2463             :     {
    2464         200 :         CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
    2465         200 :         CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
    2466         200 :     }
    2467             : };
    2468             : 
    2469             : struct PartiallyFilledEmptyMatrix
    2470             : {
    2471         200 :     void operator() (SCSIZE nCol, SCSIZE nRow, const ScMatrixValue& rVal) const
    2472             :     {
    2473         200 :         if (nCol == 1 && nRow == 1)
    2474             :         {
    2475           1 :             CPPUNIT_ASSERT_MESSAGE("element is not of boolean type", rVal.nType == SC_MATVAL_BOOLEAN);
    2476           1 :             CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == 1.0);
    2477             :         }
    2478         199 :         else if (nCol == 4 && nRow == 5)
    2479             :         {
    2480           1 :             CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_VALUE);
    2481           1 :             CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.fVal == -12.5);
    2482             :         }
    2483         198 :         else if (nCol == 8 && nRow == 2)
    2484             :         {
    2485           1 :             CPPUNIT_ASSERT_MESSAGE("element is not of value type", rVal.nType == SC_MATVAL_STRING);
    2486           1 :             CPPUNIT_ASSERT_MESSAGE("element value is not what is expected", rVal.aStr == "Test");
    2487             :         }
    2488         197 :         else if (nCol == 8 && nRow == 11)
    2489             :         {
    2490           1 :             CPPUNIT_ASSERT_MESSAGE("element is not of empty path type", rVal.nType == SC_MATVAL_EMPTYPATH);
    2491           1 :             CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
    2492             :         }
    2493             :         else
    2494             :         {
    2495         196 :             CPPUNIT_ASSERT_MESSAGE("element is not of empty type", rVal.nType == SC_MATVAL_EMPTY);
    2496         196 :             CPPUNIT_ASSERT_MESSAGE("value of \"empty\" element is expected to be zero", rVal.fVal == 0.0);
    2497             :         }
    2498         200 :     }
    2499             : };
    2500             : 
    2501           1 : void Test::testMatrix()
    2502             : {
    2503           1 :     ScMatrixRef pMat;
    2504             : 
    2505             :     // First, test the zero matrix type.
    2506           1 :     pMat = new ScMatrix(0, 0, 0.0);
    2507             :     SCSIZE nC, nR;
    2508           1 :     pMat->GetDimensions(nC, nR);
    2509           1 :     CPPUNIT_ASSERT_MESSAGE("matrix is not empty", nC == 0 && nR == 0);
    2510           1 :     pMat->Resize(4, 10, 0.0);
    2511           1 :     pMat->GetDimensions(nC, nR);
    2512           1 :     CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 4 && nR == 10);
    2513           2 :     CPPUNIT_ASSERT_MESSAGE("both 'and' and 'or' should evaluate to false",
    2514           1 :                            !pMat->And() && !pMat->Or());
    2515             : 
    2516             :     // Resizing into a larger matrix should fill the void space with zeros.
    2517           1 :     checkMatrixElements<AllZeroMatrix>(*pMat);
    2518             : 
    2519           1 :     pMat->FillDouble(3.0, 1, 2, 2, 8);
    2520           1 :     checkMatrixElements<PartiallyFilledZeroMatrix>(*pMat);
    2521           1 :     CPPUNIT_ASSERT_MESSAGE("matrix is expected to be numeric", pMat->IsNumeric());
    2522           2 :     CPPUNIT_ASSERT_MESSAGE("partially non-zero matrix should evaluate false on 'and' and true on 'or",
    2523           1 :                            !pMat->And() && pMat->Or());
    2524           1 :     pMat->FillDouble(5.0, 0, 0, nC-1, nR-1);
    2525           2 :     CPPUNIT_ASSERT_MESSAGE("fully non-zero matrix should evaluate true both on 'and' and 'or",
    2526           1 :                            pMat->And() && pMat->Or());
    2527             : 
    2528             :     // Test the AND and OR evaluations.
    2529           1 :     pMat = new ScMatrix(2, 2, 0.0);
    2530             : 
    2531             :     // Only some of the elements are non-zero.
    2532           1 :     pMat->PutBoolean(true, 0, 0);
    2533           1 :     pMat->PutDouble(1.0, 1, 1);
    2534           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
    2535           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect AND result", !pMat->And());
    2536             : 
    2537             :     // All of the elements are non-zero.
    2538           1 :     pMat->PutBoolean(true, 0, 1);
    2539           1 :     pMat->PutDouble(2.3, 1, 0);
    2540           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect OR result", pMat->Or());
    2541           1 :     CPPUNIT_ASSERT_MESSAGE("incorrect AND result", pMat->And());
    2542             : 
    2543             :     // Now test the emtpy matrix type.
    2544           1 :     pMat = new ScMatrix(10, 20);
    2545           1 :     pMat->GetDimensions(nC, nR);
    2546           1 :     CPPUNIT_ASSERT_MESSAGE("matrix size is not as expected", nC == 10 && nR == 20);
    2547           1 :     checkMatrixElements<AllEmptyMatrix>(*pMat);
    2548             : 
    2549           1 :     pMat->PutBoolean(true, 1, 1);
    2550           1 :     pMat->PutDouble(-12.5, 4, 5);
    2551           2 :     OUString aStr("Test");
    2552           1 :     pMat->PutString(aStr, 8, 2);
    2553           1 :     pMat->PutEmptyPath(8, 11);
    2554           1 :     checkMatrixElements<PartiallyFilledEmptyMatrix>(*pMat);
    2555             : 
    2556             :     // Test resizing.
    2557           1 :     pMat = new ScMatrix(0, 0);
    2558           1 :     pMat->Resize(2, 2, 1.5);
    2559           1 :     pMat->PutEmpty(1, 1);
    2560             : 
    2561           1 :     CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 0));
    2562           1 :     CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(0, 1));
    2563           1 :     CPPUNIT_ASSERT_EQUAL(1.5, pMat->GetDouble(1, 0));
    2564           1 :     CPPUNIT_ASSERT_MESSAGE("PutEmpty() call failed.", pMat->IsEmpty(1, 1));
    2565             : 
    2566             :     // Max and min values.
    2567           1 :     pMat = new ScMatrix(2, 2, 0.0);
    2568           1 :     pMat->PutDouble(-10, 0, 0);
    2569           1 :     pMat->PutDouble(-12, 0, 1);
    2570           1 :     pMat->PutDouble(-8, 1, 0);
    2571           1 :     pMat->PutDouble(-25, 1, 1);
    2572           1 :     CPPUNIT_ASSERT_EQUAL(-25.0, pMat->GetMinValue(false));
    2573           1 :     CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false));
    2574           1 :     pMat->PutString("Test", 0, 0);
    2575           1 :     CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMaxValue(true)); // text as zero.
    2576           1 :     CPPUNIT_ASSERT_EQUAL(-8.0, pMat->GetMaxValue(false)); // ignore text.
    2577           1 :     pMat->PutBoolean(true, 0, 0);
    2578           1 :     CPPUNIT_ASSERT_EQUAL(1.0, pMat->GetMaxValue(false));
    2579           1 :     pMat = new ScMatrix(2, 2, 10.0);
    2580           1 :     pMat->PutBoolean(false, 0, 0);
    2581           1 :     pMat->PutDouble(12.5, 1, 1);
    2582           1 :     CPPUNIT_ASSERT_EQUAL(0.0, pMat->GetMinValue(false));
    2583           2 :     CPPUNIT_ASSERT_EQUAL(12.5, pMat->GetMaxValue(false));
    2584           1 : }
    2585             : 
    2586           1 : void Test::testEnterMixedMatrix()
    2587             : {
    2588           1 :     m_pDoc->InsertTab(0, "foo");
    2589             : 
    2590             :     // Insert the source values in A1:B2.
    2591           1 :     m_pDoc->SetString(0, 0, 0, "A");
    2592           1 :     m_pDoc->SetString(1, 0, 0, "B");
    2593           1 :     double val = 1.0;
    2594           1 :     m_pDoc->SetValue(0, 1, 0, val);
    2595           1 :     val = 2.0;
    2596           1 :     m_pDoc->SetValue(1, 1, 0, val);
    2597             : 
    2598             :     // Create a matrix range in A4:B5 referencing A1:B2.
    2599           1 :     ScMarkData aMark;
    2600           1 :     aMark.SelectOneTable(0);
    2601           1 :     m_pDoc->InsertMatrixFormula(0, 3, 1, 4, aMark, "=A1:B2", NULL);
    2602             : 
    2603           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(0,0,0), m_pDoc->GetString(0,3,0));
    2604           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(1,0,0), m_pDoc->GetString(1,3,0));
    2605           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(0,1,0), m_pDoc->GetValue(0,4,0));
    2606           1 :     CPPUNIT_ASSERT_EQUAL(m_pDoc->GetValue(1,1,0), m_pDoc->GetValue(1,4,0));
    2607             : 
    2608           1 :     m_pDoc->DeleteTab(0);
    2609           1 : }
    2610             : 
    2611             : namespace {
    2612             : 
    2613             : struct DPFieldDef
    2614             : {
    2615             :     const char* pName;
    2616             :     sheet::DataPilotFieldOrientation eOrient;
    2617             : 
    2618             :     /**
    2619             :      * Function for data field.  It's used only for data field.  When 0, the
    2620             :      * default function (SUM) is used.
    2621             :      */
    2622             :     int eFunc;
    2623             : };
    2624             : 
    2625             : template<size_t _Size>
    2626           5 : ScRange insertDPSourceData(ScDocument* pDoc, DPFieldDef aFields[], size_t nFieldCount, const char* aData[][_Size], size_t nDataCount)
    2627             : {
    2628             :     // Insert field names in row 0.
    2629          22 :     for (size_t i = 0; i < nFieldCount; ++i)
    2630          17 :         pDoc->SetString(static_cast<SCCOL>(i), 0, 0, OUString(aFields[i].pName, strlen(aFields[i].pName), RTL_TEXTENCODING_UTF8));
    2631             : 
    2632             :     // Insert data into row 1 and downward.
    2633          31 :     for (size_t i = 0; i < nDataCount; ++i)
    2634             :     {
    2635          26 :         SCROW nRow = static_cast<SCROW>(i) + 1;
    2636         120 :         for (size_t j = 0; j < nFieldCount; ++j)
    2637             :         {
    2638          94 :             SCCOL nCol = static_cast<SCCOL>(j);
    2639          94 :             pDoc->SetString(
    2640         188 :                 nCol, nRow, 0, OUString(aData[i][j], strlen(aData[i][j]), RTL_TEXTENCODING_UTF8));
    2641             :         }
    2642             :     }
    2643             : 
    2644           5 :     SCROW nRow1 = 0, nRow2 = 0;
    2645           5 :     SCCOL nCol1 = 0, nCol2 = 0;
    2646           5 :     pDoc->GetDataArea(0, nCol1, nRow1, nCol2, nRow2, true, false);
    2647           5 :     CPPUNIT_ASSERT_MESSAGE("Data is expected to start from (col=0,row=0).", nCol1 == 0 && nRow1 == 0);
    2648           5 :     CPPUNIT_ASSERT_MESSAGE("Unexpected data range.",
    2649             :                            nCol2 == static_cast<SCCOL>(nFieldCount - 1) && nRow2 == static_cast<SCROW>(nDataCount));
    2650             : 
    2651           5 :     ScRange aSrcRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
    2652           5 :     printRange(pDoc, aSrcRange, "Data sheet content");
    2653           5 :     return aSrcRange;
    2654             : }
    2655             : 
    2656             : template<size_t _Size>
    2657          31 : bool checkDPTableOutput(ScDocument* pDoc, const ScRange& aOutRange, const char* aOutputCheck[][_Size], const char* pCaption)
    2658             : {
    2659          31 :     bool bResult = true;
    2660          31 :     const ScAddress& s = aOutRange.aStart;
    2661          31 :     const ScAddress& e = aOutRange.aEnd;
    2662          31 :     SheetPrinter printer(e.Row() - s.Row() + 1, e.Col() - s.Col() + 1);
    2663          31 :     SCROW nOutRowSize = e.Row() - s.Row() + 1;
    2664          31 :     SCCOL nOutColSize = e.Col() - s.Col() + 1;
    2665         242 :     for (SCROW nRow = 0; nRow < nOutRowSize; ++nRow)
    2666             :     {
    2667         829 :         for (SCCOL nCol = 0; nCol < nOutColSize; ++nCol)
    2668             :         {
    2669         618 :             OUString aVal = pDoc->GetString(nCol + s.Col(), nRow + s.Row(), s.Tab());
    2670         618 :             printer.set(nRow, nCol, aVal);
    2671         618 :             const char* p = aOutputCheck[nRow][nCol];
    2672         618 :             if (p)
    2673             :             {
    2674         474 :                 OUString aCheckVal = OUString::createFromAscii(p);
    2675         474 :                 bool bEqual = aCheckVal.equals(aVal);
    2676         474 :                 if (!bEqual)
    2677             :                 {
    2678           0 :                     cout << "Expected: " << aCheckVal << "  Actual: " << aVal << endl;
    2679           0 :                     bResult = false;
    2680         474 :                 }
    2681             :             }
    2682         144 :             else if (!aVal.isEmpty())
    2683             :             {
    2684           0 :                 cout << "Empty cell expected" << endl;
    2685           0 :                 bResult = false;
    2686             :             }
    2687             :         }
    2688             :     }
    2689          31 :     printer.print(pCaption);
    2690          31 :     return bResult;
    2691             : }
    2692             : 
    2693          16 : ScDPObject* createDPFromSourceDesc(
    2694             :     ScDocument* pDoc, const ScSheetSourceDesc& rDesc, DPFieldDef aFields[], size_t nFieldCount,
    2695             :     bool bFilterButton)
    2696             : {
    2697          16 :     ScDPObject* pDPObj = new ScDPObject(pDoc);
    2698          16 :     pDPObj->SetSheetDesc(rDesc);
    2699          16 :     pDPObj->SetOutRange(ScAddress(0, 0, 1));
    2700             : 
    2701          16 :     ScDPSaveData aSaveData;
    2702             :     // Set data pilot table output options.
    2703          16 :     aSaveData.SetIgnoreEmptyRows(false);
    2704          16 :     aSaveData.SetRepeatIfEmpty(false);
    2705          16 :     aSaveData.SetColumnGrand(true);
    2706          16 :     aSaveData.SetRowGrand(true);
    2707          16 :     aSaveData.SetFilterButton(bFilterButton);
    2708          16 :     aSaveData.SetDrillDown(true);
    2709             : 
    2710             :     // Check the sanity of the source range.
    2711          16 :     const ScRange& rSrcRange = rDesc.GetSourceRange();
    2712          16 :     SCROW nRow1 = rSrcRange.aStart.Row();
    2713          16 :     SCROW nRow2 = rSrcRange.aEnd.Row();
    2714          16 :     CPPUNIT_ASSERT_MESSAGE("source range contains no data!", nRow2 - nRow1 > 1);
    2715             : 
    2716             :     // Set the dimension information.
    2717          57 :     for (size_t i = 0; i < nFieldCount; ++i)
    2718             :     {
    2719          41 :         OUString aDimName = OUString::createFromAscii(aFields[i].pName);
    2720          41 :         ScDPSaveDimension* pDim = aSaveData.GetNewDimensionByName(aDimName);
    2721          41 :         pDim->SetOrientation(static_cast<sal_uInt16>(aFields[i].eOrient));
    2722          41 :         pDim->SetUsedHierarchy(0);
    2723             : 
    2724          41 :         if (aFields[i].eOrient == sheet::DataPilotFieldOrientation_DATA)
    2725             :         {
    2726          18 :             sheet::GeneralFunction eFunc = sheet::GeneralFunction_SUM;
    2727          18 :             if (aFields[i].eFunc)
    2728          11 :                 eFunc = static_cast<sheet::GeneralFunction>(aFields[i].eFunc);
    2729             : 
    2730          18 :             pDim->SetFunction(eFunc);
    2731          18 :             pDim->SetReferenceValue(NULL);
    2732             :         }
    2733             :         else
    2734             :         {
    2735          23 :             sheet::DataPilotFieldSortInfo aSortInfo;
    2736          23 :             aSortInfo.IsAscending = true;
    2737          23 :             aSortInfo.Mode = 2;
    2738          23 :             pDim->SetSortInfo(&aSortInfo);
    2739             : 
    2740          23 :             sheet::DataPilotFieldLayoutInfo aLayInfo;
    2741          23 :             aLayInfo.LayoutMode = 0;
    2742          23 :             aLayInfo.AddEmptyLines = false;
    2743          23 :             pDim->SetLayoutInfo(&aLayInfo);
    2744          46 :             sheet::DataPilotFieldAutoShowInfo aShowInfo;
    2745          23 :             aShowInfo.IsEnabled = false;
    2746          23 :             aShowInfo.ShowItemsMode = 0;
    2747          23 :             aShowInfo.ItemCount = 0;
    2748          46 :             pDim->SetAutoShowInfo(&aShowInfo);
    2749             :         }
    2750          41 :     }
    2751             : 
    2752             :     // Don't forget the data layout dimension.
    2753          16 :     ScDPSaveDimension* pDim = aSaveData.GetDataLayoutDimension();
    2754          16 :     pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
    2755          16 :     pDim->SetShowEmpty(true);
    2756             : 
    2757          16 :     pDPObj->SetSaveData(aSaveData);
    2758          16 :     pDPObj->InvalidateData();
    2759             : 
    2760          16 :     return pDPObj;
    2761             : }
    2762             : 
    2763          15 : ScDPObject* createDPFromRange(
    2764             :     ScDocument* pDoc, const ScRange& rRange, DPFieldDef aFields[], size_t nFieldCount,
    2765             :     bool bFilterButton)
    2766             : {
    2767          15 :     ScSheetSourceDesc aSheetDesc(pDoc);
    2768          15 :     aSheetDesc.SetSourceRange(rRange);
    2769          15 :     return createDPFromSourceDesc(pDoc, aSheetDesc, aFields, nFieldCount, bFilterButton);
    2770             : }
    2771             : 
    2772          28 : ScRange refresh(ScDPObject* pDPObj)
    2773             : {
    2774          28 :     bool bOverFlow = false;
    2775          28 :     ScRange aOutRange = pDPObj->GetNewOutputRange(bOverFlow);
    2776          28 :     CPPUNIT_ASSERT_MESSAGE("Table overflow!?", !bOverFlow);
    2777             : 
    2778          28 :     pDPObj->Output(aOutRange.aStart);
    2779          28 :     aOutRange = pDPObj->GetOutRange();
    2780          28 :     return aOutRange;
    2781             : }
    2782             : 
    2783           4 : ScRange refreshGroups(ScDPCollection* pDPs, ScDPObject* pDPObj)
    2784             : {
    2785             :     // We need to first create group data in the cache, then the group data in
    2786             :     // the object.
    2787           4 :     std::set<ScDPObject*> aRefs;
    2788           4 :     bool bSuccess = pDPs->ReloadGroupsInCache(pDPObj, aRefs);
    2789           4 :     CPPUNIT_ASSERT_MESSAGE("Failed to reload group data in cache.", bSuccess);
    2790           4 :     CPPUNIT_ASSERT_MESSAGE("There should be only one table linked to this cache.", aRefs.size() == 1);
    2791           4 :     pDPObj->ReloadGroupTableData();
    2792             : 
    2793           4 :     return refresh(pDPObj);
    2794             : }
    2795             : 
    2796             : }
    2797             : 
    2798           1 : void Test::testPivotTable()
    2799             : {
    2800           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    2801           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    2802             : 
    2803             :     // Dimension definition
    2804             :     DPFieldDef aFields[] = {
    2805             :         { "Name",  sheet::DataPilotFieldOrientation_ROW, 0 },
    2806             :         { "Group", sheet::DataPilotFieldOrientation_COLUMN, 0 },
    2807             :         { "Score", sheet::DataPilotFieldOrientation_DATA, 0 }
    2808           1 :     };
    2809             : 
    2810             :     // Raw data
    2811             :     const char* aData[][3] = {
    2812             :         { "Andy",    "A", "30" },
    2813             :         { "Bruce",   "A", "20" },
    2814             :         { "Charlie", "B", "45" },
    2815             :         { "David",   "B", "12" },
    2816             :         { "Edward",  "C",  "8" },
    2817             :         { "Frank",   "C", "15" },
    2818           1 :     };
    2819             : 
    2820           1 :     size_t nFieldCount = SAL_N_ELEMENTS(aFields);
    2821           1 :     size_t nDataCount = SAL_N_ELEMENTS(aData);
    2822             : 
    2823           1 :     ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
    2824           1 :     SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
    2825           1 :     SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
    2826             : 
    2827             :     ScDPObject* pDPObj = createDPFromRange(
    2828           1 :         m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
    2829             : 
    2830           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    2831           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    2832           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
    2833           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    2834           1 :                            pDPs->GetCount() == 1);
    2835           1 :     pDPObj->SetName(pDPs->CreateNewName());
    2836             : 
    2837           1 :     bool bOverFlow = false;
    2838           1 :     ScRange aOutRange = pDPObj->GetNewOutputRange(bOverFlow);
    2839           1 :     CPPUNIT_ASSERT_MESSAGE("Table overflow!?", !bOverFlow);
    2840             : 
    2841           1 :     pDPObj->Output(aOutRange.aStart);
    2842           1 :     aOutRange = pDPObj->GetOutRange();
    2843             :     {
    2844             :         // Expected output table content.  0 = empty cell
    2845             :         const char* aOutputCheck[][5] = {
    2846             :             { "Sum - Score", "Group", 0, 0, 0 },
    2847             :             { "Name", "A", "B", "C", "Total Result" },
    2848             :             { "Andy", "30", 0, 0, "30" },
    2849             :             { "Bruce", "20", 0, 0, "20" },
    2850             :             { "Charlie", 0, "45", 0, "45" },
    2851             :             { "David", 0, "12", 0, "12" },
    2852             :             { "Edward", 0, 0, "8", "8" },
    2853             :             { "Frank", 0, 0, "15", "15" },
    2854             :             { "Total Result", "50", "57", "23", "130" }
    2855           1 :         };
    2856             : 
    2857           1 :         bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
    2858           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    2859             :     }
    2860           1 :     CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
    2861             : 
    2862             :     // Update the cell values.
    2863           1 :     double aData2[] = { 100, 200, 300, 400, 500, 600 };
    2864           7 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aData2); ++i)
    2865             :     {
    2866           6 :         SCROW nRow = i + 1;
    2867           6 :         m_pDoc->SetValue(2, nRow, 0, aData2[i]);
    2868             :     }
    2869             : 
    2870           1 :     printRange(m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), "Data sheet content (modified)");
    2871             : 
    2872             :     // Now, create a copy of the datapilot object for the updated table, but
    2873             :     // don't reload the cache which should force the copy to use the old data
    2874             :     // from the cache.
    2875           1 :     ScDPObject* pDPObj2 = new ScDPObject(*pDPObj);
    2876           1 :     pDPs->InsertNewTable(pDPObj2);
    2877             : 
    2878           1 :     aOutRange = pDPObj2->GetOutRange();
    2879           1 :     pDPObj2->ClearTableData();
    2880           1 :     pDPObj2->Output(aOutRange.aStart);
    2881             :     {
    2882             :         // Expected output table content.  0 = empty cell
    2883             :         const char* aOutputCheck[][5] = {
    2884             :             { "Sum - Score", "Group", 0, 0, 0 },
    2885             :             { "Name", "A", "B", "C", "Total Result" },
    2886             :             { "Andy", "30", 0, 0, "30" },
    2887             :             { "Bruce", "20", 0, 0, "20" },
    2888             :             { "Charlie", 0, "45", 0, "45" },
    2889             :             { "David", 0, "12", 0, "12" },
    2890             :             { "Edward", 0, 0, "8", "8" },
    2891             :             { "Frank", 0, 0, "15", "15" },
    2892             :             { "Total Result", "50", "57", "23", "130" }
    2893           1 :         };
    2894             : 
    2895           1 :         bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (from old cache)");
    2896           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    2897             :     }
    2898             : 
    2899           1 :     CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
    2900             : 
    2901             :     // Free the first datapilot object after the 2nd one gets reloaded, to
    2902             :     // prevent the data cache from being deleted before the reload.
    2903           1 :     pDPs->FreeTable(pDPObj);
    2904             : 
    2905           1 :     CPPUNIT_ASSERT_MESSAGE("There should be only one data cache.", pDPs->GetSheetCaches().size() == 1);
    2906             : 
    2907             :     // This time clear the cache to refresh the data from the source range.
    2908           1 :     CPPUNIT_ASSERT_MESSAGE("This datapilot should be based on sheet data.", pDPObj2->IsSheetData());
    2909           1 :     std::set<ScDPObject*> aRefs;
    2910           1 :     sal_uLong nErrId = pDPs->ReloadCache(pDPObj2, aRefs);
    2911           1 :     CPPUNIT_ASSERT_MESSAGE("Cache reload failed.", nErrId == 0);
    2912           2 :     CPPUNIT_ASSERT_MESSAGE("Reloading a cache shouldn't remove any cache.",
    2913           1 :                            pDPs->GetSheetCaches().size() == 1);
    2914             : 
    2915           1 :     pDPObj2->ClearTableData();
    2916           1 :     pDPObj2->Output(aOutRange.aStart);
    2917             : 
    2918             :     {
    2919             :         // Expected output table content.  0 = empty cell
    2920             :         const char* aOutputCheck[][5] = {
    2921             :             { "Sum - Score", "Group", 0, 0, 0 },
    2922             :             { "Name", "A", "B", "C", "Total Result" },
    2923             :             { "Andy", "100", 0, 0, "100" },
    2924             :             { "Bruce", "200", 0, 0, "200" },
    2925             :             { "Charlie", 0, "300", 0, "300" },
    2926             :             { "David", 0, "400", 0, "400" },
    2927             :             { "Edward", 0, 0, "500", "500" },
    2928             :             { "Frank", 0, 0, "600", "600" },
    2929             :             { "Total Result", "300", "700", "1100", "2100" }
    2930           1 :         };
    2931             : 
    2932           1 :         bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (refreshed)");
    2933           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    2934             :     }
    2935             : 
    2936           1 :     CPPUNIT_ASSERT_MESSAGE("Cache should be here.", pDPs->GetSheetCaches().hasCache(aSrcRange));
    2937             : 
    2938             :     // Swap the two sheets.
    2939           1 :     m_pDoc->MoveTab(1, 0);
    2940           2 :     CPPUNIT_ASSERT_MESSAGE("Swapping the sheets shouldn't remove the cache.",
    2941           1 :                            pDPs->GetSheetCaches().size() == 1);
    2942           1 :     CPPUNIT_ASSERT_MESSAGE("Cache should have moved.", !pDPs->GetSheetCaches().hasCache(aSrcRange));
    2943           1 :     aSrcRange.aStart.SetTab(1);
    2944           1 :     aSrcRange.aEnd.SetTab(1);
    2945           1 :     CPPUNIT_ASSERT_MESSAGE("Cache should be here.", pDPs->GetSheetCaches().hasCache(aSrcRange));
    2946             : 
    2947           1 :     pDPs->FreeTable(pDPObj2);
    2948           2 :     CPPUNIT_ASSERT_MESSAGE("There shouldn't be any data pilot table stored with the document.",
    2949           1 :                            pDPs->GetCount() == 0);
    2950             : 
    2951           2 :     CPPUNIT_ASSERT_MESSAGE("There shouldn't be any more data cache.",
    2952           1 :                            pDPs->GetSheetCaches().size() == 0);
    2953             : 
    2954             :     // Insert a brand new pivot table object once again, but this time, don't
    2955             :     // create the output to avoid creating a data cache.
    2956           1 :     m_pDoc->DeleteTab(1);
    2957           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    2958             : 
    2959             :     pDPObj = createDPFromRange(
    2960           1 :         m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
    2961           1 :     bSuccess = pDPs->InsertNewTable(pDPObj);
    2962           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
    2963           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    2964           1 :                            pDPs->GetCount() == 1);
    2965           1 :     pDPObj->SetName(pDPs->CreateNewName());
    2966           2 :     CPPUNIT_ASSERT_MESSAGE("Data cache shouldn't exist yet before creating the table output.",
    2967           1 :                            pDPs->GetSheetCaches().size() == 0);
    2968             : 
    2969             :     // Now, "refresh" the table.  This should still return a reference to self
    2970             :     // even with the absence of data cache.
    2971           1 :     aRefs.clear();
    2972           1 :     pDPs->ReloadCache(pDPObj, aRefs);
    2973           2 :     CPPUNIT_ASSERT_MESSAGE("It should return the same object as a reference.",
    2974           1 :                            aRefs.size() == 1 && *aRefs.begin() == pDPObj);
    2975             : 
    2976           1 :     pDPs->FreeTable(pDPObj);
    2977             : 
    2978           1 :     m_pDoc->DeleteTab(1);
    2979           1 :     m_pDoc->DeleteTab(0);
    2980           1 : }
    2981             : 
    2982           1 : void Test::testPivotTableLabels()
    2983             : {
    2984           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    2985           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    2986             : 
    2987             :     // Dimension definition
    2988             :     DPFieldDef aFields[] = {
    2989             :         { "Software", sheet::DataPilotFieldOrientation_ROW, 0 },
    2990             :         { "Version",  sheet::DataPilotFieldOrientation_COLUMN, 0 },
    2991             :         { "1.2.3",    sheet::DataPilotFieldOrientation_DATA, 0 }
    2992           1 :     };
    2993             : 
    2994             :     // Raw data
    2995             :     const char* aData[][3] = {
    2996             :         { "LibreOffice", "3.3.0", "30" },
    2997             :         { "LibreOffice", "3.3.1", "20" },
    2998             :         { "LibreOffice", "3.4.0", "45" },
    2999           1 :     };
    3000             : 
    3001           1 :     size_t nFieldCount = SAL_N_ELEMENTS(aFields);
    3002           1 :     size_t nDataCount = SAL_N_ELEMENTS(aData);
    3003             : 
    3004           1 :     ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
    3005           1 :     SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
    3006           1 :     SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
    3007             : 
    3008             :     ScDPObject* pDPObj = createDPFromRange(
    3009           1 :         m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
    3010             : 
    3011           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    3012           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    3013           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
    3014           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    3015           1 :                            pDPs->GetCount() == 1);
    3016           1 :     pDPObj->SetName(pDPs->CreateNewName());
    3017             : 
    3018           1 :     ScRange aOutRange = refresh(pDPObj);
    3019             :     {
    3020             :         // Expected output table content.  0 = empty cell
    3021             :         const char* aOutputCheck[][5] = {
    3022             :             { "Sum - 1.2.3", "Version", 0, 0, 0 },
    3023             :             { "Software", "3.3.0", "3.3.1", "3.4.0", "Total Result" },
    3024             :             { "LibreOffice", "30", "20", "45", "95" },
    3025             :             { "Total Result", "30", "20", "45", "95" }
    3026           1 :         };
    3027             : 
    3028           1 :         bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
    3029           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3030             :     }
    3031             : 
    3032           1 :     pDPs->FreeTable(pDPObj);
    3033             : 
    3034           1 :     m_pDoc->DeleteTab(1);
    3035           1 :     m_pDoc->DeleteTab(0);
    3036           1 : }
    3037             : 
    3038           1 : void Test::testPivotTableDateLabels()
    3039             : {
    3040           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    3041           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    3042             : 
    3043             :     // Dimension definition
    3044             :     DPFieldDef aFields[] = {
    3045             :         { "Name",  sheet::DataPilotFieldOrientation_ROW, 0 },
    3046             :         { "Date",  sheet::DataPilotFieldOrientation_COLUMN, 0 },
    3047             :         { "Value", sheet::DataPilotFieldOrientation_DATA, 0 }
    3048           1 :     };
    3049             : 
    3050             :     // Raw data
    3051             :     const char* aData[][3] = {
    3052             :         { "Zena",   "2011-1-1", "30" },
    3053             :         { "Yodel",  "2011-1-2", "20" },
    3054             :         { "Xavior", "2011-1-3", "45" }
    3055           1 :     };
    3056             : 
    3057           1 :     size_t nFieldCount = SAL_N_ELEMENTS(aFields);
    3058           1 :     size_t nDataCount = SAL_N_ELEMENTS(aData);
    3059             : 
    3060           1 :     ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
    3061           1 :     SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
    3062           1 :     SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
    3063             : 
    3064             :     ScDPObject* pDPObj = createDPFromRange(
    3065           1 :         m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, false);
    3066             : 
    3067           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    3068           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    3069           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
    3070           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    3071           1 :                            pDPs->GetCount() == 1);
    3072           1 :     pDPObj->SetName(pDPs->CreateNewName());
    3073             : 
    3074           1 :     ScRange aOutRange = refresh(pDPObj);
    3075             :     {
    3076             :         // Expected output table content.  0 = empty cell
    3077             :         const char* aOutputCheck[][5] = {
    3078             :             { "Sum - Value", "Date", 0, 0, 0 },
    3079             :             { "Name", "2011-01-01", "2011-01-02", "2011-01-03", "Total Result" },
    3080             :             { "Xavior",  0, 0, "45", "45" },
    3081             :             { "Yodel",  0, "20", 0, "20" },
    3082             :             { "Zena",  "30", 0, 0, "30" },
    3083             :             { "Total Result", "30", "20", "45", "95" }
    3084           1 :         };
    3085             : 
    3086           1 :         bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
    3087           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3088             :     }
    3089             : 
    3090             :     {
    3091             :         const char* aChecks[] = {
    3092             :             "2011-01-01", "2011-01-02", "2011-01-03"
    3093           1 :         };
    3094             : 
    3095             :         // Make sure those cells that contain dates are numeric.
    3096           1 :         SCROW nRow = aOutRange.aStart.Row() + 1;
    3097           1 :         nCol1 = aOutRange.aStart.Col() + 1;
    3098           1 :         nCol2 = nCol1 + 2;
    3099           4 :         for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
    3100             :         {
    3101           3 :             OUString aVal = m_pDoc->GetString(nCol, nRow, 1);
    3102           3 :             CPPUNIT_ASSERT_MESSAGE("Cell value is not as expected.", aVal.equalsAscii(aChecks[nCol-nCol1]));
    3103           6 :             CPPUNIT_ASSERT_MESSAGE("This cell contains a date value and is supposed to be numeric.",
    3104           3 :                                    m_pDoc->HasValueData(nCol, nRow, 1));
    3105           3 :         }
    3106             :     }
    3107             : 
    3108           1 :     pDPs->FreeTable(pDPObj);
    3109             : 
    3110           1 :     m_pDoc->DeleteTab(1);
    3111           1 :     m_pDoc->DeleteTab(0);
    3112           1 : }
    3113             : 
    3114           1 : void Test::testPivotTableFilters()
    3115             : {
    3116           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    3117           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    3118             : 
    3119             :     // Dimension definition
    3120             :     DPFieldDef aFields[] = {
    3121             :         { "Name",   sheet::DataPilotFieldOrientation_HIDDEN, 0 },
    3122             :         { "Group1", sheet::DataPilotFieldOrientation_HIDDEN, 0 },
    3123             :         { "Group2", sheet::DataPilotFieldOrientation_PAGE, 0 },
    3124             :         { "Val1",   sheet::DataPilotFieldOrientation_DATA, 0 },
    3125             :         { "Val2",   sheet::DataPilotFieldOrientation_DATA, 0 }
    3126           1 :     };
    3127             : 
    3128             :     // Raw data
    3129             :     const char* aData[][5] = {
    3130             :         { "A", "1", "A", "1", "10" },
    3131             :         { "B", "1", "A", "1", "10" },
    3132             :         { "C", "1", "B", "1", "10" },
    3133             :         { "D", "1", "B", "1", "10" },
    3134             :         { "E", "2", "A", "1", "10" },
    3135             :         { "F", "2", "A", "1", "10" },
    3136             :         { "G", "2", "B", "1", "10" },
    3137             :         { "H", "2", "B", "1", "10" }
    3138           1 :     };
    3139             : 
    3140           1 :     size_t nFieldCount = SAL_N_ELEMENTS(aFields);
    3141           1 :     size_t nDataCount = SAL_N_ELEMENTS(aData);
    3142             : 
    3143           1 :     ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
    3144           1 :     SCROW nRow1 = aSrcRange.aStart.Row(), nRow2 = aSrcRange.aEnd.Row();
    3145           1 :     SCCOL nCol1 = aSrcRange.aStart.Col(), nCol2 = aSrcRange.aEnd.Col();
    3146             : 
    3147             :     ScDPObject* pDPObj = createDPFromRange(
    3148           1 :         m_pDoc, ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0), aFields, nFieldCount, true);
    3149             : 
    3150           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    3151           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    3152           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new datapilot object into document", bSuccess);
    3153           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    3154           1 :                            pDPs->GetCount() == 1);
    3155           1 :     pDPObj->SetName(pDPs->CreateNewName());
    3156             : 
    3157           1 :     ScRange aOutRange = refresh(pDPObj);
    3158             :     {
    3159             :         // Expected output table content.  0 = empty cell
    3160             :         const char* aOutputCheck[][2] = {
    3161             :             { "Filter", 0 },
    3162             :             { "Group2", "- all -" },
    3163             :             { 0, 0 },
    3164             :             { "Data", 0 },
    3165             :             { "Sum - Val1", "8" },
    3166             :             { "Sum - Val2", "80" }
    3167           1 :         };
    3168             : 
    3169           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (unfiltered)");
    3170           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3171             :     }
    3172             : 
    3173           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    3174             : 
    3175           1 :     ScAddress aFormulaAddr = aOutRange.aEnd;
    3176           1 :     aFormulaAddr.IncRow(2);
    3177           2 :     m_pDoc->SetString(aFormulaAddr.Col(), aFormulaAddr.Row(), aFormulaAddr.Tab(),
    3178           3 :                       OUString("=B6"));
    3179           1 :     double fTest = m_pDoc->GetValue(aFormulaAddr);
    3180           1 :     CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 80.0);
    3181             : 
    3182             :     // Set current page of 'Group2' to 'A'.
    3183           1 :     pDPObj->BuildAllDimensionMembers();
    3184           2 :     ScDPSaveData aSaveData(*pDPObj->GetSaveData());
    3185             :     ScDPSaveDimension* pPageDim = aSaveData.GetDimensionByName(
    3186           1 :         OUString("Group2"));
    3187           1 :     CPPUNIT_ASSERT_MESSAGE("Dimension not found", pPageDim);
    3188           2 :     OUString aPage("A");
    3189           1 :     pPageDim->SetCurrentPage(&aPage);
    3190           1 :     pDPObj->SetSaveData(aSaveData);
    3191           1 :     aOutRange = refresh(pDPObj);
    3192             :     {
    3193             :         // Expected output table content.  0 = empty cell
    3194             :         const char* aOutputCheck[][2] = {
    3195             :             { "Filter", 0 },
    3196             :             { "Group2", "A" },
    3197             :             { 0, 0 },
    3198             :             { "Data", 0 },
    3199             :             { "Sum - Val1", "4" },
    3200             :             { "Sum - Val2", "40" }
    3201           1 :         };
    3202             : 
    3203           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by page)");
    3204           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3205             :     }
    3206             : 
    3207           1 :     fTest = m_pDoc->GetValue(aFormulaAddr);
    3208           1 :     CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 40.0);
    3209             : 
    3210             :     // Set query filter.
    3211           2 :     ScSheetSourceDesc aDesc(*pDPObj->GetSheetDesc());
    3212           2 :     ScQueryParam aQueryParam(aDesc.GetQueryParam());
    3213           1 :     CPPUNIT_ASSERT_MESSAGE("There should be at least one query entry.", aQueryParam.GetEntryCount() > 0);
    3214           1 :     ScQueryEntry& rEntry = aQueryParam.GetEntry(0);
    3215           1 :     rEntry.bDoQuery = true;
    3216           1 :     rEntry.nField = 1;  // Group1
    3217           1 :     rEntry.GetQueryItem().mfVal = 1;
    3218           1 :     aDesc.SetQueryParam(aQueryParam);
    3219           1 :     pDPObj->SetSheetDesc(aDesc);
    3220           1 :     aOutRange = refresh(pDPObj);
    3221             :     {
    3222             :         // Expected output table content.  0 = empty cell
    3223             :         const char* aOutputCheck[][2] = {
    3224             :             { "Filter", 0 },
    3225             :             { "Group2", "A" },
    3226             :             { 0, 0 },
    3227             :             { "Data", 0 },
    3228             :             { "Sum - Val1", "2" },
    3229             :             { "Sum - Val2", "20" }
    3230           1 :         };
    3231             : 
    3232           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by query)");
    3233           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3234             :     }
    3235             : 
    3236           1 :     fTest = m_pDoc->GetValue(aFormulaAddr);
    3237           1 :     CPPUNIT_ASSERT_MESSAGE("Incorrect formula value that references a cell in the pivot table output.", fTest == 20.0);
    3238             : 
    3239             :     // Set the current page of 'Group2' back to '- all -'. The query filter
    3240             :     // should still be in effect.
    3241           1 :     pPageDim->SetCurrentPage(NULL); // Remove the page.
    3242           1 :     pDPObj->SetSaveData(aSaveData);
    3243           1 :     aOutRange = refresh(pDPObj);
    3244             :     {
    3245             :         // Expected output table content.  0 = empty cell
    3246             :         const char* aOutputCheck[][2] = {
    3247             :             { "Filter", 0 },
    3248             :             { "Group2", "- all -" },
    3249             :             { 0, 0 },
    3250             :             { "Data", 0 },
    3251             :             { "Sum - Val1", "4" },
    3252             :             { "Sum - Val2", "40" }
    3253           1 :         };
    3254             : 
    3255           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output (filtered by page)");
    3256           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3257             :     }
    3258             : 
    3259             : 
    3260           1 :     pDPs->FreeTable(pDPObj);
    3261           2 :     CPPUNIT_ASSERT_MESSAGE("There shouldn't be any data pilot table stored with the document.",
    3262           1 :                            pDPs->GetCount() == 0);
    3263             : 
    3264           1 :     m_pDoc->DeleteTab(1);
    3265           2 :     m_pDoc->DeleteTab(0);
    3266           1 : }
    3267             : 
    3268           1 : void Test::testPivotTableNamedSource()
    3269             : {
    3270           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    3271           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    3272             : 
    3273             :     // Dimension definition
    3274             :     DPFieldDef aFields[] = {
    3275             :         { "Name",  sheet::DataPilotFieldOrientation_ROW, 0 },
    3276             :         { "Group", sheet::DataPilotFieldOrientation_COLUMN, 0 },
    3277             :         { "Score", sheet::DataPilotFieldOrientation_DATA, 0 }
    3278           1 :     };
    3279             : 
    3280             :     // Raw data
    3281             :     const char* aData[][3] = {
    3282             :         { "Andy",    "A", "30" },
    3283             :         { "Bruce",   "A", "20" },
    3284             :         { "Charlie", "B", "45" },
    3285             :         { "David",   "B", "12" },
    3286             :         { "Edward",  "C",  "8" },
    3287             :         { "Frank",   "C", "15" },
    3288           1 :     };
    3289             : 
    3290           1 :     size_t nFieldCount = SAL_N_ELEMENTS(aFields);
    3291           1 :     size_t nDataCount = SAL_N_ELEMENTS(aData);
    3292             : 
    3293             :     // Insert the raw data.
    3294           1 :     ScRange aSrcRange = insertDPSourceData(m_pDoc, aFields, nFieldCount, aData, nDataCount);
    3295           1 :     OUString aRangeStr;
    3296           1 :     aSrcRange.Format(aRangeStr, SCR_ABS_3D, m_pDoc);
    3297             : 
    3298             :     // Name this range.
    3299           2 :     OUString aRangeName("MyData");
    3300           1 :     ScRangeName* pNames = m_pDoc->GetRangeName();
    3301           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to get global range name container.", pNames);
    3302             :     ScRangeData* pName = new ScRangeData(
    3303           1 :         m_pDoc, aRangeName, aRangeStr);
    3304           1 :     bool bSuccess = pNames->insert(pName);
    3305           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bSuccess);
    3306             : 
    3307           2 :     ScSheetSourceDesc aSheetDesc(m_pDoc);
    3308           1 :     aSheetDesc.SetRangeName(aRangeName);
    3309           1 :     ScDPObject* pDPObj = createDPFromSourceDesc(m_pDoc, aSheetDesc, aFields, nFieldCount, false);
    3310           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to create a new pivot table object.", pDPObj);
    3311             : 
    3312           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    3313           1 :     bSuccess = pDPs->InsertNewTable(pDPObj);
    3314           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    3315           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    3316           1 :                            pDPs->GetCount() == 1);
    3317           1 :     pDPObj->SetName(pDPs->CreateNewName());
    3318             : 
    3319           1 :     ScRange aOutRange = refresh(pDPObj);
    3320             :     {
    3321             :         // Expected output table content.  0 = empty cell
    3322             :         const char* aOutputCheck[][5] = {
    3323             :             { "Sum - Score", "Group", 0, 0, 0 },
    3324             :             { "Name", "A", "B", "C", "Total Result" },
    3325             :             { "Andy", "30", 0, 0, "30" },
    3326             :             { "Bruce", "20", 0, 0, "20" },
    3327             :             { "Charlie", 0, "45", 0, "45" },
    3328             :             { "David", 0, "12", 0, "12" },
    3329             :             { "Edward", 0, 0, "8", "8" },
    3330             :             { "Frank", 0, 0, "15", "15" },
    3331             :             { "Total Result", "50", "57", "23", "130" }
    3332           1 :         };
    3333             : 
    3334           1 :         bSuccess = checkDPTableOutput<5>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
    3335           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3336             :     }
    3337             : 
    3338           2 :     CPPUNIT_ASSERT_MESSAGE("There should be one named range data cache.",
    3339           1 :                            pDPs->GetNameCaches().size() == 1 && pDPs->GetSheetCaches().size() == 0);
    3340             : 
    3341             :     // Move the table with pivot table to the left of the source data sheet.
    3342           1 :     m_pDoc->MoveTab(1, 0);
    3343           2 :     OUString aTabName;
    3344           1 :     m_pDoc->GetName(0, aTabName);
    3345           1 :     CPPUNIT_ASSERT_MESSAGE( "Wrong sheet name.", aTabName == "Table" );
    3346           2 :     CPPUNIT_ASSERT_MESSAGE("Pivot table output is on the wrong sheet!",
    3347           1 :                            pDPObj->GetOutRange().aStart.Tab() == 0);
    3348             : 
    3349           2 :     CPPUNIT_ASSERT_MESSAGE("Moving the pivot table to another sheet shouldn't have changed the cache state.",
    3350           1 :                            pDPs->GetNameCaches().size() == 1 && pDPs->GetSheetCaches().size() == 0);
    3351             : 
    3352           1 :     const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc();
    3353           1 :     CPPUNIT_ASSERT_MESSAGE("Sheet source description doesn't exist.", pDesc);
    3354           2 :     CPPUNIT_ASSERT_MESSAGE("Named source range has been altered unexpectedly!",
    3355           1 :                            pDesc->GetRangeName().equals(aRangeName));
    3356             : 
    3357           1 :     CPPUNIT_ASSERT_MESSAGE("Cache should exist.", pDPs->GetNameCaches().hasCache(aRangeName));
    3358             : 
    3359           1 :     pDPs->FreeTable(pDPObj);
    3360           1 :     CPPUNIT_ASSERT_MESSAGE("There should be no more tables.", pDPs->GetCount() == 0);
    3361           2 :     CPPUNIT_ASSERT_MESSAGE("There shouldn't be any more cache stored.",
    3362           1 :                            pDPs->GetNameCaches().size() == 0);
    3363             : 
    3364           1 :     pNames->clear();
    3365           1 :     m_pDoc->DeleteTab(1);
    3366           2 :     m_pDoc->DeleteTab(0);
    3367           1 : }
    3368             : 
    3369           1 : void Test::testPivotTableCache()
    3370             : {
    3371           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    3372             : 
    3373             :     // Raw data
    3374             :     const char* aData[][3] = {
    3375             :         { "F1", "F2", "F3" },
    3376             :         { "Z",  "A", "30" },
    3377             :         { "R",  "A", "20" },
    3378             :         { "A",  "B", "45" },
    3379             :         { "F",  "B", "12" },
    3380             :         { "Y",  "C",  "8" },
    3381             :         { "12", "C", "15" },
    3382           1 :     };
    3383             : 
    3384           1 :     ScAddress aPos(1,1,0);
    3385           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    3386           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    3387             : 
    3388           1 :     ScDPCache aCache(m_pDoc);
    3389           1 :     aCache.InitFromDoc(m_pDoc, aDataRange);
    3390           1 :     long nDimCount = aCache.GetColumnCount();
    3391           1 :     CPPUNIT_ASSERT_MESSAGE("wrong dimension count.", nDimCount == 3);
    3392           2 :     OUString aDimName = aCache.GetDimensionName(0);
    3393           1 :     CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F1"));
    3394           1 :     aDimName = aCache.GetDimensionName(1);
    3395           1 :     CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F2"));
    3396           1 :     aDimName = aCache.GetDimensionName(2);
    3397           1 :     CPPUNIT_ASSERT_MESSAGE("wrong dimension name", aDimName.equalsAscii("F3"));
    3398             : 
    3399             :     // In each dimension, member ID values also represent their sort order (in
    3400             :     // source dimensions only, not in group dimensions). Value items are
    3401             :     // sorted before string ones. Also, no duplicate dimension members should
    3402             :     // exist.
    3403             : 
    3404             :     // Dimension 0 - a mix of strings and values.
    3405           1 :     long nMemCount = aCache.GetDimMemberCount(0);
    3406           1 :     CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 6);
    3407           1 :     const ScDPItemData* pItem = aCache.GetItemDataById(0, 0);
    3408           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3409             :                            pItem->GetType() == ScDPItemData::Value &&
    3410           1 :                            pItem->GetValue() == 12);
    3411           1 :     pItem = aCache.GetItemDataById(0, 1);
    3412           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3413             :                            pItem->GetType() == ScDPItemData::String &&
    3414           1 :                            pItem->GetString().equalsAscii("A"));
    3415           1 :     pItem = aCache.GetItemDataById(0, 2);
    3416           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3417             :                            pItem->GetType() == ScDPItemData::String &&
    3418           1 :                            pItem->GetString().equalsAscii("F"));
    3419           1 :     pItem = aCache.GetItemDataById(0, 3);
    3420           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3421             :                            pItem->GetType() == ScDPItemData::String &&
    3422           1 :                            pItem->GetString().equalsAscii("R"));
    3423           1 :     pItem = aCache.GetItemDataById(0, 4);
    3424           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3425             :                            pItem->GetType() == ScDPItemData::String &&
    3426           1 :                            pItem->GetString().equalsAscii("Y"));
    3427           1 :     pItem = aCache.GetItemDataById(0, 5);
    3428           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3429             :                            pItem->GetType() == ScDPItemData::String &&
    3430           1 :                            pItem->GetString().equalsAscii("Z"));
    3431           1 :     pItem = aCache.GetItemDataById(0, 6);
    3432           1 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
    3433             : 
    3434             :     // Dimension 1 - duplicate values in source.
    3435           1 :     nMemCount = aCache.GetDimMemberCount(1);
    3436           1 :     CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 3);
    3437           1 :     pItem = aCache.GetItemDataById(1, 0);
    3438           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3439             :                            pItem->GetType() == ScDPItemData::String &&
    3440           1 :                            pItem->GetString().equalsAscii("A"));
    3441           1 :     pItem = aCache.GetItemDataById(1, 1);
    3442           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3443             :                            pItem->GetType() == ScDPItemData::String &&
    3444           1 :                            pItem->GetString().equalsAscii("B"));
    3445           1 :     pItem = aCache.GetItemDataById(1, 2);
    3446           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3447             :                            pItem->GetType() == ScDPItemData::String &&
    3448           1 :                            pItem->GetString().equalsAscii("C"));
    3449           1 :     pItem = aCache.GetItemDataById(1, 3);
    3450           1 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
    3451             : 
    3452             :     // Dimension 2 - values only.
    3453           1 :     nMemCount = aCache.GetDimMemberCount(2);
    3454           1 :     CPPUNIT_ASSERT_MESSAGE("wrong dimension member count", nMemCount == 6);
    3455           1 :     pItem = aCache.GetItemDataById(2, 0);
    3456           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3457             :                            pItem->GetType() == ScDPItemData::Value &&
    3458           1 :                            pItem->GetValue() == 8);
    3459           1 :     pItem = aCache.GetItemDataById(2, 1);
    3460           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3461             :                            pItem->GetType() == ScDPItemData::Value &&
    3462           1 :                            pItem->GetValue() == 12);
    3463           1 :     pItem = aCache.GetItemDataById(2, 2);
    3464           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3465             :                            pItem->GetType() == ScDPItemData::Value &&
    3466           1 :                            pItem->GetValue() == 15);
    3467           1 :     pItem = aCache.GetItemDataById(2, 3);
    3468           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3469             :                            pItem->GetType() == ScDPItemData::Value &&
    3470           1 :                            pItem->GetValue() == 20);
    3471           1 :     pItem = aCache.GetItemDataById(2, 4);
    3472           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3473             :                            pItem->GetType() == ScDPItemData::Value &&
    3474           1 :                            pItem->GetValue() == 30);
    3475           1 :     pItem = aCache.GetItemDataById(2, 5);
    3476           2 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", pItem &&
    3477             :                            pItem->GetType() == ScDPItemData::Value &&
    3478           1 :                            pItem->GetValue() == 45);
    3479           1 :     pItem = aCache.GetItemDataById(2, 6);
    3480           1 :     CPPUNIT_ASSERT_MESSAGE("wrong item value", !pItem);
    3481             : 
    3482             :     {
    3483             :         // Check the integrity of the source data.
    3484           1 :         ScDPItemData aTest;
    3485             :         long nDim;
    3486             : 
    3487             :         {
    3488             :             // Dimension 0: Z, R, A, F, Y, 12
    3489           1 :             nDim = 0;
    3490           1 :             const char* aChecks[] = { "Z", "R", "A", "F", "Y" };
    3491           6 :             for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    3492             :             {
    3493           5 :                 pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
    3494           5 :                 aTest.SetString(OUString::createFromAscii(aChecks[i]));
    3495           5 :                 CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
    3496             :             }
    3497             : 
    3498           1 :             pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, 5, false));
    3499           1 :             aTest.SetValue(12);
    3500           1 :             CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
    3501             :         }
    3502             : 
    3503             :         {
    3504             :             // Dimension 1: A, A, B, B, C, C
    3505           1 :             nDim = 1;
    3506           1 :             const char* aChecks[] = { "A", "A", "B", "B", "C", "C" };
    3507           7 :             for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    3508             :             {
    3509           6 :                 pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
    3510           6 :                 aTest.SetString(OUString::createFromAscii(aChecks[i]));
    3511           6 :                 CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
    3512             :             }
    3513             :         }
    3514             : 
    3515             :         {
    3516             :             // Dimension 2: 30, 20, 45, 12, 8, 15
    3517           1 :             nDim = 2;
    3518           1 :             double aChecks[] = { 30, 20, 45, 12, 8, 15 };
    3519           7 :             for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    3520             :             {
    3521           6 :                 pItem = aCache.GetItemDataById(nDim, aCache.GetItemDataId(nDim, i, false));
    3522           6 :                 aTest.SetValue(aChecks[i]);
    3523           6 :                 CPPUNIT_ASSERT_MESSAGE("wrong data value", pItem && *pItem == aTest);
    3524             :             }
    3525           1 :         }
    3526             :     }
    3527             : 
    3528             :     // Now, on to testing the filtered cache.
    3529             : 
    3530             :     {
    3531             :         // Non-filtered cache - everything should be visible.
    3532           1 :         ScDPFilteredCache aFilteredCache(aCache);
    3533           1 :         aFilteredCache.fillTable();
    3534             : 
    3535           1 :         sal_Int32 nRows = aFilteredCache.getRowSize();
    3536           1 :         CPPUNIT_ASSERT_MESSAGE("Wrong dimension.", nRows == 6 && aFilteredCache.getColSize() == 3);
    3537             : 
    3538           7 :         for (sal_Int32 i = 0; i < nRows; ++i)
    3539             :         {
    3540           6 :             if (!aFilteredCache.isRowActive(i))
    3541             :             {
    3542           0 :                 std::ostringstream os;
    3543           0 :                 os << "Row " << i << " should be visible but it isn't.";
    3544           0 :                 CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
    3545             :             }
    3546           1 :         }
    3547             :     }
    3548             : 
    3549             :     // TODO : Add test for filtered caches.
    3550             : 
    3551           2 :     m_pDoc->DeleteTab(0);
    3552           1 : }
    3553             : 
    3554           1 : void Test::testPivotTableDuplicateDataFields()
    3555             : {
    3556           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    3557           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    3558             : 
    3559             :     // Raw data
    3560             :     const char* aData[][2] = {
    3561             :         { "Name", "Value" },
    3562             :         { "A",       "45" },
    3563             :         { "A",        "5" },
    3564             :         { "A",       "41" },
    3565             :         { "A",       "49" },
    3566             :         { "A",        "4" },
    3567             :         { "B",       "33" },
    3568             :         { "B",       "84" },
    3569             :         { "B",       "74" },
    3570             :         { "B",        "8" },
    3571             :         { "B",       "68" }
    3572           1 :     };
    3573             : 
    3574             :     // Dimension definition
    3575             :     DPFieldDef aFields[] = {
    3576             :         { "Name",  sheet::DataPilotFieldOrientation_ROW, 0 },
    3577             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    3578             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_COUNT }
    3579           1 :     };
    3580             : 
    3581           1 :     ScAddress aPos(2,2,0);
    3582           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    3583           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    3584             : 
    3585             :     ScDPObject* pDPObj = createDPFromRange(
    3586           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    3587             : 
    3588           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    3589           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    3590             : 
    3591           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    3592           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
    3593           1 :                            pDPs->GetCount(), static_cast<size_t>(1));
    3594           1 :     pDPObj->SetName(pDPs->CreateNewName());
    3595             : 
    3596           1 :     ScRange aOutRange = refresh(pDPObj);
    3597             :     {
    3598             :         // Expected output table content.  0 = empty cell
    3599             :         const char* aOutputCheck[][3] = {
    3600             :             { "Name", "Data", 0 },
    3601             :             { "A", "Sum - Value", "144" },
    3602             :             { 0, "Count - Value", "5" },
    3603             :             { "B", "Sum - Value", "267" },
    3604             :             { 0, "Count - Value", "5" },
    3605             :             { "Total Sum - Value", 0, "411" },
    3606             :             { "Total Count - Value", 0, "10" },
    3607           1 :         };
    3608             : 
    3609           1 :         bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
    3610           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3611             :     }
    3612             : 
    3613             :     // Move the data layout dimension from row to column.
    3614           1 :     ScDPSaveData* pSaveData = pDPObj->GetSaveData();
    3615           1 :     CPPUNIT_ASSERT_MESSAGE("No save data!?", pSaveData);
    3616           1 :     ScDPSaveDimension* pDataLayout = pSaveData->GetDataLayoutDimension();
    3617           1 :     CPPUNIT_ASSERT_MESSAGE("No data layout dimension.", pDataLayout);
    3618           1 :     pDataLayout->SetOrientation(sheet::DataPilotFieldOrientation_COLUMN);
    3619           1 :     pDPObj->SetSaveData(*pSaveData);
    3620             : 
    3621             :     // Refresh the table output.
    3622           1 :     aOutRange = refresh(pDPObj);
    3623             :     {
    3624             :         // Expected output table content.  0 = empty cell
    3625             :         const char* aOutputCheck[][3] = {
    3626             :             { 0, "Data", 0 },
    3627             :             { "Name", "Sum - Value", "Count - Value" },
    3628             :             { "A", "144", "5" },
    3629             :             { "B", "267", "5" },
    3630             :             { "Total Result", "411", "10" }
    3631           1 :         };
    3632             : 
    3633           1 :         bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "DataPilot table output");
    3634           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3635             :     }
    3636             : 
    3637           1 :     ScPivotParam aParam;
    3638           1 :     pDPObj->FillLabelData(aParam);
    3639           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be exactly 4 labels (2 original, 1 data layout, and 1 duplicate dimensions).",
    3640           1 :                            aParam.maLabelArray.size(), static_cast<size_t>(4));
    3641             : 
    3642           1 :     pDPs->FreeTable(pDPObj);
    3643           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    3644           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    3645           1 :                            pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    3646             : 
    3647           1 :     m_pDoc->DeleteTab(1);
    3648           1 :     m_pDoc->DeleteTab(0);
    3649           1 : }
    3650             : 
    3651           1 : void Test::testPivotTableNormalGrouping()
    3652             : {
    3653           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    3654           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    3655             : 
    3656             :     // Raw data
    3657             :     const char* aData[][2] = {
    3658             :         { "Name", "Value" },
    3659             :         { "A", "1" },
    3660             :         { "B", "2" },
    3661             :         { "C", "3" },
    3662             :         { "D", "4" },
    3663             :         { "E", "5" },
    3664             :         { "F", "6" },
    3665             :         { "G", "7" }
    3666           1 :     };
    3667             : 
    3668             :     // Dimension definition
    3669             :     DPFieldDef aFields[] = {
    3670             :         { "Name",  sheet::DataPilotFieldOrientation_ROW, 0 },
    3671             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    3672           1 :     };
    3673             : 
    3674           1 :     ScAddress aPos(1,1,0);
    3675           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    3676           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    3677             : 
    3678             :     ScDPObject* pDPObj = createDPFromRange(
    3679           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    3680             : 
    3681           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    3682           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    3683             : 
    3684           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    3685           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
    3686           1 :                            pDPs->GetCount(), static_cast<size_t>(1));
    3687           1 :     pDPObj->SetName(pDPs->CreateNewName());
    3688             : 
    3689           1 :     ScRange aOutRange = refresh(pDPObj);
    3690             :     {
    3691             :         // Expected output table content.  0 = empty cell
    3692             :         const char* aOutputCheck[][2] = {
    3693             :             { "Name", 0 },
    3694             :             { "A", "1" },
    3695             :             { "B", "2" },
    3696             :             { "C", "3" },
    3697             :             { "D", "4" },
    3698             :             { "E", "5" },
    3699             :             { "F", "6" },
    3700             :             { "G", "7" },
    3701             :             { "Total Result", "28" }
    3702           1 :         };
    3703             : 
    3704           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Initial output without grouping");
    3705           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3706             :     }
    3707             : 
    3708           1 :     ScDPSaveData* pSaveData = pDPObj->GetSaveData();
    3709           1 :     CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
    3710           1 :     ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
    3711           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to create dimension data.", pDimData);
    3712             : 
    3713           1 :     OUString aGroupPrefix("Group");
    3714           2 :     OUString aBaseDimName("Name");
    3715             :     OUString aGroupDimName =
    3716           2 :         pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, NULL);
    3717             : 
    3718             :     {
    3719             :         // Group A, B and C together.
    3720           1 :         ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
    3721           2 :         OUString aGroupName = aGroupDim.CreateGroupName(aGroupPrefix);
    3722           1 :         CPPUNIT_ASSERT_MESSAGE("Unexpected group name", aGroupName.equalsAscii("Group1"));
    3723             : 
    3724           2 :         ScDPSaveGroupItem aGroup(aGroupName);
    3725           1 :         aGroup.AddElement(OUString("A"));
    3726           1 :         aGroup.AddElement(OUString("B"));
    3727           1 :         aGroup.AddElement(OUString("C"));
    3728           1 :         aGroupDim.AddGroupItem(aGroup);
    3729           1 :         pDimData->AddGroupDimension(aGroupDim);
    3730             : 
    3731           1 :         ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
    3732           1 :         pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
    3733           2 :         pSaveData->SetPosition(pDim, 0); // Set it before the base dimension.
    3734             :     }
    3735             : 
    3736           1 :     pDPObj->SetSaveData(*pSaveData);
    3737           1 :     aOutRange = refreshGroups(pDPs, pDPObj);
    3738             :     {
    3739             :         // Expected output table content.  0 = empty cell
    3740             :         const char* aOutputCheck[][3] = {
    3741             :             { "Name2", "Name", 0 },
    3742             :             { "D", "D", "4" },
    3743             :             { "E", "E", "5" },
    3744             :             { "F", "F", "6" },
    3745             :             { "G", "G", "7" },
    3746             :             { "Group1", "A", "1" },
    3747             :             { 0,        "B", "2" },
    3748             :             { 0,        "C", "3" },
    3749             :             { "Total Result", 0, "28" }
    3750           1 :         };
    3751             : 
    3752           1 :         bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "A, B, C grouped by Group1.");
    3753           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3754             :     }
    3755             : 
    3756           1 :     pSaveData = pDPObj->GetSaveData();
    3757           1 :     pDimData = pSaveData->GetDimensionData();
    3758             : 
    3759             :     {
    3760             :         // Group D, E, F together.
    3761           1 :         ScDPSaveGroupDimension* pGroupDim = pDimData->GetGroupDimAccForBase(aBaseDimName);
    3762           1 :         CPPUNIT_ASSERT_MESSAGE("There should be an existing group dimension.", pGroupDim);
    3763           1 :         OUString aGroupName = pGroupDim->CreateGroupName(aGroupPrefix);
    3764           1 :         CPPUNIT_ASSERT_MESSAGE("Unexpected group name", aGroupName.equalsAscii("Group2"));
    3765             : 
    3766           2 :         ScDPSaveGroupItem aGroup(aGroupName);
    3767           1 :         aGroup.AddElement(OUString("D"));
    3768           1 :         aGroup.AddElement(OUString("E"));
    3769           1 :         aGroup.AddElement(OUString("F"));
    3770           2 :         pGroupDim->AddGroupItem(aGroup);
    3771             :     }
    3772             : 
    3773           1 :     pDPObj->SetSaveData(*pSaveData);
    3774           1 :     aOutRange = refreshGroups(pDPs, pDPObj);
    3775             :     {
    3776             :         // Expected output table content.  0 = empty cell
    3777             :         const char* aOutputCheck[][3] = {
    3778             :             { "Name2", "Name", 0 },
    3779             :             { "G", "G", "7" },
    3780             :             { "Group1", "A", "1" },
    3781             :             { 0,        "B", "2" },
    3782             :             { 0,        "C", "3" },
    3783             :             { "Group2", "D", "4" },
    3784             :             { 0,        "E", "5" },
    3785             :             { 0,        "F", "6" },
    3786             :             { "Total Result", 0, "28" }
    3787           1 :         };
    3788             : 
    3789           1 :         bSuccess = checkDPTableOutput<3>(m_pDoc, aOutRange, aOutputCheck, "D, E, F grouped by Group2.");
    3790           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3791             :     }
    3792             : 
    3793           1 :     pDPs->FreeTable(pDPObj);
    3794           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    3795           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    3796           1 :                            pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    3797             : 
    3798           1 :     m_pDoc->DeleteTab(1);
    3799           2 :     m_pDoc->DeleteTab(0);
    3800           1 : }
    3801             : 
    3802           1 : void Test::testPivotTableNumberGrouping()
    3803             : {
    3804           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    3805           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    3806             : 
    3807             :     // Raw data
    3808             :     const char* aData[][2] = {
    3809             :         { "Order", "Score" },
    3810             :         { "43", "171" },
    3811             :         { "18", "20"  },
    3812             :         { "69", "159" },
    3813             :         { "95", "19"  },
    3814             :         { "96", "163" },
    3815             :         { "46", "70"  },
    3816             :         { "22", "36"  },
    3817             :         { "81", "49"  },
    3818             :         { "54", "61"  },
    3819             :         { "39", "62"  },
    3820             :         { "86", "17"  },
    3821             :         { "34", "0"   },
    3822             :         { "30", "25"  },
    3823             :         { "24", "103" },
    3824             :         { "16", "59"  },
    3825             :         { "24", "119" },
    3826             :         { "15", "86"  },
    3827             :         { "69", "170" }
    3828           1 :     };
    3829             : 
    3830             :     // Dimension definition
    3831             :     DPFieldDef aFields[] = {
    3832             :         { "Order", sheet::DataPilotFieldOrientation_ROW, 0 },
    3833             :         { "Score", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    3834           1 :     };
    3835             : 
    3836           1 :     ScAddress aPos(1,1,0);
    3837           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    3838           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    3839             : 
    3840             :     ScDPObject* pDPObj = createDPFromRange(
    3841           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    3842             : 
    3843           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    3844           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    3845             : 
    3846           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    3847           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
    3848           1 :                            pDPs->GetCount(), static_cast<size_t>(1));
    3849           1 :     pDPObj->SetName(pDPs->CreateNewName());
    3850             : 
    3851           1 :     ScDPSaveData* pSaveData = pDPObj->GetSaveData();
    3852           1 :     CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
    3853           1 :     ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
    3854           1 :     CPPUNIT_ASSERT_MESSAGE("No dimension data !?", pDimData);
    3855             : 
    3856             :     {
    3857           1 :         ScDPNumGroupInfo aInfo;
    3858           1 :         aInfo.mbEnable = true;
    3859           1 :         aInfo.mbAutoStart = false;
    3860           1 :         aInfo.mbAutoEnd = false;
    3861           1 :         aInfo.mbDateValues = false;
    3862           1 :         aInfo.mbIntegerOnly = true;
    3863           1 :         aInfo.mfStart = 30;
    3864           1 :         aInfo.mfEnd = 60;
    3865           1 :         aInfo.mfStep = 10;
    3866           1 :         ScDPSaveNumGroupDimension aGroup(OUString("Order"), aInfo);
    3867           1 :         pDimData->AddNumGroupDimension(aGroup);
    3868             :     }
    3869             : 
    3870           1 :     pDPObj->SetSaveData(*pSaveData);
    3871           1 :     ScRange aOutRange = refreshGroups(pDPs, pDPObj);
    3872             :     {
    3873             :         // Expected output table content.  0 = empty cell
    3874             :         const char* aOutputCheck[][2] = {
    3875             :             { "Order", 0 },
    3876             :             { "<30",   "423" },
    3877             :             { "30-39", "87"  },
    3878             :             { "40-49", "241" },
    3879             :             { "50-60", "61"  },
    3880             :             { ">60",   "577" },
    3881             :             { "Total Result", "1389" }
    3882           1 :         };
    3883             : 
    3884           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Order grouped by numbers");
    3885           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    3886             :     }
    3887             : 
    3888           1 :     pDPs->FreeTable(pDPObj);
    3889           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    3890           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    3891           1 :                            pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    3892             : 
    3893           1 :     m_pDoc->DeleteTab(1);
    3894           1 :     m_pDoc->DeleteTab(0);
    3895           1 : }
    3896             : 
    3897           1 : void Test::testPivotTableDateGrouping()
    3898             : {
    3899           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    3900           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    3901             : 
    3902             :     // Raw data
    3903             :     const char* aData[][2] = {
    3904             :         { "Date", "Value" },
    3905             :         { "2011-01-01", "1" },
    3906             :         { "2011-03-02", "2" },
    3907             :         { "2012-01-04", "3" },
    3908             :         { "2012-02-23", "4" },
    3909             :         { "2012-02-24", "5" },
    3910             :         { "2012-03-15", "6" },
    3911             :         { "2011-09-03", "7" },
    3912             :         { "2012-12-25", "8" }
    3913           1 :     };
    3914             : 
    3915             :     // Dimension definition
    3916             :     DPFieldDef aFields[] = {
    3917             :         { "Date", sheet::DataPilotFieldOrientation_ROW, 0 },
    3918             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    3919           1 :     };
    3920             : 
    3921           1 :     ScAddress aPos(1,1,0);
    3922           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    3923           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    3924             : 
    3925             :     ScDPObject* pDPObj = createDPFromRange(
    3926           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    3927             : 
    3928           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    3929           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    3930             : 
    3931           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    3932           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    3933           1 :                            pDPs->GetCount() == 1);
    3934           1 :     pDPObj->SetName(pDPs->CreateNewName());
    3935             : 
    3936           1 :     ScDPSaveData* pSaveData = pDPObj->GetSaveData();
    3937           1 :     CPPUNIT_ASSERT_MESSAGE("No save data !?", pSaveData);
    3938           1 :     ScDPDimensionSaveData* pDimData = pSaveData->GetDimensionData();
    3939           1 :     CPPUNIT_ASSERT_MESSAGE("No dimension data !?", pDimData);
    3940             : 
    3941           1 :     OUString aBaseDimName("Date");
    3942             : 
    3943           1 :     ScDPNumGroupInfo aInfo;
    3944           1 :     aInfo.mbEnable = true;
    3945           1 :     aInfo.mbAutoStart = true;
    3946           1 :     aInfo.mbAutoEnd = true;
    3947             :     {
    3948             :         // Turn the Date dimension into months.  The first of the date
    3949             :         // dimensions is always a number-group dimension which replaces the
    3950             :         // original dimension.
    3951           1 :         ScDPSaveNumGroupDimension aGroup(aBaseDimName, aInfo, sheet::DataPilotFieldGroupBy::MONTHS);
    3952           1 :         pDimData->AddNumGroupDimension(aGroup);
    3953             :     }
    3954             : 
    3955             :     {
    3956             :         // Add quarter dimension.  This will be an additional dimension.
    3957             :         OUString aGroupDimName =
    3958             :             pDimData->CreateDateGroupDimName(
    3959           1 :                 sheet::DataPilotFieldGroupBy::QUARTERS, *pDPObj, true, NULL);
    3960           2 :         ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
    3961           1 :         aGroupDim.SetDateInfo(aInfo, sheet::DataPilotFieldGroupBy::QUARTERS);
    3962           1 :         pDimData->AddGroupDimension(aGroupDim);
    3963             : 
    3964             :         // Set orientation.
    3965           1 :         ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
    3966           1 :         pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
    3967           2 :         pSaveData->SetPosition(pDim, 0); // set it to the left end.
    3968             :     }
    3969             : 
    3970             :     {
    3971             :         // Add year dimension.  This is a new dimension also.
    3972             :         OUString aGroupDimName =
    3973             :             pDimData->CreateDateGroupDimName(
    3974           1 :                 sheet::DataPilotFieldGroupBy::YEARS, *pDPObj, true, NULL);
    3975           2 :         ScDPSaveGroupDimension aGroupDim(aBaseDimName, aGroupDimName);
    3976           1 :         aGroupDim.SetDateInfo(aInfo, sheet::DataPilotFieldGroupBy::YEARS);
    3977           1 :         pDimData->AddGroupDimension(aGroupDim);
    3978             : 
    3979             :         // Set orientation.
    3980           1 :         ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aGroupDimName);
    3981           1 :         pDim->SetOrientation(sheet::DataPilotFieldOrientation_ROW);
    3982           2 :         pSaveData->SetPosition(pDim, 0); // set it to the left end.
    3983             :     }
    3984             : 
    3985           1 :     pDPObj->SetSaveData(*pSaveData);
    3986           1 :     ScRange aOutRange = refreshGroups(pDPs, pDPObj);
    3987             :     {
    3988             :         // Expected output table content.  0 = empty cell
    3989             :         const char* aOutputCheck[][4] = {
    3990             :             { "Years", "Quarters", "Date", 0 },
    3991             :             { "2011", "Q1", "Jan", "1" },
    3992             :             { 0, 0,         "Mar", "2" },
    3993             :             { 0,      "Q3", "Sep", "7" },
    3994             :             { "2012", "Q1", "Jan", "3" },
    3995             :             { 0, 0,         "Feb", "9" },
    3996             :             { 0, 0,         "Mar", "6" },
    3997             :             { 0,      "Q4", "Dec", "8" },
    3998             :             { "Total Result", 0, 0, "36" },
    3999           1 :         };
    4000             : 
    4001           1 :         bSuccess = checkDPTableOutput<4>(m_pDoc, aOutRange, aOutputCheck, "Years, quarters and months date groups.");
    4002           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4003             :     }
    4004             : 
    4005             :     {
    4006             :         // Let's hide year 2012.
    4007           1 :         pSaveData = pDPObj->GetSaveData();
    4008           1 :         ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(OUString("Years"));
    4009           1 :         CPPUNIT_ASSERT_MESSAGE("Years dimension should exist.", pDim);
    4010           1 :         ScDPSaveMember* pMem = pDim->GetMemberByName(OUString("2012"));
    4011           1 :         CPPUNIT_ASSERT_MESSAGE("Member should exist.", pMem);
    4012           1 :         pMem->SetIsVisible(false);
    4013             :     }
    4014           1 :     pDPObj->SetSaveData(*pSaveData);
    4015           1 :     pDPObj->ReloadGroupTableData();
    4016           1 :     pDPObj->InvalidateData();
    4017             : 
    4018           1 :     aOutRange = refresh(pDPObj);
    4019             :     {
    4020             :         // Expected output table content.  0 = empty cell
    4021             :         const char* aOutputCheck[][4] = {
    4022             :             { "Years", "Quarters", "Date", 0 },
    4023             :             { "2011", "Q1", "Jan", "1" },
    4024             :             { 0, 0,         "Mar", "2" },
    4025             :             { 0,      "Q3", "Sep", "7" },
    4026             :             { "Total Result", 0, 0, "10" },
    4027           1 :         };
    4028             : 
    4029           1 :         bSuccess = checkDPTableOutput<4>(m_pDoc, aOutRange, aOutputCheck, "Year 2012 data now hidden");
    4030           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4031             :     }
    4032             : 
    4033             :     // Remove all date grouping. The source dimension "Date" has two
    4034             :     // external dimensions ("Years" and "Quarters") and one internal ("Date"
    4035             :     // the same name but different hierarchy).  Remove all of them.
    4036           1 :     pSaveData = pDPObj->GetSaveData();
    4037           1 :     pSaveData->RemoveAllGroupDimensions(aBaseDimName);
    4038           1 :     pDPObj->SetSaveData(*pSaveData);
    4039           1 :     pDPObj->ReloadGroupTableData();
    4040           1 :     pDPObj->InvalidateData();
    4041             : 
    4042           1 :     aOutRange = refresh(pDPObj);
    4043             :     {
    4044             :         // Expected output table content.  0 = empty cell
    4045             :         const char* aOutputCheck[][2] = {
    4046             :             { "Date", 0 },
    4047             :             { "2011-01-01", "1" },
    4048             :             { "2011-03-02", "2" },
    4049             :             { "2011-09-03", "7" },
    4050             :             { "2012-01-04", "3" },
    4051             :             { "2012-02-23", "4" },
    4052             :             { "2012-02-24", "5" },
    4053             :             { "2012-03-15", "6" },
    4054             :             { "2012-12-25", "8" },
    4055             :             { "Total Result", "36" }
    4056           1 :         };
    4057             : 
    4058           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Remove all date grouping.");
    4059           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4060             :     }
    4061             : 
    4062           1 :     pDPs->FreeTable(pDPObj);
    4063           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    4064           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    4065           1 :                            pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    4066             : 
    4067           1 :     m_pDoc->DeleteTab(1);
    4068           1 :     m_pDoc->DeleteTab(0);
    4069           1 : }
    4070             : 
    4071           1 : void Test::testPivotTableEmptyRows()
    4072             : {
    4073           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    4074           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    4075             : 
    4076             :     // Raw data
    4077             :     const char* aData[][2] = {
    4078             :         { "Name", "Value" },
    4079             :         { "A", "1" },
    4080             :         { "B", "2" },
    4081             :         { "C", "3" },
    4082             :         { "D", "4" },
    4083           1 :     };
    4084             : 
    4085             :     // Dimension definition
    4086             :     DPFieldDef aFields[] = {
    4087             :         { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
    4088             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    4089           1 :     };
    4090             : 
    4091           1 :     ScAddress aPos(1,1,0);
    4092           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    4093           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    4094             : 
    4095             :     // Extend the range downward to include some trailing empty rows.
    4096           1 :     aDataRange.aEnd.IncRow(2);
    4097             : 
    4098             :     ScDPObject* pDPObj = createDPFromRange(
    4099           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    4100             : 
    4101           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    4102           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    4103             : 
    4104           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    4105           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    4106           1 :                            pDPs->GetCount() == 1);
    4107           1 :     pDPObj->SetName(pDPs->CreateNewName());
    4108             : 
    4109           1 :     ScRange aOutRange = refresh(pDPObj);
    4110             : 
    4111             :     {
    4112             :         // Expected output table content.  0 = empty cell
    4113             :         const char* aOutputCheck[][2] = {
    4114             :             { "Name", 0 },
    4115             :             { "A", "1" },
    4116             :             { "B", "2" },
    4117             :             { "C", "3" },
    4118             :             { "D", "4" },
    4119             :             { "(empty)", 0 },
    4120             :             { "Total Result", "10" },
    4121           1 :         };
    4122             : 
    4123           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Include empty rows");
    4124           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4125             :     }
    4126             : 
    4127             :     // This time, ignore empty rows.
    4128           1 :     ScDPSaveData* pSaveData = pDPObj->GetSaveData();
    4129           1 :     CPPUNIT_ASSERT_MESSAGE("Save data doesn't exist.", pSaveData);
    4130           1 :     pSaveData->SetIgnoreEmptyRows(true);
    4131           1 :     pDPObj->ClearTableData();
    4132           1 :     aOutRange = refresh(pDPObj);
    4133             : 
    4134             :     {
    4135             :         // Expected output table content.  0 = empty cell
    4136             :         const char* aOutputCheck[][2] = {
    4137             :             { "Name", 0 },
    4138             :             { "A", "1" },
    4139             :             { "B", "2" },
    4140             :             { "C", "3" },
    4141             :             { "D", "4" },
    4142             :             { "Total Result", "10" },
    4143           1 :         };
    4144             : 
    4145           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Ignore empty rows");
    4146           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4147             :     }
    4148             : 
    4149             :     // Modify the source to remove member 'A', then refresh the table.
    4150           1 :     m_pDoc->SetString(1, 2, 0, "B");
    4151             : 
    4152           1 :     std::set<ScDPObject*> aRefs;
    4153           1 :     sal_uLong nErr = pDPs->ReloadCache(pDPObj, aRefs);
    4154           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to reload cache.", !nErr);
    4155           2 :     CPPUNIT_ASSERT_MESSAGE("There should only be one pivot table linked to this cache.",
    4156           1 :                            aRefs.size() == 1 && *aRefs.begin() == pDPObj);
    4157             : 
    4158           1 :     pDPObj->ClearTableData();
    4159           1 :     aOutRange = refresh(pDPObj);
    4160             : 
    4161             :     {
    4162             :         // Expected output table content.  0 = empty cell
    4163             :         const char* aOutputCheck[][2] = {
    4164             :             { "Name", 0 },
    4165             :             { "B", "3" },
    4166             :             { "C", "3" },
    4167             :             { "D", "4" },
    4168             :             { "Total Result", "10" },
    4169           1 :         };
    4170             : 
    4171           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Ignore empty rows");
    4172           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4173             :     }
    4174             : 
    4175           1 :     pDPs->FreeTable(pDPObj);
    4176           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    4177           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    4178           1 :                            pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    4179             : 
    4180           1 :     m_pDoc->DeleteTab(1);
    4181           1 :     m_pDoc->DeleteTab(0);
    4182           1 : }
    4183             : 
    4184           1 : void Test::testPivotTableTextNumber()
    4185             : {
    4186           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    4187           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    4188             : 
    4189             :     // Raw data
    4190             :     const char* aData[][2] = {
    4191             :         { "Name", "Value" },
    4192             :         { "0001", "1" },
    4193             :         { "0002", "2" },
    4194             :         { "0003", "3" },
    4195             :         { "0004", "4" },
    4196           1 :     };
    4197             : 
    4198             :     // Dimension definition
    4199             :     DPFieldDef aFields[] = {
    4200             :         { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
    4201             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    4202           1 :     };
    4203             : 
    4204             :     // Insert raw data such that the first column values are entered as text.
    4205           1 :     size_t nRowCount = SAL_N_ELEMENTS(aData);
    4206           6 :     for (size_t nRow = 0; nRow < nRowCount; ++nRow)
    4207             :     {
    4208           5 :         ScSetStringParam aParam;
    4209           5 :         aParam.mbDetectNumberFormat = false;
    4210           5 :         aParam.meSetTextNumFormat = ScSetStringParam::Always;
    4211           5 :         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aData[nRow][0]), &aParam);
    4212           5 :         aParam.meSetTextNumFormat = ScSetStringParam::Never;
    4213           5 :         m_pDoc->SetString(1, nRow, 0, OUString::createFromAscii(aData[nRow][1]), &aParam);
    4214             : 
    4215           5 :         if (nRow == 0)
    4216             :             // Don't check the header row.
    4217           1 :             continue;
    4218             : 
    4219             :         // Check the data rows.
    4220           4 :         CPPUNIT_ASSERT_MESSAGE("This cell is supposed to be text.", m_pDoc->HasStringData(0, nRow, 0));
    4221           4 :         CPPUNIT_ASSERT_MESSAGE("This cell is supposed to be numeric.", m_pDoc->HasValueData(1, nRow, 0));
    4222             :     }
    4223             : 
    4224           1 :     ScRange aDataRange(0, 0, 0, 1, 4, 0);
    4225             : 
    4226             :     ScDPObject* pDPObj = createDPFromRange(
    4227           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    4228             : 
    4229           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    4230           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    4231             : 
    4232           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    4233           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    4234           1 :                            pDPs->GetCount() == 1);
    4235           1 :     pDPObj->SetName(pDPs->CreateNewName());
    4236             : 
    4237           1 :     ScRange aOutRange = refresh(pDPObj);
    4238             : 
    4239             :     {
    4240             :         // Expected output table content.  0 = empty cell
    4241             :         const char* aOutputCheck[][2] = {
    4242             :             { "Name", 0 },
    4243             :             { "0001", "1" },
    4244             :             { "0002", "2" },
    4245             :             { "0003", "3" },
    4246             :             { "0004", "4" },
    4247             :             { "Total Result", "10" },
    4248           1 :         };
    4249             : 
    4250           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Text number field members");
    4251           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4252             :     }
    4253             : 
    4254           1 :     pDPs->FreeTable(pDPObj);
    4255           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    4256           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    4257           1 :                                  pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    4258             : 
    4259           1 :     m_pDoc->DeleteTab(1);
    4260           1 :     m_pDoc->DeleteTab(0);
    4261           1 : }
    4262             : 
    4263           1 : void Test::testPivotTableCaseInsensitiveStrings()
    4264             : {
    4265           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    4266           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    4267             : 
    4268             :     // Raw data
    4269             :     const char* aData[][2] = {
    4270             :         { "Name", "Value" },
    4271             :         { "A", "1" },
    4272             :         { "a", "2" },
    4273           1 :     };
    4274             : 
    4275             :     // Dimension definition
    4276             :     DPFieldDef aFields[] = {
    4277             :         { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
    4278             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    4279           1 :     };
    4280             : 
    4281           1 :     ScAddress aPos(1,1,0);
    4282           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    4283           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    4284             : 
    4285             :     ScDPObject* pDPObj = createDPFromRange(
    4286           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    4287             : 
    4288           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    4289           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    4290             : 
    4291           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    4292           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    4293           1 :                            pDPs->GetCount() == 1);
    4294           1 :     pDPObj->SetName(pDPs->CreateNewName());
    4295             : 
    4296           1 :     ScRange aOutRange = refresh(pDPObj);
    4297             : 
    4298             :     {
    4299             :         // Expected output table content.  0 = empty cell
    4300             :         const char* aOutputCheck[][2] = {
    4301             :             { "Name", 0 },
    4302             :             { "A", "3" },
    4303             :             { "Total Result", "3" },
    4304           1 :         };
    4305             : 
    4306           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Case insensitive strings");
    4307           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4308             :     }
    4309             : 
    4310           1 :     pDPs->FreeTable(pDPObj);
    4311           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    4312           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    4313           1 :                            pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    4314             : 
    4315           1 :     m_pDoc->DeleteTab(1);
    4316           1 :     m_pDoc->DeleteTab(0);
    4317           1 : }
    4318             : 
    4319           1 : void Test::testPivotTableNumStability()
    4320             : {
    4321           1 :     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
    4322             : 
    4323             :     // Raw Data
    4324             :     const char* aData[][4] = {
    4325             :         { "Name",   "Time Start", "Time End", "Total"          },
    4326             :         { "Sam",    "07:48 AM",   "09:00 AM", "=RC[-1]-RC[-2]" },
    4327             :         { "Sam",    "09:00 AM",   "10:30 AM", "=RC[-1]-RC[-2]" },
    4328             :         { "Sam",    "10:30 AM",   "12:30 PM", "=RC[-1]-RC[-2]" },
    4329             :         { "Sam",    "12:30 PM",   "01:00 PM", "=RC[-1]-RC[-2]" },
    4330             :         { "Sam",    "01:00 PM",   "01:30 PM", "=RC[-1]-RC[-2]" },
    4331             :         { "Sam",    "01:30 PM",   "02:00 PM", "=RC[-1]-RC[-2]" },
    4332             :         { "Sam",    "02:00 PM",   "07:15 PM", "=RC[-1]-RC[-2]" },
    4333             :         { "Sam",    "07:47 AM",   "09:00 AM", "=RC[-1]-RC[-2]" },
    4334             :         { "Sam",    "09:00 AM",   "10:00 AM", "=RC[-1]-RC[-2]" },
    4335             :         { "Sam",    "10:00 AM",   "11:00 AM", "=RC[-1]-RC[-2]" },
    4336             :         { "Sam",    "11:00 AM",   "11:30 AM", "=RC[-1]-RC[-2]" },
    4337             :         { "Sam",    "11:30 AM",   "12:45 PM", "=RC[-1]-RC[-2]" },
    4338             :         { "Sam",    "12:45 PM",   "01:15 PM", "=RC[-1]-RC[-2]" },
    4339             :         { "Sam",    "01:15 PM",   "02:30 PM", "=RC[-1]-RC[-2]" },
    4340             :         { "Sam",    "02:30 PM",   "02:45 PM", "=RC[-1]-RC[-2]" },
    4341             :         { "Sam",    "02:45 PM",   "04:30 PM", "=RC[-1]-RC[-2]" },
    4342             :         { "Sam",    "04:30 PM",   "06:00 PM", "=RC[-1]-RC[-2]" },
    4343             :         { "Sam",    "06:00 PM",   "07:15 PM", "=RC[-1]-RC[-2]" },
    4344             :         { "Mike",   "06:15 AM",   "08:30 AM", "=RC[-1]-RC[-2]" },
    4345             :         { "Mike",   "08:30 AM",   "10:03 AM", "=RC[-1]-RC[-2]" },
    4346             :         { "Mike",   "10:03 AM",   "12:00 PM", "=RC[-1]-RC[-2]" },
    4347             :         { "Dennis", "11:00 AM",   "01:00 PM", "=RC[-1]-RC[-2]" },
    4348             :         { "Dennis", "01:00 PM",   "02:00 PM", "=RC[-1]-RC[-2]" }
    4349           1 :     };
    4350             : 
    4351             :     // Dimension definition
    4352             :     DPFieldDef aFields[] = {
    4353             :         { "Name",  sheet::DataPilotFieldOrientation_ROW, 0 },
    4354             :         { "Total", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    4355           1 :     };
    4356             : 
    4357           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    4358           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    4359             : 
    4360           1 :     size_t nRowCount = SAL_N_ELEMENTS(aData);
    4361           1 :     ScAddress aPos(1,1,0);
    4362           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, nRowCount);
    4363             : 
    4364             :     // Insert formulas to manually calculate sums for each name.
    4365           1 :     m_pDoc->SetString(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+1, aDataRange.aStart.Tab(), "=SUMIF(R[-23]C:R[-1]C;\"Dennis\";R[-23]C[3]:R[-1]C[3])");
    4366           1 :     m_pDoc->SetString(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+2, aDataRange.aStart.Tab(), "=SUMIF(R[-24]C:R[-2]C;\"Mike\";R[-24]C[3]:R[-2]C[3])");
    4367           1 :     m_pDoc->SetString(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+3, aDataRange.aStart.Tab(), "=SUMIF(R[-25]C:R[-3]C;\"Sam\";R[-25]C[3]:R[-3]C[3])");
    4368             : 
    4369           1 :     m_pDoc->CalcAll();
    4370             : 
    4371             :     // Get correct sum values.
    4372           1 :     double fDennisTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+1, aDataRange.aStart.Tab());
    4373           1 :     double fMikeTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+2, aDataRange.aStart.Tab());
    4374           1 :     double fSamTotal = m_pDoc->GetValue(aDataRange.aStart.Col(), aDataRange.aEnd.Row()+3, aDataRange.aStart.Tab());
    4375             : 
    4376             :     ScDPObject* pDPObj = createDPFromRange(
    4377           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    4378             : 
    4379           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    4380           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    4381             : 
    4382           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    4383           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("there should be only one data pilot table.",
    4384           1 :                            pDPs->GetCount(), static_cast<size_t>(1));
    4385           1 :     pDPObj->SetName(pDPs->CreateNewName());
    4386             : 
    4387           1 :     ScRange aOutRange = refresh(pDPObj);
    4388             : 
    4389             :     // Manually check the total value for each name.
    4390             :     //
    4391             :     // +--------------+----------------+
    4392             :     // | Name         |                |
    4393             :     // +--------------+----------------+
    4394             :     // | Dennis       | <Dennis total> |
    4395             :     // +--------------+----------------+
    4396             :     // | Mike         | <Miks total>   |
    4397             :     // +--------------+----------------+
    4398             :     // | Sam          | <Sam total>    |
    4399             :     // +--------------+----------------+
    4400             :     // | Total Result | ...            |
    4401             :     // +--------------+----------------+
    4402             : 
    4403           1 :     aPos = aOutRange.aStart;
    4404           1 :     aPos.IncCol();
    4405           1 :     aPos.IncRow();
    4406           1 :     double fTest = m_pDoc->GetValue(aPos);
    4407           1 :     CPPUNIT_ASSERT_MESSAGE("Incorrect value for Dennis.", rtl::math::approxEqual(fTest, fDennisTotal));
    4408           1 :     aPos.IncRow();
    4409           1 :     fTest = m_pDoc->GetValue(aPos);
    4410           1 :     CPPUNIT_ASSERT_MESSAGE("Incorrect value for Mike.", rtl::math::approxEqual(fTest, fMikeTotal));
    4411           1 :     aPos.IncRow();
    4412           1 :     fTest = m_pDoc->GetValue(aPos);
    4413           1 :     CPPUNIT_ASSERT_MESSAGE("Incorrect value for Sam.", rtl::math::approxEqual(fTest, fSamTotal));
    4414             : 
    4415           1 :     pDPs->FreeTable(pDPObj);
    4416           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    4417           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    4418           1 :                            pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    4419             : 
    4420           1 :     m_pDoc->DeleteTab(1);
    4421           1 :     m_pDoc->DeleteTab(0);
    4422           1 : }
    4423             : 
    4424           1 : void Test::testPivotTableFieldReference()
    4425             : {
    4426           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    4427           1 :     m_pDoc->InsertTab(1, OUString("Table"));
    4428             : 
    4429             :     // Raw data
    4430             :     const char* aData[][2] = {
    4431             :         { "Name", "Value" },
    4432             :         { "A", "1" },
    4433             :         { "B", "2" },
    4434             :         { "C", "4" },
    4435             :         { "D", "8" },
    4436           1 :     };
    4437             : 
    4438             :     // Dimension definition
    4439             :     DPFieldDef aFields[] = {
    4440             :         { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
    4441             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    4442           1 :     };
    4443             : 
    4444           1 :     ScAddress aPos(1,1,0);
    4445           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    4446           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    4447             : 
    4448             :     ScDPObject* pDPObj = createDPFromRange(
    4449           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    4450             : 
    4451           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    4452           1 :     bool bSuccess = pDPs->InsertNewTable(pDPObj);
    4453             : 
    4454           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert a new pivot table object into document.", bSuccess);
    4455           2 :     CPPUNIT_ASSERT_MESSAGE("there should be only one data pilot table.",
    4456           1 :                            pDPs->GetCount() == 1);
    4457           1 :     pDPObj->SetName(pDPs->CreateNewName());
    4458             : 
    4459           1 :     ScRange aOutRange = refresh(pDPObj);
    4460             : 
    4461             :     {
    4462             :         // Expected output table content.  0 = empty cell
    4463             :         const char* aOutputCheck[][2] = {
    4464             :             { "Name", 0 },
    4465             :             { "A", "1" },
    4466             :             { "B", "2" },
    4467             :             { "C", "4" },
    4468             :             { "D", "8" },
    4469             :             { "Total Result", "15" },
    4470           1 :         };
    4471             : 
    4472           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (none)");
    4473           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4474             :     }
    4475             : 
    4476           1 :     ScDPSaveData aSaveData = *pDPObj->GetSaveData();
    4477           2 :     sheet::DataPilotFieldReference aFieldRef;
    4478           1 :     aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE;
    4479           1 :     aFieldRef.ReferenceField = "Name";
    4480           1 :     aFieldRef.ReferenceItemType = sheet::DataPilotFieldReferenceItemType::NAMED;
    4481           1 :     aFieldRef.ReferenceItemName = "A";
    4482           1 :     ScDPSaveDimension* pDim = aSaveData.GetDimensionByName("Value");
    4483           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to retrieve dimension 'Value'.", pDim);
    4484           1 :     pDim->SetReferenceValue(&aFieldRef);
    4485           1 :     pDPObj->SetSaveData(aSaveData);
    4486             : 
    4487           1 :     aOutRange = refresh(pDPObj);
    4488             :     {
    4489             :         // Expected output table content.  0 = empty cell
    4490             :         const char* aOutputCheck[][2] = {
    4491             :             { "Name", 0 },
    4492             :             { "A", 0 },
    4493             :             { "B", "1" },
    4494             :             { "C", "3" },
    4495             :             { "D", "7" },
    4496             :             { "Total Result", 0 },
    4497           1 :         };
    4498             : 
    4499           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (difference from)");
    4500           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4501             :     }
    4502             : 
    4503           1 :     aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE;
    4504           1 :     pDim->SetReferenceValue(&aFieldRef);
    4505           1 :     pDPObj->SetSaveData(aSaveData);
    4506             : 
    4507           1 :     aOutRange = refresh(pDPObj);
    4508             :     {
    4509             :         // Expected output table content.  0 = empty cell
    4510             :         const char* aOutputCheck[][2] = {
    4511             :             { "Name", 0 },
    4512             :             { "A", "100.00%" },
    4513             :             { "B", "200.00%" },
    4514             :             { "C", "400.00%" },
    4515             :             { "D", "800.00%" },
    4516             :             { "Total Result", 0 },
    4517           1 :         };
    4518             : 
    4519           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% of)");
    4520           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4521             :     }
    4522             : 
    4523           1 :     aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE;
    4524           1 :     pDim->SetReferenceValue(&aFieldRef);
    4525           1 :     pDPObj->SetSaveData(aSaveData);
    4526             : 
    4527           1 :     aOutRange = refresh(pDPObj);
    4528             :     {
    4529             :         // Expected output table content.  0 = empty cell
    4530             :         const char* aOutputCheck[][2] = {
    4531             :             { "Name", 0 },
    4532             :             { "A", 0 },
    4533             :             { "B", "100.00%" },
    4534             :             { "C", "300.00%" },
    4535             :             { "D", "700.00%" },
    4536             :             { "Total Result", 0 },
    4537           1 :         };
    4538             : 
    4539           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% difference from)");
    4540           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4541             :     }
    4542             : 
    4543           1 :     aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::RUNNING_TOTAL;
    4544           1 :     pDim->SetReferenceValue(&aFieldRef);
    4545           1 :     pDPObj->SetSaveData(aSaveData);
    4546             : 
    4547           1 :     aOutRange = refresh(pDPObj);
    4548             :     {
    4549             :         // Expected output table content.  0 = empty cell
    4550             :         const char* aOutputCheck[][2] = {
    4551             :             { "Name", 0 },
    4552             :             { "A", "1" },
    4553             :             { "B", "3" },
    4554             :             { "C", "7" },
    4555             :             { "D", "15" },
    4556             :             { "Total Result", 0 },
    4557           1 :         };
    4558             : 
    4559           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (Running total)");
    4560           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4561             :     }
    4562             : 
    4563           1 :     aFieldRef.ReferenceType = sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE;
    4564           1 :     pDim->SetReferenceValue(&aFieldRef);
    4565           1 :     pDPObj->SetSaveData(aSaveData);
    4566             : 
    4567           1 :     aOutRange = refresh(pDPObj);
    4568             :     {
    4569             :         // Expected output table content.  0 = empty cell
    4570             :         const char* aOutputCheck[][2] = {
    4571             :             { "Name", 0 },
    4572             :             { "A", "6.67%" },
    4573             :             { "B", "13.33%" },
    4574             :             { "C", "26.67%" },
    4575             :             { "D", "53.33%" },
    4576             :             { "Total Result", "100.00%" },
    4577           1 :         };
    4578             : 
    4579           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Field reference (% of column)");
    4580           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4581             :     }
    4582             : 
    4583           1 :     pDPs->FreeTable(pDPObj);
    4584           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be no more tables.", pDPs->GetCount(), static_cast<size_t>(0));
    4585           2 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There shouldn't be any more cache stored.",
    4586           1 :                            pDPs->GetSheetCaches().size(), static_cast<size_t>(0));
    4587             : 
    4588           1 :     m_pDoc->DeleteTab(1);
    4589           2 :     m_pDoc->DeleteTab(0);
    4590           1 : }
    4591             : 
    4592           1 : void Test::testPivotTableDocFunc()
    4593             : {
    4594           1 :     m_pDoc->InsertTab(0, "Data");
    4595           1 :     m_pDoc->InsertTab(1, "Table");
    4596             : 
    4597             :     // Raw data
    4598             :     const char* aData[][2] = {
    4599             :         { "Name",      "Value" },
    4600             :         { "Sun",       "1" },
    4601             :         { "Oracle",    "2" },
    4602             :         { "Red Hat",   "4" },
    4603             :         { "SUSE",      "8" },
    4604             :         { "Apple",     "16" },
    4605             :         { "Microsoft", "32" },
    4606           1 :     };
    4607             : 
    4608             :     // Dimension definition
    4609             :     DPFieldDef aFields[] = {
    4610             :         { "Name", sheet::DataPilotFieldOrientation_ROW, 0 },
    4611             :         { "Value", sheet::DataPilotFieldOrientation_DATA, sheet::GeneralFunction_SUM },
    4612           1 :     };
    4613             : 
    4614           1 :     ScAddress aPos(1,1,0);
    4615           1 :     ScRange aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    4616           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    4617             : 
    4618             :     ScDPObject* pDPObj = createDPFromRange(
    4619           1 :         m_pDoc, aDataRange, aFields, SAL_N_ELEMENTS(aFields), false);
    4620             : 
    4621           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to create pivot table object.", pDPObj);
    4622             : 
    4623             :     // Craete a new pivot table output.
    4624           1 :     ScDBDocFunc aFunc(*m_xDocShRef);
    4625           1 :     bool bSuccess = aFunc.CreatePivotTable(*pDPObj, false, true);
    4626           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to create pivot table output via ScDBDocFunc.", bSuccess);
    4627           1 :     ScDPCollection* pDPs = m_pDoc->GetDPCollection();
    4628           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to get pivot table collection.", pDPs);
    4629           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pDPs->GetCount());
    4630           1 :     pDPObj = (*pDPs)[0];
    4631           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to retrieve pivot table object from the collection", pDPObj);
    4632           1 :     ScRange aOutRange = pDPObj->GetOutRange();
    4633             :     {
    4634             :         // Expected output table content.  0 = empty cell
    4635             :         const char* aOutputCheck[][2] = {
    4636             :             { "Name", 0 },
    4637             :             { "Apple", "16" },
    4638             :             { "Microsoft", "32" },
    4639             :             { "Oracle", "2" },
    4640             :             { "Red Hat", "4" },
    4641             :             { "Sun", "1" },
    4642             :             { "SUSE", "8" },
    4643             :             { "Total Result", "63" },
    4644           1 :         };
    4645             : 
    4646           1 :         bSuccess = checkDPTableOutput<2>(m_pDoc, aOutRange, aOutputCheck, "Pivot table created via ScDBDocFunc");
    4647           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4648             :     }
    4649             : 
    4650             :     // Remove this pivot table output. This should also clear the pivot cache
    4651             :     // it was referencing.
    4652           1 :     bSuccess = aFunc.RemovePivotTable(*pDPObj, false, true);
    4653           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to remove pivot table output via ScDBDocFunc.", bSuccess);
    4654           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pDPs->GetCount());
    4655           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), pDPs->GetSheetCaches().size());
    4656             : 
    4657           1 :     m_pDoc->DeleteTab(1);
    4658           1 :     m_pDoc->DeleteTab(0);
    4659           1 : }
    4660             : 
    4661           1 : void Test::testSheetCopy()
    4662             : {
    4663           1 :     m_pDoc->InsertTab(0, "TestTab");
    4664           1 :     m_pDoc->SetString(ScAddress(0,0,0), "copy me");
    4665           1 :     CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
    4666             :     SCROW nRow1, nRow2;
    4667           1 :     bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    4668           1 :     CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
    4669             : 
    4670             :     // Copy and test the result.
    4671           1 :     m_pDoc->CopyTab(0, 1);
    4672           1 :     CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
    4673           1 :     bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
    4674           1 :     CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
    4675           1 :     m_pDoc->DeleteTab(1);
    4676             : 
    4677           1 :     m_pDoc->SetRowHidden(5, 10, 0, true);
    4678           1 :     bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    4679           1 :     CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
    4680           1 :     bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
    4681           1 :     CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
    4682           1 :     bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
    4683           1 :     CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
    4684             : 
    4685             :     // Copy the sheet once again.
    4686           1 :     m_pDoc->CopyTab(0, 1);
    4687           1 :     CPPUNIT_ASSERT_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount() == 2);
    4688           1 :     bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
    4689           1 :     CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
    4690           1 :     bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
    4691           1 :     CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
    4692           1 :     bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
    4693           1 :     CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
    4694           1 :     m_pDoc->DeleteTab(1);
    4695           1 :     m_pDoc->DeleteTab(0);
    4696           1 : }
    4697             : 
    4698           1 : void Test::testSheetMove()
    4699             : {
    4700           1 :     OUString aTabName("TestTab1");
    4701           1 :     m_pDoc->InsertTab(0, aTabName);
    4702           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount(), static_cast<SCTAB>(1));
    4703             :     SCROW nRow1, nRow2;
    4704           1 :     bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    4705           1 :     CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
    4706             : 
    4707             :     //test if inserting before another sheet works
    4708           1 :     m_pDoc->InsertTab(0, OUString("TestTab2"));
    4709           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("document should have two sheets", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
    4710           1 :     bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    4711           1 :     CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
    4712             : 
    4713             :     // Move and test the result.
    4714           1 :     m_pDoc->MoveTab(0, 1);
    4715           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
    4716           1 :     bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
    4717           1 :     CPPUNIT_ASSERT_MESSAGE("copied sheet should also have all rows visible as the original.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
    4718           2 :     OUString aName;
    4719           1 :     m_pDoc->GetName(0, aName);
    4720           1 :     CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab1" );
    4721             : 
    4722           1 :     m_pDoc->SetRowHidden(5, 10, 0, true);
    4723           1 :     bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    4724           1 :     CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
    4725           1 :     bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
    4726           1 :     CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
    4727           1 :     bHidden = m_pDoc->RowHidden(11, 0, &nRow1, &nRow2);
    4728           1 :     CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
    4729             : 
    4730             :     // Move the sheet once again.
    4731           1 :     m_pDoc->MoveTab(1, 0);
    4732           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("document now should have two sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(2));
    4733           1 :     bHidden = m_pDoc->RowHidden(0, 1, &nRow1, &nRow2);
    4734           1 :     CPPUNIT_ASSERT_MESSAGE("rows 0 - 4 should be visible", !bHidden && nRow1 == 0 && nRow2 == 4);
    4735           1 :     bHidden = m_pDoc->RowHidden(5, 1, &nRow1, &nRow2);
    4736           1 :     CPPUNIT_ASSERT_MESSAGE("rows 5 - 10 should be hidden", bHidden && nRow1 == 5 && nRow2 == 10);
    4737           1 :     bHidden = m_pDoc->RowHidden(11, 1, &nRow1, &nRow2);
    4738           1 :     CPPUNIT_ASSERT_MESSAGE("rows 11 - maxrow should be visible", !bHidden && nRow1 == 11 && nRow2 == MAXROW);
    4739           1 :     m_pDoc->GetName(0, aName);
    4740           1 :     CPPUNIT_ASSERT_MESSAGE( "sheets should have changed places", aName == "TestTab2" );
    4741           1 :     m_pDoc->DeleteTab(1);
    4742           2 :     m_pDoc->DeleteTab(0);
    4743           1 : }
    4744             : 
    4745           5 : ScDocShell* findLoadedDocShellByName(const OUString& rName)
    4746             : {
    4747           5 :     TypeId aType(TYPE(ScDocShell));
    4748           5 :     ScDocShell* pShell = static_cast<ScDocShell*>(SfxObjectShell::GetFirst(&aType, false));
    4749         263 :     while (pShell)
    4750             :     {
    4751         256 :         SfxMedium* pMedium = pShell->GetMedium();
    4752         256 :         if (pMedium)
    4753             :         {
    4754         256 :             OUString aName = pMedium->GetName();
    4755         256 :             if (aName.equals(rName))
    4756           3 :                 return pShell;
    4757             :         }
    4758         253 :         pShell = static_cast<ScDocShell*>(SfxObjectShell::GetNext(*pShell, &aType, false));
    4759             :     }
    4760           2 :     return NULL;
    4761             : }
    4762             : 
    4763           2 : ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
    4764             : {
    4765           2 :     ScRange aRange;
    4766             : 
    4767           2 :     vector<SCROW> aRows;
    4768           2 :     pCacheTab->getAllRows(aRows);
    4769           2 :     vector<SCROW>::const_iterator itrRow = aRows.begin(), itrRowEnd = aRows.end();
    4770           2 :     bool bFirst = true;
    4771          11 :     for (; itrRow != itrRowEnd; ++itrRow)
    4772             :     {
    4773           9 :         SCROW nRow = *itrRow;
    4774           9 :         vector<SCCOL> aCols;
    4775           9 :         pCacheTab->getAllCols(nRow, aCols);
    4776           9 :         vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end();
    4777          27 :         for (; itrCol != itrColEnd; ++itrCol)
    4778             :         {
    4779          18 :             SCCOL nCol = *itrCol;
    4780          18 :             if (bFirst)
    4781             :             {
    4782           2 :                 aRange.aStart = ScAddress(nCol, nRow, 0);
    4783           2 :                 aRange.aEnd = aRange.aStart;
    4784           2 :                 bFirst = false;
    4785             :             }
    4786             :             else
    4787             :             {
    4788          16 :                 if (nCol < aRange.aStart.Col())
    4789           0 :                     aRange.aStart.SetCol(nCol);
    4790          16 :                 else if (aRange.aEnd.Col() < nCol)
    4791           2 :                     aRange.aEnd.SetCol(nCol);
    4792             : 
    4793          16 :                 if (nRow < aRange.aStart.Row())
    4794           0 :                     aRange.aStart.SetRow(nRow);
    4795          16 :                 else if (aRange.aEnd.Row() < nRow)
    4796           7 :                     aRange.aEnd.SetRow(nRow);
    4797             :             }
    4798             :         }
    4799           9 :     }
    4800           2 :     return aRange;
    4801             : }
    4802             : 
    4803           1 : void Test::testExternalRef()
    4804             : {
    4805           1 :     ScDocShellRef xExtDocSh = new ScDocShell;
    4806           2 :     OUString aExtDocName("file:///extdata.fake");
    4807           2 :     OUString aExtSh1Name("Data1");
    4808           2 :     OUString aExtSh2Name("Data2");
    4809           2 :     OUString aExtSh3Name("Data3");
    4810           1 :     SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
    4811           1 :     xExtDocSh->DoInitNew(pMed);
    4812           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
    4813           1 :                            findLoadedDocShellByName(aExtDocName) != NULL);
    4814             : 
    4815             :     // Populate the external source document.
    4816           1 :     ScDocument* pExtDoc = xExtDocSh->GetDocument();
    4817           1 :     pExtDoc->InsertTab(0, aExtSh1Name);
    4818           1 :     pExtDoc->InsertTab(1, aExtSh2Name);
    4819           1 :     pExtDoc->InsertTab(2, aExtSh3Name);
    4820             : 
    4821           2 :     OUString name("Name");
    4822           2 :     OUString value("Value");
    4823           2 :     OUString andy("Andy");
    4824           2 :     OUString bruce("Bruce");
    4825           2 :     OUString charlie("Charlie");
    4826           2 :     OUString david("David");
    4827           2 :     OUString edward("Edward");
    4828           2 :     OUString frank("Frank");
    4829           2 :     OUString george("George");
    4830           2 :     OUString henry("Henry");
    4831             : 
    4832             :     // Sheet 1
    4833           1 :     pExtDoc->SetString(0, 0, 0, name);
    4834           1 :     pExtDoc->SetString(0, 1, 0, andy);
    4835           1 :     pExtDoc->SetString(0, 2, 0, bruce);
    4836           1 :     pExtDoc->SetString(0, 3, 0, charlie);
    4837           1 :     pExtDoc->SetString(0, 4, 0, david);
    4838           1 :     pExtDoc->SetString(1, 0, 0, value);
    4839           1 :     double val = 10;
    4840           1 :     pExtDoc->SetValue(1, 1, 0, val);
    4841           1 :     val = 11;
    4842           1 :     pExtDoc->SetValue(1, 2, 0, val);
    4843           1 :     val = 12;
    4844           1 :     pExtDoc->SetValue(1, 3, 0, val);
    4845           1 :     val = 13;
    4846           1 :     pExtDoc->SetValue(1, 4, 0, val);
    4847             : 
    4848             :     // Sheet 2 remains empty.
    4849             : 
    4850             :     // Sheet 3
    4851           1 :     pExtDoc->SetString(0, 0, 2, name);
    4852           1 :     pExtDoc->SetString(0, 1, 2, edward);
    4853           1 :     pExtDoc->SetString(0, 2, 2, frank);
    4854           1 :     pExtDoc->SetString(0, 3, 2, george);
    4855           1 :     pExtDoc->SetString(0, 4, 2, henry);
    4856           1 :     pExtDoc->SetString(1, 0, 2, value);
    4857           1 :     val = 99;
    4858           1 :     pExtDoc->SetValue(1, 1, 2, val);
    4859           1 :     val = 98;
    4860           1 :     pExtDoc->SetValue(1, 2, 2, val);
    4861           1 :     val = 97;
    4862           1 :     pExtDoc->SetValue(1, 3, 2, val);
    4863           1 :     val = 96;
    4864           1 :     pExtDoc->SetValue(1, 4, 2, val);
    4865             : 
    4866             :     // Test external refernces on the main document while the external
    4867             :     // document is still in memory.
    4868           1 :     m_pDoc->InsertTab(0, OUString("Test Sheet"));
    4869           1 :     m_pDoc->SetString(0, 0, 0, OUString("='file:///extdata.fake'#Data1.A1"));
    4870           2 :     OUString test = m_pDoc->GetString(0, 0, 0);
    4871           1 :     CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(name));
    4872             : 
    4873             :     // After the initial access to the external document, the external ref
    4874             :     // manager should create sheet cache entries for *all* sheets from that
    4875             :     // document.  Note that the doc may have more than 3 sheets but ensure
    4876             :     // that the first 3 are what we expect.
    4877           1 :     ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
    4878           1 :     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
    4879           2 :     vector<OUString> aTabNames;
    4880           1 :     pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
    4881           1 :     CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
    4882           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[0].equals(aExtSh1Name));
    4883           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[1].equals(aExtSh2Name));
    4884           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[2].equals(aExtSh3Name));
    4885             : 
    4886           1 :     m_pDoc->SetString(1, 0, 0, OUString("='file:///extdata.fake'#Data1.B1"));
    4887           1 :     test = m_pDoc->GetString(1, 0, 0);
    4888           1 :     CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(value));
    4889             : 
    4890           1 :     m_pDoc->SetString(0, 1, 0, OUString("='file:///extdata.fake'#Data1.A2"));
    4891           1 :     m_pDoc->SetString(0, 2, 0, OUString("='file:///extdata.fake'#Data1.A3"));
    4892           1 :     m_pDoc->SetString(0, 3, 0, OUString("='file:///extdata.fake'#Data1.A4"));
    4893           1 :     m_pDoc->SetString(0, 4, 0, OUString("='file:///extdata.fake'#Data1.A5"));
    4894           1 :     m_pDoc->SetString(0, 5, 0, OUString("='file:///extdata.fake'#Data1.A6"));
    4895             : 
    4896             :     {
    4897             :         // Referencing an empty cell should display '0'.
    4898           1 :         const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
    4899           6 :         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
    4900             :         {
    4901           5 :             test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
    4902           5 :             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
    4903             :         }
    4904             :     }
    4905           1 :     m_pDoc->SetString(1, 1, 0, OUString("='file:///extdata.fake'#Data1.B2"));
    4906           1 :     m_pDoc->SetString(1, 2, 0, OUString("='file:///extdata.fake'#Data1.B3"));
    4907           1 :     m_pDoc->SetString(1, 3, 0, OUString("='file:///extdata.fake'#Data1.B4"));
    4908           1 :     m_pDoc->SetString(1, 4, 0, OUString("='file:///extdata.fake'#Data1.B5"));
    4909           1 :     m_pDoc->SetString(1, 5, 0, OUString("='file:///extdata.fake'#Data1.B6"));
    4910             :     {
    4911           1 :         double pChecks[] = { 10, 11, 12, 13, 0 };
    4912           6 :         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
    4913             :         {
    4914           5 :             m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
    4915           5 :             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", val == pChecks[i]);
    4916             :         }
    4917             :     }
    4918             : 
    4919           1 :     m_pDoc->SetString(2, 0, 0, OUString("='file:///extdata.fake'#Data3.A1"));
    4920           1 :     m_pDoc->SetString(2, 1, 0, OUString("='file:///extdata.fake'#Data3.A2"));
    4921           1 :     m_pDoc->SetString(2, 2, 0, OUString("='file:///extdata.fake'#Data3.A3"));
    4922           1 :     m_pDoc->SetString(2, 3, 0, OUString("='file:///extdata.fake'#Data3.A4"));
    4923             :     {
    4924           1 :         const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
    4925           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
    4926             :         {
    4927           4 :             test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
    4928           4 :             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
    4929             :         }
    4930             :     }
    4931             : 
    4932           1 :     m_pDoc->SetString(3, 0, 0, OUString("='file:///extdata.fake'#Data3.B1"));
    4933           1 :     m_pDoc->SetString(3, 1, 0, OUString("='file:///extdata.fake'#Data3.B2"));
    4934           1 :     m_pDoc->SetString(3, 2, 0, OUString("='file:///extdata.fake'#Data3.B3"));
    4935           1 :     m_pDoc->SetString(3, 3, 0, OUString("='file:///extdata.fake'#Data3.B4"));
    4936             :     {
    4937           1 :         const char* pChecks[] = { "Value", "99", "98", "97" };
    4938           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
    4939             :         {
    4940           4 :             test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
    4941           4 :             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
    4942             :         }
    4943             :     }
    4944             : 
    4945             :     // At this point, all accessed cell data from the external document should
    4946             :     // have been cached.
    4947             :     ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
    4948           2 :         nFileId, aExtSh1Name, false);
    4949           1 :     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab.get() != NULL);
    4950           1 :     ScRange aCachedRange = getCachedRange(pCacheTab);
    4951           2 :     CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
    4952             :                            aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
    4953           1 :                            aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 4);
    4954             : 
    4955             :     // Sheet2 is not referenced at all; the cache table shouldn't even exist.
    4956           1 :     pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
    4957           1 :     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", pCacheTab.get() == NULL);
    4958             : 
    4959             :     // Sheet3's row 5 is not referenced; it should not be cached.
    4960           1 :     pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
    4961           1 :     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab.get() != NULL);
    4962           1 :     aCachedRange = getCachedRange(pCacheTab);
    4963           2 :     CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
    4964             :                            aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
    4965           1 :                            aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 3);
    4966             : 
    4967             :     // Unload the external document shell.
    4968           1 :     xExtDocSh->DoClose();
    4969           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
    4970           1 :                            findLoadedDocShellByName(aExtDocName) == NULL);
    4971             : 
    4972           2 :     m_pDoc->DeleteTab(0);
    4973           1 : }
    4974             : 
    4975           1 : void testExtRefFuncT(ScDocument* pDoc, ScDocument* pExtDoc)
    4976             : {
    4977           1 :     clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
    4978           1 :     clearRange(pExtDoc, ScRange(0, 0, 0, 1, 9, 0));
    4979             : 
    4980           1 :     pExtDoc->SetString(0, 0, 0, OUString("'1.2"));
    4981           1 :     pExtDoc->SetString(0, 1, 0, OUString("Foo"));
    4982           1 :     pExtDoc->SetValue(0, 2, 0, 12.3);
    4983           1 :     pDoc->SetString(0, 0, 0, OUString("=T('file:///extdata.fake'#Data.A1)"));
    4984           1 :     pDoc->SetString(0, 1, 0, OUString("=T('file:///extdata.fake'#Data.A2)"));
    4985           1 :     pDoc->SetString(0, 2, 0, OUString("=T('file:///extdata.fake'#Data.A3)"));
    4986           1 :     pDoc->CalcAll();
    4987             : 
    4988           1 :     OUString aRes = pDoc->GetString(0, 0, 0);
    4989           1 :     CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "1.2" );
    4990           1 :     aRes = pDoc->GetString(0, 1, 0);
    4991           1 :     CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "Foo" );
    4992           1 :     aRes = pDoc->GetString(0, 2, 0);
    4993           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
    4994           1 : }
    4995             : 
    4996           1 : void Test::testExternalRefFunctions()
    4997             : {
    4998           1 :     ScDocShellRef xExtDocSh = new ScDocShell;
    4999           2 :     OUString aExtDocName("file:///extdata.fake");
    5000           1 :     SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
    5001           1 :     xExtDocSh->DoInitNew(pMed);
    5002           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
    5003           1 :                            findLoadedDocShellByName(aExtDocName) != NULL);
    5004             : 
    5005           1 :     ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
    5006           1 :     CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
    5007           1 :     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
    5008           1 :     const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
    5009           2 :     CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
    5010           1 :                            pFileName && pFileName->equals(aExtDocName));
    5011             : 
    5012             :     // Populate the external source document.
    5013           1 :     ScDocument* pExtDoc = xExtDocSh->GetDocument();
    5014           1 :     pExtDoc->InsertTab(0, OUString("Data"));
    5015           1 :     double val = 1;
    5016           1 :     pExtDoc->SetValue(0, 0, 0, val);
    5017             :     // leave cell B1 empty.
    5018           1 :     val = 2;
    5019           1 :     pExtDoc->SetValue(0, 1, 0, val);
    5020           1 :     pExtDoc->SetValue(1, 1, 0, val);
    5021           1 :     val = 3;
    5022           1 :     pExtDoc->SetValue(0, 2, 0, val);
    5023           1 :     pExtDoc->SetValue(1, 2, 0, val);
    5024           1 :     val = 4;
    5025           1 :     pExtDoc->SetValue(0, 3, 0, val);
    5026           1 :     pExtDoc->SetValue(1, 3, 0, val);
    5027             : 
    5028           1 :     m_pDoc->InsertTab(0, OUString("Test"));
    5029             : 
    5030             :     struct {
    5031             :         const char* pFormula; double fResult;
    5032             :     } aChecks[] = {
    5033             :         { "=SUM('file:///extdata.fake'#Data.A1:A4)",     10 },
    5034             :         { "=SUM('file:///extdata.fake'#Data.B1:B4)",     9 },
    5035             :         { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
    5036             :         { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
    5037             :         { "=COUNT('file:///extdata.fake'#Data.A1:A4)",   4 },
    5038             :         { "=COUNT('file:///extdata.fake'#Data.B1:B4)",   3 }
    5039           1 :     };
    5040             : 
    5041           7 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    5042             :     {
    5043           6 :         m_pDoc->SetString(0, 0, 0, OUString::createFromAscii(aChecks[i].pFormula));
    5044           6 :         m_pDoc->CalcAll();
    5045           6 :         m_pDoc->GetValue(0, 0, 0, val);
    5046           6 :         CPPUNIT_ASSERT_MESSAGE("unexpected result involving external ranges.", val == aChecks[i].fResult);
    5047             :     }
    5048             : 
    5049           1 :     pRefMgr->clearCache(nFileId);
    5050           1 :     testExtRefFuncT(m_pDoc, pExtDoc);
    5051             : 
    5052             :     // Unload the external document shell.
    5053           1 :     xExtDocSh->DoClose();
    5054           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
    5055           1 :                            findLoadedDocShellByName(aExtDocName) == NULL);
    5056             : 
    5057           2 :     m_pDoc->DeleteTab(0);
    5058           1 : }
    5059             : 
    5060           1 : void Test::testDataArea()
    5061             : {
    5062           1 :     m_pDoc->InsertTab(0, OUString("Data"));
    5063             : 
    5064             :     // Totally empty sheet should be rightfully considered empty in all accounts.
    5065           1 :     CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
    5066           1 :     CPPUNIT_ASSERT_MESSAGE("Sheet is expected to be empty.", m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
    5067             : 
    5068             :     // Now, set borders in some cells....
    5069           1 :     ::editeng::SvxBorderLine aLine(NULL, 50, table::BorderLineStyle::SOLID);
    5070           1 :     SvxBoxItem aBorderItem(ATTR_BORDER);
    5071           1 :     aBorderItem.SetLine(&aLine, BOX_LINE_LEFT);
    5072           1 :     aBorderItem.SetLine(&aLine, BOX_LINE_RIGHT);
    5073         101 :     for (SCROW i = 0; i < 100; ++i)
    5074             :         // Set borders from row 1 to 100.
    5075         100 :         m_pDoc->ApplyAttr(0, i, 0, aBorderItem);
    5076             : 
    5077             :     // Now the sheet is considered non-empty for printing purposes, but still
    5078             :     // be empty in all the other cases.
    5079           2 :     CPPUNIT_ASSERT_MESSAGE("Empty sheet with borders should be printable.",
    5080           1 :                            !m_pDoc->IsPrintEmpty(0, 0, 0, 100, 100));
    5081           2 :     CPPUNIT_ASSERT_MESSAGE("But it should still be considered empty in all the other cases.",
    5082           1 :                            m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
    5083             : 
    5084             :     // Adding a real cell content should turn the block non-empty.
    5085           1 :     m_pDoc->SetString(0, 0, 0, OUString("Some text"));
    5086           2 :     CPPUNIT_ASSERT_MESSAGE("Now the block should not be empty with a real cell content.",
    5087           1 :                            !m_pDoc->IsBlockEmpty(0, 0, 0, 100, 100));
    5088             : 
    5089             :     // TODO: Add more tests for normal data area calculation.
    5090             : 
    5091           1 :     m_pDoc->DeleteTab(0);
    5092           1 : }
    5093             : 
    5094           1 : void Test::testStreamValid()
    5095             : {
    5096           1 :     m_pDoc->InsertTab(0, OUString("Sheet1"));
    5097           1 :     m_pDoc->InsertTab(1, OUString("Sheet2"));
    5098           1 :     m_pDoc->InsertTab(2, OUString("Sheet3"));
    5099           1 :     m_pDoc->InsertTab(3, OUString("Sheet4"));
    5100           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("We should have 4 sheet instances.", m_pDoc->GetTableCount(), static_cast<SCTAB>(4));
    5101             : 
    5102           1 :     OUString a1("A1");
    5103           2 :     OUString a2("A2");
    5104           2 :     OUString test;
    5105             : 
    5106             :     // Put values into Sheet1.
    5107           1 :     m_pDoc->SetString(0, 0, 0, a1);
    5108           1 :     m_pDoc->SetString(0, 1, 0, a2);
    5109           1 :     test = m_pDoc->GetString(0, 0, 0);
    5110           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A1", test.equals(a1));
    5111           1 :     test = m_pDoc->GetString(0, 1, 0);
    5112           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet1.A2", test.equals(a2));
    5113             : 
    5114             :     // Put formulas into Sheet2 to Sheet4 to reference values from Sheet1.
    5115           1 :     m_pDoc->SetString(0, 0, 1, OUString("=Sheet1.A1"));
    5116           1 :     m_pDoc->SetString(0, 1, 1, OUString("=Sheet1.A2"));
    5117           1 :     m_pDoc->SetString(0, 0, 2, OUString("=Sheet1.A1"));
    5118           1 :     m_pDoc->SetString(0, 0, 3, OUString("=Sheet1.A2"));
    5119             : 
    5120           1 :     test = m_pDoc->GetString(0, 0, 1);
    5121           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A1", test.equals(a1));
    5122           1 :     test = m_pDoc->GetString(0, 1, 1);
    5123           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet2.A2", test.equals(a2));
    5124           1 :     test = m_pDoc->GetString(0, 0, 2);
    5125           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a1));
    5126           1 :     test = m_pDoc->GetString(0, 0, 3);
    5127           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected value in Sheet3.A1", test.equals(a2));
    5128             : 
    5129             :     // Set all sheet streams valid after all the initial cell values are in
    5130             :     // place. In reality we need to have real XML streams stored in order to
    5131             :     // claim they are valid, but we are just testing the flag values here.
    5132           1 :     m_pDoc->SetStreamValid(0, true);
    5133           1 :     m_pDoc->SetStreamValid(1, true);
    5134           1 :     m_pDoc->SetStreamValid(2, true);
    5135           1 :     m_pDoc->SetStreamValid(3, true);
    5136           1 :     CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(0));
    5137           1 :     CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(1));
    5138           1 :     CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(2));
    5139           1 :     CPPUNIT_ASSERT_MESSAGE("Stream is expected to be valid.", m_pDoc->IsStreamValid(3));
    5140             : 
    5141             :     // Now, insert a new row at row 2 position on Sheet1.  This will move cell
    5142             :     // A2 downward but cell A1 remains unmoved.
    5143           1 :     m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 2);
    5144           1 :     test = m_pDoc->GetString(0, 0, 0);
    5145           1 :     CPPUNIT_ASSERT_MESSAGE("Cell A1 should not have moved.", test.equals(a1));
    5146           1 :     test = m_pDoc->GetString(0, 3, 0);
    5147           1 :     CPPUNIT_ASSERT_MESSAGE("the old cell A2 should now be at A4.", test.equals(a2));
    5148           2 :     ScRefCellValue aCell;
    5149           1 :     aCell.assign(*m_pDoc, ScAddress(0,1,0));
    5150           1 :     CPPUNIT_ASSERT_MESSAGE("Cell A2 should be empty.", aCell.isEmpty());
    5151           1 :     aCell.assign(*m_pDoc, ScAddress(0,2,0));
    5152           1 :     CPPUNIT_ASSERT_MESSAGE("Cell A3 should be empty.", aCell.isEmpty());
    5153             : 
    5154             :     // After the move, Sheet1, Sheet2, and Sheet4 should have their stream
    5155             :     // invalidated, whereas Sheet3's stream should still be valid.
    5156           1 :     CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(0));
    5157           1 :     CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(1));
    5158           1 :     CPPUNIT_ASSERT_MESSAGE("Stream should have been invalidated.", !m_pDoc->IsStreamValid(3));
    5159           1 :     CPPUNIT_ASSERT_MESSAGE("Stream should still be valid.", m_pDoc->IsStreamValid(2));
    5160             : 
    5161           1 :     m_pDoc->DeleteTab(3);
    5162           1 :     m_pDoc->DeleteTab(2);
    5163           1 :     m_pDoc->DeleteTab(1);
    5164           2 :     m_pDoc->DeleteTab(0);
    5165           1 : }
    5166             : 
    5167           1 : void Test::testFunctionLists()
    5168             : {
    5169             :     const char* aDataBase[] = {
    5170             :         "DAVERAGE",
    5171             :         "DCOUNT",
    5172             :         "DCOUNTA",
    5173             :         "DGET",
    5174             :         "DMAX",
    5175             :         "DMIN",
    5176             :         "DPRODUCT",
    5177             :         "DSTDEV",
    5178             :         "DSTDEVP",
    5179             :         "DSUM",
    5180             :         "DVAR",
    5181             :         "DVARP",
    5182             :         0
    5183           1 :     };
    5184             : 
    5185             :     const char* aDateTime[] = {
    5186             :         "DATE",
    5187             :         "DATEDIF",
    5188             :         "DATEVALUE",
    5189             :         "DAY",
    5190             :         "DAYS",
    5191             :         "DAYS360",
    5192             :         "EASTERSUNDAY",
    5193             :         "HOUR",
    5194             :         "MINUTE",
    5195             :         "MONTH",
    5196             :         "NOW",
    5197             :         "SECOND",
    5198             :         "TIME",
    5199             :         "TIMEVALUE",
    5200             :         "TODAY",
    5201             :         "WEEKDAY",
    5202             :         "WEEKNUM",
    5203             :         "YEAR",
    5204             :         0
    5205           1 :     };
    5206             : 
    5207             :     const char* aFinancial[] = {
    5208             :         "CUMIPMT",
    5209             :         "CUMPRINC",
    5210             :         "DB",
    5211             :         "DDB",
    5212             :         "DURATION",
    5213             :         "EFFECTIVE",
    5214             :         "FV",
    5215             :         "IPMT",
    5216             :         "IRR",
    5217             :         "ISPMT",
    5218             :         "MIRR",
    5219             :         "NOMINAL",
    5220             :         "NPER",
    5221             :         "NPV",
    5222             :         "PMT",
    5223             :         "PPMT",
    5224             :         "PV",
    5225             :         "RATE",
    5226             :         "RRI",
    5227             :         "SLN",
    5228             :         "SYD",
    5229             :         "VDB",
    5230             :         0
    5231           1 :     };
    5232             : 
    5233             :     const char* aInformation[] = {
    5234             :         "CELL",
    5235             :         "CURRENT",
    5236             :         "FORMULA",
    5237             :         "INFO",
    5238             :         "ISBLANK",
    5239             :         "ISERR",
    5240             :         "ISERROR",
    5241             :         "ISFORMULA",
    5242             :         "ISLOGICAL",
    5243             :         "ISNA",
    5244             :         "ISNONTEXT",
    5245             :         "ISNUMBER",
    5246             :         "ISREF",
    5247             :         "ISTEXT",
    5248             :         "N",
    5249             :         "NA",
    5250             :         "TYPE",
    5251             :         0
    5252           1 :     };
    5253             : 
    5254             :     const char* aLogical[] = {
    5255             :         "AND",
    5256             :         "FALSE",
    5257             :         "IF",
    5258             :         "IFERROR",
    5259             :         "IFNA",
    5260             :         "NOT",
    5261             :         "OR",
    5262             :         "TRUE",
    5263             :         "XOR",
    5264             :         0
    5265           1 :     };
    5266             : 
    5267             :     const char* aMathematical[] = {
    5268             :         "ABS",
    5269             :         "ACOS",
    5270             :         "ACOSH",
    5271             :         "ACOT",
    5272             :         "ACOTH",
    5273             :         "ASIN",
    5274             :         "ASINH",
    5275             :         "ATAN",
    5276             :         "ATAN2",
    5277             :         "ATANH",
    5278             :         "AVERAGEIF",
    5279             :         "AVERAGEIFS",
    5280             :         "BITAND",
    5281             :         "BITLSHIFT",
    5282             :         "BITOR",
    5283             :         "BITRSHIFT",
    5284             :         "BITXOR",
    5285             :         "CEILING",
    5286             :         "COMBIN",
    5287             :         "COMBINA",
    5288             :         "CONVERT",
    5289             :         "COS",
    5290             :         "COSH",
    5291             :         "COT",
    5292             :         "COTH",
    5293             :         "COUNTBLANK",
    5294             :         "COUNTIF",
    5295             :         "COUNTIFS",
    5296             :         "CSC",
    5297             :         "CSCH",
    5298             :         "DEGREES",
    5299             :         "EUROCONVERT",
    5300             :         "EVEN",
    5301             :         "EXP",
    5302             :         "FACT",
    5303             :         "FLOOR",
    5304             :         "GCD",
    5305             :         "INT",
    5306             :         "ISEVEN",
    5307             :         "ISODD",
    5308             :         "LCM",
    5309             :         "LN",
    5310             :         "LOG",
    5311             :         "LOG10",
    5312             :         "MOD",
    5313             :         "ODD",
    5314             :         "PI",
    5315             :         "POWER",
    5316             :         "PRODUCT",
    5317             :         "RADIANS",
    5318             :         "RAND",
    5319             :         "ROUND",
    5320             :         "ROUNDDOWN",
    5321             :         "ROUNDUP",
    5322             :         "SEC",
    5323             :         "SECH",
    5324             :         "SIGN",
    5325             :         "SIN",
    5326             :         "SINH",
    5327             :         "SQRT",
    5328             :         "SUBTOTAL",
    5329             :         "SUM",
    5330             :         "SUMIF",
    5331             :         "SUMIFS",
    5332             :         "SUMSQ",
    5333             :         "TAN",
    5334             :         "TANH",
    5335             :         "TRUNC",
    5336             :         0
    5337           1 :     };
    5338             : 
    5339             :     const char* aArray[] = {
    5340             :         "FREQUENCY",
    5341             :         "GROWTH",
    5342             :         "LINEST",
    5343             :         "LOGEST",
    5344             :         "MDETERM",
    5345             :         "MINVERSE",
    5346             :         "MMULT",
    5347             :         "MUNIT",
    5348             :         "SUMPRODUCT",
    5349             :         "SUMX2MY2",
    5350             :         "SUMX2PY2",
    5351             :         "SUMXMY2",
    5352             :         "TRANSPOSE",
    5353             :         "TREND",
    5354             :         0
    5355           1 :     };
    5356             : 
    5357             :     const char* aStatistical[] = {
    5358             :         "AVEDEV",
    5359             :         "AVERAGE",
    5360             :         "AVERAGEA",
    5361             :         "B",
    5362             :         "BETADIST",
    5363             :         "BETAINV",
    5364             :         "BINOMDIST",
    5365             :         "CHIDIST",
    5366             :         "CHIINV",
    5367             :         "CHISQDIST",
    5368             :         "CHISQINV",
    5369             :         "CHITEST",
    5370             :         "CONFIDENCE",
    5371             :         "CORREL",
    5372             :         "COUNT",
    5373             :         "COUNTA",
    5374             :         "COVAR",
    5375             :         "CRITBINOM",
    5376             :         "DEVSQ",
    5377             :         "EXPONDIST",
    5378             :         "FDIST",
    5379             :         "FINV",
    5380             :         "FISHER",
    5381             :         "FISHERINV",
    5382             :         "FORECAST",
    5383             :         "FTEST",
    5384             :         "GAMMA",
    5385             :         "GAMMADIST",
    5386             :         "GAMMAINV",
    5387             :         "GAMMALN",
    5388             :         "GAUSS",
    5389             :         "GEOMEAN",
    5390             :         "HARMEAN",
    5391             :         "HYPGEOMDIST",
    5392             :         "INTERCEPT",
    5393             :         "KURT",
    5394             :         "LARGE",
    5395             :         "LOGINV",
    5396             :         "LOGNORMDIST",
    5397             :         "MAX",
    5398             :         "MAXA",
    5399             :         "MEDIAN",
    5400             :         "MIN",
    5401             :         "MINA",
    5402             :         "MODE",
    5403             :         "NEGBINOMDIST",
    5404             :         "NORMDIST",
    5405             :         "NORMINV",
    5406             :         "NORMSDIST",
    5407             :         "NORMSINV",
    5408             :         "PEARSON",
    5409             :         "PERCENTILE",
    5410             :         "PERCENTRANK",
    5411             :         "PERMUT",
    5412             :         "PERMUTATIONA",
    5413             :         "PHI",
    5414             :         "POISSON",
    5415             :         "PROB",
    5416             :         "QUARTILE",
    5417             :         "RANK",
    5418             :         "RSQ",
    5419             :         "SKEW",
    5420             :         "SKEWP",
    5421             :         "SLOPE",
    5422             :         "SMALL",
    5423             :         "STANDARDIZE",
    5424             :         "STDEV",
    5425             :         "STDEVA",
    5426             :         "STDEVP",
    5427             :         "STDEVPA",
    5428             :         "STEYX",
    5429             :         "TDIST",
    5430             :         "TINV",
    5431             :         "TRIMMEAN",
    5432             :         "TTEST",
    5433             :         "VAR",
    5434             :         "VARA",
    5435             :         "VARP",
    5436             :         "VARPA",
    5437             :         "WEIBULL",
    5438             :         "ZTEST",
    5439             :         0
    5440           1 :     };
    5441             : 
    5442             :     const char* aSpreadsheet[] = {
    5443             :         "ADDRESS",
    5444             :         "AREAS",
    5445             :         "CHOOSE",
    5446             :         "COLUMN",
    5447             :         "COLUMNS",
    5448             :         "DDE",
    5449             :         "ERRORTYPE",
    5450             :         "GETPIVOTDATA",
    5451             :         "HLOOKUP",
    5452             :         "HYPERLINK",
    5453             :         "INDEX",
    5454             :         "INDIRECT",
    5455             :         "LOOKUP",
    5456             :         "MATCH",
    5457             :         "OFFSET",
    5458             :         "ROW",
    5459             :         "ROWS",
    5460             :         "SHEET",
    5461             :         "SHEETS",
    5462             :         "STYLE",
    5463             :         "VLOOKUP",
    5464             :         0
    5465           1 :     };
    5466             : 
    5467             :     const char* aText[] = {
    5468             :         "ARABIC",
    5469             :         "ASC",
    5470             :         "BAHTTEXT",
    5471             :         "BASE",
    5472             :         "CHAR",
    5473             :         "CLEAN",
    5474             :         "CODE",
    5475             :         "CONCATENATE",
    5476             :         "DECIMAL",
    5477             :         "DOLLAR",
    5478             :         "EXACT",
    5479             :         "FILTERXML",
    5480             :         "FIND",
    5481             :         "FIXED",
    5482             :         "JIS",
    5483             :         "LEFT",
    5484             :         "LEFTB",
    5485             :         "LEN",
    5486             :         "LENB",
    5487             :         "LOWER",
    5488             :         "MID",
    5489             :         "MIDB",
    5490             :         "NUMBERVALUE",
    5491             :         "PROPER",
    5492             :         "REPLACE",
    5493             :         "REPT",
    5494             :         "RIGHT",
    5495             :         "RIGHTB",
    5496             :         "ROMAN",
    5497             :         "SEARCH",
    5498             :         "SUBSTITUTE",
    5499             :         "T",
    5500             :         "TEXT",
    5501             :         "TRIM",
    5502             :         "UNICHAR",
    5503             :         "UNICODE",
    5504             :         "UPPER",
    5505             :         "VALUE",
    5506             :         "WEBSERVICE",
    5507             :         0
    5508           1 :     };
    5509             : 
    5510             :     struct {
    5511             :         const char* Category; const char** Functions;
    5512             :     } aTests[] = {
    5513             :         { "Database",     aDataBase },
    5514             :         { "Date&Time",    aDateTime },
    5515             :         { "Financial",    aFinancial },
    5516             :         { "Information",  aInformation },
    5517             :         { "Logical",      aLogical },
    5518             :         { "Mathematical", aMathematical },
    5519             :         { "Array",        aArray },
    5520             :         { "Statistical",  aStatistical },
    5521             :         { "Spreadsheet",  aSpreadsheet },
    5522             :         { "Text",         aText },
    5523             :         { "Add-in",       0 },
    5524             :         { 0, 0 }
    5525           1 :     };
    5526             : 
    5527           1 :     ScFunctionMgr* pFuncMgr = ScGlobal::GetStarCalcFunctionMgr();
    5528           1 :     sal_uInt32 n = pFuncMgr->getCount();
    5529          12 :     for (sal_uInt32 i = 0; i < n; ++i)
    5530             :     {
    5531          11 :         const formula::IFunctionCategory* pCat = pFuncMgr->getCategory(i);
    5532          11 :         CPPUNIT_ASSERT_MESSAGE("Unexpected category name", pCat->getName().equalsAscii(aTests[i].Category));
    5533          11 :         sal_uInt32 nFuncCount = pCat->getCount();
    5534         312 :         for (sal_uInt32 j = 0; j < nFuncCount; ++j)
    5535             :         {
    5536         301 :             const formula::IFunctionDescription* pFunc = pCat->getFunction(j);
    5537         301 :             CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected function name", pFunc->getFunctionName(), OUString::createFromAscii(aTests[i].Functions[j]));
    5538             :         }
    5539             :     }
    5540           1 : }
    5541             : 
    5542           1 : void Test::testGraphicsInGroup()
    5543             : {
    5544           1 :     OUString aTabName("TestTab");
    5545           1 :     m_pDoc->InsertTab(0, aTabName);
    5546           1 :     CPPUNIT_ASSERT_MESSAGE("document should have one sheet to begin with.", m_pDoc->GetTableCount() == 1);
    5547             :     SCROW nRow1, nRow2;
    5548           1 :     bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    5549           1 :     CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
    5550             : 
    5551           1 :     m_pDoc->InitDrawLayer();
    5552           1 :     ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
    5553           1 :     CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
    5554           1 :     SdrPage* pPage = pDrawLayer->GetPage(0);
    5555           1 :     CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
    5556             : 
    5557             :     {
    5558             :         //Add a square
    5559           1 :         Rectangle aOrigRect(2,2,100,100);
    5560           1 :         SdrRectObj *pObj = new SdrRectObj(aOrigRect);
    5561           1 :         pPage->InsertObject(pObj);
    5562           1 :         const Rectangle &rNewRect = pObj->GetLogicRect();
    5563           1 :         CPPUNIT_ASSERT_MESSAGE("must have equal position and size", aOrigRect == rNewRect);
    5564             : 
    5565           1 :         ScDrawLayer::SetPageAnchored(*pObj);
    5566             : 
    5567             :         //Use a range of rows guaranteed to include all of the square
    5568           1 :         m_pDoc->ShowRows(0, 100, 0, false);
    5569           1 :         m_pDoc->SetDrawPageSize(0);
    5570           1 :         CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
    5571           1 :         m_pDoc->ShowRows(0, 100, 0, true);
    5572           1 :         m_pDoc->SetDrawPageSize(0);
    5573           1 :         CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
    5574             : 
    5575           1 :         ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
    5576           1 :         CPPUNIT_ASSERT_MESSAGE("That shouldn't change size or positioning", aOrigRect == rNewRect);
    5577             : 
    5578           1 :         m_pDoc->ShowRows(0, 100, 0, false);
    5579           1 :         m_pDoc->SetDrawPageSize(0);
    5580           2 :         CPPUNIT_ASSERT_MESSAGE("Left and Right should be unchanged",
    5581           1 :             aOrigRect.Left() == rNewRect.Left() && aOrigRect.Right() == rNewRect.Right());
    5582           2 :         CPPUNIT_ASSERT_MESSAGE("Height should be minimum allowed height",
    5583           1 :             (rNewRect.Bottom() - rNewRect.Top()) <= 1);
    5584           1 :         m_pDoc->ShowRows(0, 100, 0, true);
    5585           1 :         m_pDoc->SetDrawPageSize(0);
    5586           1 :         CPPUNIT_ASSERT_MESSAGE("Should not change when page anchored", aOrigRect == rNewRect);
    5587             :     }
    5588             : 
    5589             :     {
    5590             :         // Add a circle.
    5591           1 :         Rectangle aOrigRect = Rectangle(10,10,210,210); // 200 x 200
    5592           1 :         SdrCircObj* pObj = new SdrCircObj(OBJ_CIRC, aOrigRect);
    5593           1 :         pPage->InsertObject(pObj);
    5594           1 :         const Rectangle& rNewRect = pObj->GetLogicRect();
    5595           2 :         CPPUNIT_ASSERT_MESSAGE("Position and size of the circle shouldn't change when inserted into the page.",
    5596           1 :                                aOrigRect == rNewRect);
    5597             : 
    5598           1 :         ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
    5599           2 :         CPPUNIT_ASSERT_MESSAGE("Size changed when cell anchored. Not good.",
    5600           1 :                                aOrigRect == rNewRect);
    5601             : 
    5602             :         // Insert 2 rows at the top.  This should push the circle object down.
    5603           1 :         m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
    5604           1 :         m_pDoc->SetDrawPageSize(0);
    5605             : 
    5606             :         // Make sure the size of the circle is still identical.
    5607           2 :         CPPUNIT_ASSERT_MESSAGE("Size of the circle has changed, but shouldn't!",
    5608           1 :                                aOrigRect.GetSize() == rNewRect.GetSize());
    5609             : 
    5610             :         // Delete 2 rows at the top.  This should bring the circle object to its original position.
    5611           1 :         m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
    5612           1 :         m_pDoc->SetDrawPageSize(0);
    5613           1 :         CPPUNIT_ASSERT_MESSAGE("Failed to move back to its original position.", aOrigRect == rNewRect);
    5614             :     }
    5615             : 
    5616             :     {
    5617             :         // Add a line.
    5618           1 :         basegfx::B2DPolygon aTempPoly;
    5619           1 :         Point aStartPos(10,300), aEndPos(110,200); // bottom-left to top-right.
    5620           1 :         Rectangle aOrigRect(10,200,110,300); // 100 x 100
    5621           1 :         aTempPoly.append(basegfx::B2DPoint(aStartPos.X(), aStartPos.Y()));
    5622           1 :         aTempPoly.append(basegfx::B2DPoint(aEndPos.X(), aEndPos.Y()));
    5623           1 :         SdrPathObj* pObj = new SdrPathObj(OBJ_LINE, basegfx::B2DPolyPolygon(aTempPoly));
    5624           1 :         pObj->NbcSetLogicRect(aOrigRect);
    5625           1 :         pPage->InsertObject(pObj);
    5626           1 :         const Rectangle& rNewRect = pObj->GetLogicRect();
    5627           1 :         CPPUNIT_ASSERT_MESSAGE("Size differ.", aOrigRect == rNewRect);
    5628             : 
    5629           1 :         ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
    5630           2 :         CPPUNIT_ASSERT_MESSAGE("Size changed when cell-anchored. Not good.",
    5631           1 :                                aOrigRect == rNewRect);
    5632             : 
    5633             :         // Insert 2 rows at the top and delete them immediately.
    5634           1 :         m_pDoc->InsertRow(0, 0, MAXCOL, 0, 0, 2);
    5635           1 :         m_pDoc->DeleteRow(0, 0, MAXCOL, 0, 0, 2);
    5636           1 :         m_pDoc->SetDrawPageSize(0);
    5637           2 :         CPPUNIT_ASSERT_MESSAGE("Size of a line object changed after row insertion and removal.",
    5638           1 :                                aOrigRect == rNewRect);
    5639             : 
    5640           1 :         sal_Int32 n = pObj->GetPointCount();
    5641           1 :         CPPUNIT_ASSERT_MESSAGE("There should be exactly 2 points in a line object.", n == 2);
    5642           2 :         CPPUNIT_ASSERT_MESSAGE("Line shape has changed.",
    5643           2 :                                aStartPos == pObj->GetPoint(0) && aEndPos == pObj->GetPoint(1));
    5644             :     }
    5645             : 
    5646           1 :     m_pDoc->DeleteTab(0);
    5647           1 : }
    5648             : 
    5649           1 : void Test::testGraphicsOnSheetMove()
    5650             : {
    5651           1 :     m_pDoc->InsertTab(0, OUString("Tab1"));
    5652           1 :     m_pDoc->InsertTab(1, OUString("Tab2"));
    5653           1 :     CPPUNIT_ASSERT_MESSAGE("There should be only 2 sheets to begin with", m_pDoc->GetTableCount() == 2);
    5654             : 
    5655           1 :     m_pDoc->InitDrawLayer();
    5656           1 :     ScDrawLayer* pDrawLayer = m_pDoc->GetDrawLayer();
    5657           1 :     CPPUNIT_ASSERT_MESSAGE("No drawing layer.", pDrawLayer);
    5658           1 :     SdrPage* pPage = pDrawLayer->GetPage(0);
    5659           1 :     CPPUNIT_ASSERT_MESSAGE("No page instance for the 1st sheet.", pPage);
    5660             : 
    5661             :     // Insert an object.
    5662           1 :     Rectangle aObjRect(2,2,100,100);
    5663           1 :     SdrObject* pObj = new SdrRectObj(aObjRect);
    5664           1 :     pPage->InsertObject(pObj);
    5665           1 :     ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
    5666             : 
    5667           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be one object on the 1st sheet.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(1));
    5668             : 
    5669           1 :     const ScDrawObjData* pData = ScDrawLayer::GetObjData(pObj);
    5670           1 :     CPPUNIT_ASSERT_MESSAGE("Object meta-data doesn't exist.", pData);
    5671           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
    5672             : 
    5673           1 :     pPage = pDrawLayer->GetPage(1);
    5674           1 :     CPPUNIT_ASSERT_MESSAGE("No page instance for the 2nd sheet.", pPage);
    5675           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("2nd sheet shouldn't have any object.", pPage->GetObjCount(), static_cast<sal_uIntPtr>(0));
    5676             : 
    5677             :     // Insert a new sheet at left-end, and make sure the object has moved to
    5678             :     // the 2nd page.
    5679           1 :     m_pDoc->InsertTab(0, OUString("NewTab"));
    5680           1 :     CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 3 sheets.", m_pDoc->GetTableCount(), static_cast<SCTAB>(3));
    5681           1 :     pPage = pDrawLayer->GetPage(0);
    5682           1 :     CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
    5683           1 :     pPage = pDrawLayer->GetPage(1);
    5684           1 :     CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
    5685           1 :     pPage = pDrawLayer->GetPage(2);
    5686           1 :     CPPUNIT_ASSERT_MESSAGE("3rd sheet should have no object.", pPage && pPage->GetObjCount() == 0);
    5687             : 
    5688           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
    5689             : 
    5690             :     // Now, delete the sheet that just got inserted. The object should be back
    5691             :     // on the 1st sheet.
    5692           1 :     m_pDoc->DeleteTab(0);
    5693           1 :     pPage = pDrawLayer->GetPage(0);
    5694           1 :     CPPUNIT_ASSERT_MESSAGE("1st sheet should have one object.", pPage && pPage->GetObjCount() == 1);
    5695           2 :     CPPUNIT_ASSERT_MESSAGE("Size and position of the object shouldn't change.",
    5696           1 :                            pObj->GetLogicRect() == aObjRect);
    5697             : 
    5698           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 0 && pData->maEnd.Tab() == 0);
    5699             : 
    5700             :     // Move the 1st sheet to the last position.
    5701           1 :     m_pDoc->MoveTab(0, 1);
    5702           1 :     pPage = pDrawLayer->GetPage(0);
    5703           1 :     CPPUNIT_ASSERT_MESSAGE("1st sheet should have no object.", pPage && pPage->GetObjCount() == 0);
    5704           1 :     pPage = pDrawLayer->GetPage(1);
    5705           1 :     CPPUNIT_ASSERT_MESSAGE("2nd sheet should have one object.", pPage && pPage->GetObjCount() == 1);
    5706           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 1 && pData->maEnd.Tab() == 1);
    5707             : 
    5708             :     // Copy the 2nd sheet, which has one drawing object to the last position.
    5709           1 :     m_pDoc->CopyTab(1, 2);
    5710           1 :     pPage = pDrawLayer->GetPage(2);
    5711           1 :     CPPUNIT_ASSERT_MESSAGE("Copied sheet should have one object.", pPage && pPage->GetObjCount() == 1);
    5712           1 :     pObj = pPage->GetObj(0);
    5713           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object.", pObj);
    5714           1 :     pData = ScDrawLayer::GetObjData(pObj);
    5715           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to get drawing object meta-data.", pData);
    5716           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong sheet ID in cell anchor data!", pData->maStart.Tab() == 2 && pData->maEnd.Tab() == 2);
    5717             : 
    5718           1 :     m_pDoc->DeleteTab(2);
    5719           1 :     m_pDoc->DeleteTab(1);
    5720           1 :     m_pDoc->DeleteTab(0);
    5721           1 : }
    5722             : 
    5723           1 : void Test::testPostIts()
    5724             : {
    5725           1 :     OUString aHello("Hello world");
    5726           2 :     OUString aJimBob("Jim Bob");
    5727           2 :     OUString aTabName("PostIts");
    5728           2 :     OUString aTabName2("Table2");
    5729           1 :     m_pDoc->InsertTab(0, aTabName);
    5730             : 
    5731           1 :     ScAddress rAddr(2, 2, 0); // cell C3
    5732           1 :     ScPostIt *pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
    5733           1 :     pNote->SetText(rAddr, aHello);
    5734           1 :     pNote->SetAuthor(aJimBob);
    5735             : 
    5736           1 :     ScPostIt *pGetNote = m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr);
    5737           1 :     CPPUNIT_ASSERT_MESSAGE("note should be itself", pGetNote == pNote );
    5738             : 
    5739             :     // Insert one row at row 1.
    5740           1 :     bool bInsertRow = m_pDoc->InsertRow(0, 0, MAXCOL, 0, 1, 1);
    5741           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert row", bInsertRow );
    5742             : 
    5743           1 :     CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
    5744           1 :     rAddr.IncRow(); // cell C4
    5745           1 :     CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
    5746             : 
    5747             :     // Insert column at column A.
    5748           1 :     bool bInsertCol = m_pDoc->InsertCol(0, 0, MAXROW, 0, 1, 1);
    5749           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert column", bInsertCol );
    5750             : 
    5751           1 :     CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
    5752           1 :     rAddr.IncCol(); // cell D4
    5753           1 :     CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
    5754             : 
    5755             :     // Insert a new sheet to shift the current sheet to the right.
    5756           1 :     m_pDoc->InsertTab(0, aTabName2);
    5757           1 :     CPPUNIT_ASSERT_MESSAGE("note hasn't moved", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == NULL);
    5758           1 :     rAddr.IncTab(); // Move to the next sheet.
    5759           1 :     CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
    5760             : 
    5761           1 :     m_pDoc->DeleteTab(0);
    5762           1 :     rAddr.IncTab(-1);
    5763           1 :     CPPUNIT_ASSERT_MESSAGE("note not there", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
    5764             : 
    5765             :     // Insert cell at C4.  This should NOT shift the note position.
    5766           1 :     bInsertRow = m_pDoc->InsertRow(2, 0, 2, 0, 3, 1);
    5767           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to insert cell at C4.", bInsertRow);
    5768           1 :     CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
    5769             : 
    5770             :     // Delete cell at C4.  Again, this should NOT shift the note position.
    5771           1 :     m_pDoc->DeleteRow(2, 0, 2, 0, 3, 1);
    5772           1 :     CPPUNIT_ASSERT_MESSAGE("Note shouldn't have moved but it has.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
    5773             : 
    5774             :     // Now, with the note at D4, delete cell D3. This should shift the note one cell up.
    5775           1 :     m_pDoc->DeleteRow(3, 0, 3, 0, 2, 1);
    5776           1 :     rAddr.IncRow(-1); // cell D3
    5777           1 :     CPPUNIT_ASSERT_MESSAGE("Note at D4 should have shifted up to D3.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
    5778             : 
    5779             :     // Delete column C. This should shift the note one cell left.
    5780           1 :     m_pDoc->DeleteCol(0, 0, MAXROW, 0, 2, 1);
    5781           1 :     rAddr.IncCol(-1); // cell C3
    5782           1 :     CPPUNIT_ASSERT_MESSAGE("Note at D3 should have shifted left to C3.", m_pDoc->GetNotes(rAddr.Tab())->findByAddress(rAddr) == pNote);
    5783             : 
    5784           2 :     m_pDoc->DeleteTab(0);
    5785           1 : }
    5786             : 
    5787           1 : void Test::testToggleRefFlag()
    5788             : {
    5789             :     // In this test, there is no need to insert formula string into a cell in
    5790             :     // the document, as ScRefFinder does not depend on the content of the
    5791             :     // document except for the sheet names.
    5792             : 
    5793           1 :     OUString aTabName("Test");
    5794           1 :     m_pDoc->InsertTab(0, aTabName);
    5795             : 
    5796             :     {
    5797             :         // Calc A1: basic 2D reference
    5798             : 
    5799           1 :         OUString aFormula("=B100");
    5800           1 :         ScAddress aPos(1, 5, 0);
    5801           2 :         ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
    5802             : 
    5803             :         // Original
    5804           1 :         CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
    5805             : 
    5806             :         // column relative / row relative -> column absolute / row absolute
    5807           1 :         aFinder.ToggleRel(0, aFormula.getLength());
    5808           1 :         aFormula = aFinder.GetText();
    5809           1 :         CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B$100" );
    5810             : 
    5811             :         // column absolute / row absolute -> column relative / row absolute
    5812           1 :         aFinder.ToggleRel(0, aFormula.getLength());
    5813           1 :         aFormula = aFinder.GetText();
    5814           1 :         CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B$100" );
    5815             : 
    5816             :         // column relative / row absolute -> column absolute / row relative
    5817           1 :         aFinder.ToggleRel(0, aFormula.getLength());
    5818           1 :         aFormula = aFinder.GetText();
    5819           1 :         CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=$B100" );
    5820             : 
    5821             :         // column absolute / row relative -> column relative / row relative
    5822           1 :         aFinder.ToggleRel(0, aFormula.getLength());
    5823           1 :         aFormula = aFinder.GetText();
    5824           2 :         CPPUNIT_ASSERT_MESSAGE( "Wrong conversion.", aFormula == "=B100" );
    5825             :     }
    5826             : 
    5827             :     {
    5828             :         // Excel R1C1: basic 2D reference
    5829             : 
    5830           1 :         OUString aFormula("=R2C1");
    5831           1 :         ScAddress aPos(3, 5, 0);
    5832           2 :         ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
    5833             : 
    5834             :         // Original
    5835           1 :         CPPUNIT_ASSERT_MESSAGE("Does not equal the original text.", aFormula.equals(aFinder.GetText()));
    5836             : 
    5837             :         // column absolute / row absolute -> column relative / row absolute
    5838           1 :         aFinder.ToggleRel(0, aFormula.getLength());
    5839           1 :         aFormula = aFinder.GetText();
    5840           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=R2C[-3]"), aFormula);
    5841             : 
    5842             :         // column relative / row absolute - > column absolute / row relative
    5843           1 :         aFinder.ToggleRel(0, aFormula.getLength());
    5844           1 :         aFormula = aFinder.GetText();
    5845           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C1"), aFormula);
    5846             : 
    5847             :         // column absolute / row relative -> column relative / row relative
    5848           1 :         aFinder.ToggleRel(0, aFormula.getLength());
    5849           1 :         aFormula = aFinder.GetText();
    5850           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=R[-4]C[-3]"), aFormula);
    5851             : 
    5852             :         // column relative / row relative -> column absolute / row absolute
    5853           1 :         aFinder.ToggleRel(0, aFormula.getLength());
    5854           1 :         aFormula = aFinder.GetText();
    5855           2 :         CPPUNIT_ASSERT_EQUAL(OUString("=R2C1"), aFormula);
    5856             :     }
    5857             : 
    5858             :     {
    5859             :         // Excel R1C1: Selection at the end of the formula string and does not
    5860             :         // overlap the formula string at all (inspired by fdo#39135).
    5861           1 :         OUString aFormula("=R1C1");
    5862           1 :         ScAddress aPos(1, 1, 0);
    5863           2 :         ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_XL_R1C1);
    5864             : 
    5865             :         // Original
    5866           1 :         CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
    5867             : 
    5868             :         // Make the column relative.
    5869           1 :         sal_Int32 n = aFormula.getLength();
    5870           1 :         aFinder.ToggleRel(n, n);
    5871           1 :         aFormula = aFinder.GetText();
    5872           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=R1C[-1]"), aFormula);
    5873             : 
    5874             :         // Make the row relative.
    5875           1 :         n = aFormula.getLength();
    5876           1 :         aFinder.ToggleRel(n, n);
    5877           1 :         aFormula = aFinder.GetText();
    5878           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C1"), aFormula);
    5879             : 
    5880             :         // Make both relative.
    5881           1 :         n = aFormula.getLength();
    5882           1 :         aFinder.ToggleRel(n, n);
    5883           1 :         aFormula = aFinder.GetText();
    5884           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=R[-1]C[-1]"), aFormula);
    5885             : 
    5886             :         // Back to the original.
    5887           1 :         n = aFormula.getLength();
    5888           1 :         aFinder.ToggleRel(n, n);
    5889           1 :         aFormula = aFinder.GetText();
    5890           2 :         CPPUNIT_ASSERT_EQUAL(OUString("=R1C1"), aFormula);
    5891             :     }
    5892             : 
    5893             :     {
    5894             :         // Calc A1:
    5895           1 :         OUString aFormula("=A1+4");
    5896           1 :         ScAddress aPos(1, 1, 0);
    5897           2 :         ScRefFinder aFinder(aFormula, aPos, m_pDoc, formula::FormulaGrammar::CONV_OOO);
    5898             : 
    5899             :         // Original
    5900           1 :         CPPUNIT_ASSERT_EQUAL(aFormula, aFinder.GetText());
    5901             : 
    5902             :         // Set the cursor over the 'A1' part and toggle.
    5903           1 :         aFinder.ToggleRel(2, 2);
    5904           1 :         aFormula = aFinder.GetText();
    5905           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=$A$1+4"), aFormula);
    5906             : 
    5907           1 :         aFinder.ToggleRel(2, 2);
    5908           1 :         aFormula = aFinder.GetText();
    5909           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=A$1+4"), aFormula);
    5910             : 
    5911           1 :         aFinder.ToggleRel(2, 2);
    5912           1 :         aFormula = aFinder.GetText();
    5913           1 :         CPPUNIT_ASSERT_EQUAL(OUString("=$A1+4"), aFormula);
    5914             : 
    5915           1 :         aFinder.ToggleRel(2, 2);
    5916           1 :         aFormula = aFinder.GetText();
    5917           2 :         CPPUNIT_ASSERT_EQUAL(OUString("=A1+4"), aFormula);
    5918             :     }
    5919             : 
    5920             :     // TODO: Add more test cases esp. for 3D references, Excel A1 syntax, and
    5921             :     // partial selection within formula string.
    5922             : 
    5923           1 :     m_pDoc->DeleteTab(0);
    5924           1 : }
    5925             : 
    5926           1 : void Test::testAutofilter()
    5927             : {
    5928           1 :     OUString aTabName("Test");
    5929           2 :     OUString aDBName("NONAME");
    5930             : 
    5931           1 :     m_pDoc->InsertTab( 0, aTabName );
    5932             : 
    5933             :     // cell contents (0 = empty cell)
    5934             :     const char* aData[][3] = {
    5935             :         { "C1", "C2", "C3" },
    5936             :         {  "0",  "1",  "A" },
    5937             :         {  "1",  "2",    0 },
    5938             :         {  "1",  "2",  "B" },
    5939             :         {  "0",  "2",  "B" }
    5940           1 :     };
    5941             : 
    5942           1 :     SCCOL nCols = SAL_N_ELEMENTS(aData[0]);
    5943           1 :     SCROW nRows = SAL_N_ELEMENTS(aData);
    5944             : 
    5945             :     // Populate cells.
    5946           6 :     for (SCROW i = 0; i < nRows; ++i)
    5947          20 :         for (SCCOL j = 0; j < nCols; ++j)
    5948          15 :             if (aData[i][j])
    5949          14 :                 m_pDoc->SetString(j, i, 0, OUString::createFromAscii(aData[i][j]));
    5950             : 
    5951           1 :     ScDBData* pDBData = new ScDBData(aDBName, 0, 0, 0, nCols-1, nRows-1);
    5952           1 :     m_pDoc->SetAnonymousDBData(0,pDBData);
    5953             : 
    5954           1 :     pDBData->SetAutoFilter(true);
    5955           1 :     ScRange aRange;
    5956           1 :     pDBData->GetArea(aRange);
    5957           1 :     m_pDoc->ApplyFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
    5958           1 :                            aRange.aEnd.Col(), aRange.aStart.Row(),
    5959           3 :                            aRange.aStart.Tab(), SC_MF_AUTO);
    5960             : 
    5961             :     //create the query param
    5962           2 :     ScQueryParam aParam;
    5963           1 :     pDBData->GetQueryParam(aParam);
    5964           1 :     ScQueryEntry& rEntry = aParam.GetEntry(0);
    5965           1 :     rEntry.bDoQuery = true;
    5966           1 :     rEntry.nField = 0;
    5967           1 :     rEntry.eOp = SC_EQUAL;
    5968           1 :     rEntry.GetQueryItem().mfVal = 0;
    5969             :     // add queryParam to database range.
    5970           1 :     pDBData->SetQueryParam(aParam);
    5971             : 
    5972             :     // perform the query.
    5973           1 :     m_pDoc->Query(0, aParam, true);
    5974             : 
    5975             :     //control output
    5976             :     SCROW nRow1, nRow2;
    5977           1 :     bool bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
    5978           1 :     CPPUNIT_ASSERT_MESSAGE("rows 2 & 3 should be hidden", bHidden && nRow1 == 2 && nRow2 == 3);
    5979             : 
    5980             :     // Remove filtering.
    5981           1 :     rEntry.Clear();
    5982           1 :     m_pDoc->Query(0, aParam, true);
    5983           1 :     bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    5984           1 :     CPPUNIT_ASSERT_MESSAGE("All rows should be shown.", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
    5985             : 
    5986             :     // Filter for non-empty cells by column C.
    5987           1 :     rEntry.bDoQuery = true;
    5988           1 :     rEntry.nField = 2;
    5989           1 :     rEntry.SetQueryByNonEmpty();
    5990           1 :     m_pDoc->Query(0, aParam, true);
    5991             : 
    5992             :     // only row 3 should be hidden.  The rest should be visible.
    5993           1 :     bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    5994           1 :     CPPUNIT_ASSERT_MESSAGE("rows 1 & 2 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 1);
    5995           1 :     bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
    5996           1 :     CPPUNIT_ASSERT_MESSAGE("row 3 should be hidden.", bHidden && nRow1 == 2 && nRow2 == 2);
    5997           1 :     bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
    5998           1 :     CPPUNIT_ASSERT_MESSAGE("row 4 and down should be visible.", !bHidden && nRow1 == 3 && nRow2 == MAXROW);
    5999             : 
    6000             :     // Now, filter for empty cells by column C.
    6001           1 :     rEntry.SetQueryByEmpty();
    6002           1 :     m_pDoc->Query(0, aParam, true);
    6003             : 
    6004             :     // Now, only row 1 and 3, and 6 and down should be visible.
    6005           1 :     bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    6006           1 :     CPPUNIT_ASSERT_MESSAGE("row 1 should be visible.", !bHidden && nRow1 == 0 && nRow2 == 0);
    6007           1 :     bHidden = m_pDoc->RowHidden(1, 0, &nRow1, &nRow2);
    6008           1 :     CPPUNIT_ASSERT_MESSAGE("row 2 should be hidden.", bHidden && nRow1 == 1 && nRow2 == 1);
    6009           1 :     bHidden = m_pDoc->RowHidden(2, 0, &nRow1, &nRow2);
    6010           1 :     CPPUNIT_ASSERT_MESSAGE("row 3 should be visible.", !bHidden && nRow1 == 2 && nRow2 == 2);
    6011           1 :     bHidden = m_pDoc->RowHidden(3, 0, &nRow1, &nRow2);
    6012           1 :     CPPUNIT_ASSERT_MESSAGE("rows 4 & 5 should be hidden.", bHidden && nRow1 == 3 && nRow2 == 4);
    6013           1 :     bHidden = m_pDoc->RowHidden(5, 0, &nRow1, &nRow2);
    6014           1 :     CPPUNIT_ASSERT_MESSAGE("rows 6 and down should be all visible.", !bHidden && nRow1 == 5 && nRow2 == MAXROW);
    6015             : 
    6016           2 :     m_pDoc->DeleteTab(0);
    6017           1 : }
    6018             : 
    6019           1 : void Test::testCopyPaste()
    6020             : {
    6021           1 :     m_pDoc->InsertTab(0, OUString("Sheet1"));
    6022           1 :     m_pDoc->InsertTab(1, OUString("Sheet2"));
    6023             :     //test copy&paste + ScUndoPaste
    6024             :     //copy local and global range names in formulas
    6025             :     //string cells and value cells
    6026           1 :     m_pDoc->SetValue(0, 0, 0, 1);
    6027           1 :     m_pDoc->SetValue(3, 0, 0, 0);
    6028           1 :     m_pDoc->SetValue(3, 1, 0, 1);
    6029           1 :     m_pDoc->SetValue(3, 2, 0, 2);
    6030           1 :     m_pDoc->SetValue(3, 3, 0, 3);
    6031           1 :     m_pDoc->SetString(2, 0, 0, OUString("test"));
    6032           1 :     ScAddress aAdr (0, 0, 0);
    6033             : 
    6034             :     //create some range names, local and global
    6035           1 :     ScRangeData* pLocal1 = new ScRangeData(m_pDoc, OUString("local1"), aAdr);
    6036           1 :     ScRangeData* pLocal2 = new ScRangeData(m_pDoc, OUString("local2"), aAdr);
    6037           1 :     ScRangeData* pGlobal = new ScRangeData(m_pDoc, OUString("global"), aAdr);
    6038           1 :     ScRangeName* pGlobalRangeName = new ScRangeName();
    6039           1 :     pGlobalRangeName->insert(pGlobal);
    6040           1 :     ScRangeName* pLocalRangeName1 = new ScRangeName();
    6041           1 :     pLocalRangeName1->insert(pLocal1);
    6042           1 :     pLocalRangeName1->insert(pLocal2);
    6043           1 :     m_pDoc->SetRangeName(pGlobalRangeName);
    6044           1 :     m_pDoc->SetRangeName(0, pLocalRangeName1);
    6045             : 
    6046             :     // Add formula to B1.
    6047           1 :     OUString aFormulaString("=local1+global+SUM($C$1:$D$4)");
    6048           1 :     m_pDoc->SetString(1, 0, 0, aFormulaString);
    6049             : 
    6050           1 :     double fValue = m_pDoc->GetValue(ScAddress(1,0,0));
    6051           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 8", fValue, 8);
    6052             : 
    6053             :     //copy Sheet1.A1:C1 to Sheet2.A2:C2
    6054           1 :     ScRange aRange(0,0,0,2,0,0);
    6055           2 :     ScClipParam aClipParam(aRange, false);
    6056           2 :     ScMarkData aMark;
    6057           1 :     aMark.SetMarkArea(aRange);
    6058           2 :     ScDocument aClipDoc(SCDOCMODE_CLIP);
    6059           1 :     m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
    6060             : 
    6061           1 :     sal_uInt16 nFlags = IDF_ALL;
    6062           1 :     aRange = ScRange(0,1,1,2,1,1);//target: Sheet2.A2:C2
    6063           1 :     ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
    6064           1 :     pUndoDoc->InitUndo(m_pDoc, 1, 1, true, true);
    6065           2 :     ScMarkData aMarkData2;
    6066           1 :     aMarkData2.SetMarkArea(aRange);
    6067           1 :     ScRefUndoData* pRefUndoData= new ScRefUndoData(m_pDoc);
    6068             :     ScUndoPaste aUndo(
    6069           2 :         &m_xDocShRef, aRange, aMarkData2, pUndoDoc, NULL, IDF_ALL, pRefUndoData, false);
    6070           1 :     m_pDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, &aClipDoc);
    6071             : 
    6072             :     //check values after copying
    6073           2 :     OUString aString;
    6074           1 :     fValue = m_pDoc->GetValue(ScAddress(1,1,1));
    6075           1 :     m_pDoc->GetFormula(1,1,1, aString);
    6076           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("copied formula should return 2", fValue, 2);
    6077           1 :     CPPUNIT_ASSERT_MESSAGE("formula string was not copied correctly", aString == aFormulaString);
    6078           1 :     fValue = m_pDoc->GetValue(ScAddress(0,1,1));
    6079           1 :     CPPUNIT_ASSERT_MESSAGE("copied value should be 1", fValue == 1);
    6080             : 
    6081             :     //chack local range name after copying
    6082           1 :     pLocal1 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL1"));
    6083           1 :     CPPUNIT_ASSERT_MESSAGE("local range name 1 should be copied", pLocal1);
    6084           1 :     ScRange aRangeLocal1;
    6085           1 :     pLocal1->IsValidReference(aRangeLocal1);
    6086           1 :     CPPUNIT_ASSERT_MESSAGE("local range 1 should still point to Sheet1.A1",aRangeLocal1 == ScRange(0,0,0,0,0,0));
    6087           1 :     pLocal2 = m_pDoc->GetRangeName(1)->findByUpperName(OUString("LOCAL2"));
    6088           1 :     CPPUNIT_ASSERT_MESSAGE("local2 should not be copied", pLocal2 == NULL);
    6089             : 
    6090             : 
    6091             :     //check undo and redo
    6092           1 :     aUndo.Undo();
    6093           1 :     fValue = m_pDoc->GetValue(ScAddress(1,1,1));
    6094           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after undo formula should return nothing", fValue, 0);
    6095           1 :     aString = m_pDoc->GetString(2, 1, 1);
    6096           1 :     CPPUNIT_ASSERT_MESSAGE("after undo string should be removed", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("")));
    6097             : 
    6098           1 :     aUndo.Redo();
    6099           1 :     fValue = m_pDoc->GetValue(ScAddress(1,1,1));
    6100           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 2 after redo", fValue, 2);
    6101           1 :     aString = m_pDoc->GetString(2, 1, 1);
    6102           1 :     CPPUNIT_ASSERT_MESSAGE("Cell Sheet2.C2 should contain: test", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("test")));
    6103           1 :     m_pDoc->GetFormula(1,1,1, aString);
    6104           1 :     CPPUNIT_ASSERT_MESSAGE("Formula should be correct again", aString == aFormulaString);
    6105             : 
    6106           1 :     m_pDoc->DeleteTab(1);
    6107           2 :     m_pDoc->DeleteTab(0);
    6108           1 : }
    6109             : 
    6110           1 : void Test::testMergedCells()
    6111             : {
    6112             :     //test merge and unmerge
    6113             :     //TODO: an undo/redo test for this would be a good idea
    6114           1 :     m_pDoc->InsertTab(0, OUString("Sheet1"));
    6115           1 :     m_pDoc->DoMerge(0, 1, 1, 3, 3, false);
    6116           1 :     SCCOL nEndCol = 1;
    6117           1 :     SCROW nEndRow = 1;
    6118           1 :     m_pDoc->ExtendMerge( 1, 1, nEndCol, nEndRow, 0, false);
    6119           1 :     CPPUNIT_ASSERT_MESSAGE("did not merge cells", nEndCol == 3 && nEndRow == 3);
    6120           1 :     ScRange aRange(0,2,0,MAXCOL,2,0);
    6121           1 :     ScMarkData aMark;
    6122           1 :     aMark.SetMarkArea(aRange);
    6123           1 :     m_xDocShRef->GetDocFunc().InsertCells(aRange, &aMark, INS_INSROWS, true, true);
    6124           1 :     m_pDoc->ExtendMerge(1, 1, nEndCol, nEndRow, 0, false);
    6125           1 :     cout << nEndRow << nEndCol;
    6126           1 :     CPPUNIT_ASSERT_MESSAGE("did not increase merge area", nEndCol == 3 && nEndRow == 4);
    6127           1 :     m_pDoc->DeleteTab(0);
    6128           1 : }
    6129             : 
    6130             : 
    6131           1 : void Test::testRenameTable()
    6132             : {
    6133             :     //test set rename table
    6134             :     //TODO: set name1 and name2 and do an undo to check if name 1 is set now
    6135             :     //TODO: also check if new name for table is same as another table
    6136             : 
    6137           1 :     m_pDoc->InsertTab(0, "Sheet1");
    6138           1 :     m_pDoc->InsertTab(1, "Sheet2");
    6139             : 
    6140             :     //test case 1 , rename table2 to sheet 1, it should return error
    6141           1 :     OUString nameToSet = "Sheet1";
    6142           1 :     ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
    6143           1 :     CPPUNIT_ASSERT_MESSAGE("name same as another table is being set", !rDocFunc.RenameTable(1,nameToSet,false,true) );
    6144             : 
    6145             :     //test case 2 , simple rename to check name
    6146           1 :     nameToSet = "test1";
    6147           1 :     m_xDocShRef->GetDocFunc().RenameTable(0,nameToSet,false,true);
    6148           2 :     OUString nameJustSet;
    6149           1 :     m_pDoc->GetName(0,nameJustSet);
    6150           1 :     CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet == nameJustSet);
    6151             : 
    6152             :     //test case 3 , rename again
    6153           2 :     OUString anOldName;
    6154           1 :     m_pDoc->GetName(0,anOldName);
    6155             : 
    6156           1 :     nameToSet = "test2";
    6157           1 :     rDocFunc.RenameTable(0,nameToSet,false,true);
    6158           1 :     m_pDoc->GetName(0,nameJustSet);
    6159           1 :     CPPUNIT_ASSERT_MESSAGE("table not renamed", nameToSet == nameJustSet);
    6160             : 
    6161             :     //test case 4 , check if  undo works
    6162           1 :     SfxUndoAction* pUndo = new ScUndoRenameTab(m_xDocShRef,0,anOldName,nameToSet);
    6163           1 :     pUndo->Undo();
    6164           1 :     m_pDoc->GetName(0,nameJustSet);
    6165           1 :     CPPUNIT_ASSERT_MESSAGE("the correct name is not set after undo", nameJustSet == anOldName);
    6166             : 
    6167           1 :     pUndo->Redo();
    6168           1 :     m_pDoc->GetName(0,nameJustSet);
    6169           1 :     CPPUNIT_ASSERT_MESSAGE("the correct color is not set after redo", nameJustSet == nameToSet);
    6170             : 
    6171           1 :     m_pDoc->DeleteTab(0);
    6172           2 :     m_pDoc->DeleteTab(1);
    6173           1 : }
    6174             : 
    6175             : 
    6176             : 
    6177           1 : void Test::testSetBackgroundColor()
    6178             : {
    6179             :     //test set background color
    6180             :     //TODO: set color1 and set color2 and do an undo to check if color1 is set now.
    6181             : 
    6182           1 :     m_pDoc->InsertTab(0, "Sheet1");
    6183           1 :     Color aColor;
    6184             : 
    6185             :      //test yellow
    6186           1 :     aColor=Color(COL_YELLOW);
    6187           1 :     m_xDocShRef->GetDocFunc().SetTabBgColor(0,aColor,false, true);
    6188           2 :     CPPUNIT_ASSERT_MESSAGE("the correct color is not set",
    6189           1 :                            m_pDoc->GetTabBgColor(0) == aColor);
    6190             : 
    6191             : 
    6192           1 :     Color aOldTabBgColor=m_pDoc->GetTabBgColor(0);
    6193           1 :     aColor.SetColor(COL_BLUE);//set BLUE
    6194           1 :     m_xDocShRef->GetDocFunc().SetTabBgColor(0,aColor,false, true);
    6195           2 :     CPPUNIT_ASSERT_MESSAGE("the correct color is not set the second time",
    6196           1 :                            m_pDoc->GetTabBgColor(0) == aColor);
    6197             : 
    6198             :     //now check for undo
    6199           1 :     SfxUndoAction* pUndo = new ScUndoTabColor(m_xDocShRef,0, aOldTabBgColor, aColor);
    6200           1 :     pUndo->Undo();
    6201           1 :     CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aOldTabBgColor);
    6202           1 :     pUndo->Redo();
    6203           1 :     CPPUNIT_ASSERT_MESSAGE("the correct color is not set after undo", m_pDoc->GetTabBgColor(0)== aColor);
    6204           1 :     m_pDoc->DeleteTab(0);
    6205           1 : }
    6206             : 
    6207             : 
    6208             : 
    6209           1 : void Test::testUpdateReference()
    6210             : {
    6211             :     //test that formulas are correctly updated during sheet delete
    6212             :     //TODO: add tests for relative references, updating of named ranges, ...
    6213           1 :     OUString aSheet1("Sheet1");
    6214           2 :     OUString aSheet2("Sheet2");
    6215           2 :     OUString aSheet3("Sheet3");
    6216           2 :     OUString aSheet4("Sheet4");
    6217           1 :     m_pDoc->InsertTab(0, aSheet1);
    6218           1 :     m_pDoc->InsertTab(1, aSheet2);
    6219           1 :     m_pDoc->InsertTab(2, aSheet3);
    6220           1 :     m_pDoc->InsertTab(3, aSheet4);
    6221             : 
    6222           1 :     m_pDoc->SetValue(0,0,2, 1);
    6223           1 :     m_pDoc->SetValue(1,0,2, 2);
    6224           1 :     m_pDoc->SetValue(1,1,3, 4);
    6225           1 :     m_pDoc->SetString(2,0,2, OUString("=A1+B1"));
    6226           1 :     m_pDoc->SetString(2,1,2, OUString("=Sheet4.B2+A1"));
    6227             : 
    6228             :     double aValue;
    6229           1 :     m_pDoc->GetValue(2,0,2, aValue);
    6230           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 3);
    6231           1 :     m_pDoc->GetValue(2,1,2, aValue);
    6232           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("formula does not return correct result", aValue, 5);
    6233             : 
    6234             :     //test deleting both sheets: one is not directly before the sheet, the other one is
    6235           1 :     m_pDoc->DeleteTab(0);
    6236           1 :     m_pDoc->GetValue(2,0,1, aValue);
    6237           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 3);
    6238           1 :     m_pDoc->GetValue(2,1,1, aValue);
    6239           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting first sheet formula does not return correct result", aValue, 5);
    6240             : 
    6241           1 :     m_pDoc->DeleteTab(0);
    6242           1 :     m_pDoc->GetValue(2,0,0, aValue);
    6243           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 3);
    6244           1 :     m_pDoc->GetValue(2,1,0, aValue);
    6245           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting second sheet formula does not return correct result", aValue, 5);
    6246             : 
    6247             :     //test adding two sheets
    6248           1 :     m_pDoc->InsertTab(0, aSheet2);
    6249           1 :     m_pDoc->GetValue(2,0,1, aValue);
    6250           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 3);
    6251           1 :     m_pDoc->GetValue(2,1,1, aValue);
    6252           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting first sheet formula does not return correct result", aValue, 5);
    6253             : 
    6254           1 :     m_pDoc->InsertTab(0, aSheet1);
    6255           1 :     m_pDoc->GetValue(2,0,2, aValue);
    6256           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 3);
    6257           1 :     m_pDoc->GetValue(2,1,2, aValue);
    6258           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting second sheet formula does not return correct result", aValue, 5);
    6259             : 
    6260             :     //test new DeleteTabs/InsertTabs methods
    6261           1 :     m_pDoc->DeleteTabs(0, 2);
    6262           1 :     m_pDoc->GetValue(2, 0, 0, aValue);
    6263           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 3);
    6264           1 :     m_pDoc->GetValue(2, 1, 0, aValue);
    6265           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after deleting sheets formula does not return correct result", aValue, 5);
    6266             : 
    6267           2 :     std::vector<OUString> aSheets;
    6268           1 :     aSheets.push_back(aSheet1);
    6269           1 :     aSheets.push_back(aSheet2);
    6270           1 :     m_pDoc->InsertTabs(0, aSheets, false, true);
    6271           1 :     m_pDoc->GetValue(2, 0, 2, aValue);
    6272           2 :     OUString aFormula;
    6273           1 :     m_pDoc->GetFormula(2,0,2, aFormula);
    6274           1 :     std::cout << "formel: " << OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8).getStr() << std::endl;
    6275           1 :     std::cout << std::endl << aValue << std::endl;
    6276           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 3);
    6277           1 :     m_pDoc->GetValue(2, 1, 2, aValue);
    6278           1 :     ASSERT_DOUBLES_EQUAL_MESSAGE("after inserting sheets formula does not return correct result", aValue, 5);
    6279             : 
    6280           1 :     m_pDoc->DeleteTab(3);
    6281           1 :     m_pDoc->DeleteTab(2);
    6282           1 :     m_pDoc->DeleteTab(1);
    6283           2 :     m_pDoc->DeleteTab(0);
    6284           1 : }
    6285             : 
    6286           1 : void Test::testSearchCells()
    6287             : {
    6288           1 :     m_pDoc->InsertTab(0, "Test");
    6289             : 
    6290           1 :     m_pDoc->SetString(ScAddress(0,0,0), "A");
    6291           1 :     m_pDoc->SetString(ScAddress(0,1,0), "B");
    6292           1 :     m_pDoc->SetString(ScAddress(0,2,0), "A");
    6293             :     // Leave A4 blank.
    6294           1 :     m_pDoc->SetString(ScAddress(0,4,0), "A");
    6295           1 :     m_pDoc->SetString(ScAddress(0,5,0), "B");
    6296           1 :     m_pDoc->SetString(ScAddress(0,6,0), "C");
    6297             : 
    6298           1 :     SvxSearchItem aItem(SID_SEARCH_ITEM);
    6299           1 :     aItem.SetSearchString(OUString("A"));
    6300           1 :     aItem.SetCommand(SVX_SEARCHCMD_FIND_ALL);
    6301           2 :     ScMarkData aMarkData;
    6302           1 :     aMarkData.SelectOneTable(0);
    6303           1 :     SCCOL nCol = 0;
    6304           1 :     SCROW nRow = 0;
    6305           1 :     SCTAB nTab = 0;
    6306           2 :     ScRangeList aMatchedRanges;
    6307           2 :     OUString aUndoStr;
    6308           1 :     m_pDoc->SearchAndReplace(aItem, nCol, nRow, nTab, aMarkData, aMatchedRanges, aUndoStr);
    6309             : 
    6310           1 :     CPPUNIT_ASSERT_MESSAGE("There should be exactly 3 matching cells.", aMatchedRanges.size() == 3);
    6311           1 :     ScAddress aHit(0,0,0);
    6312           1 :     CPPUNIT_ASSERT_MESSAGE("A1 should be inside the matched range.", aMatchedRanges.In(aHit));
    6313           1 :     aHit.SetRow(2);
    6314           1 :     CPPUNIT_ASSERT_MESSAGE("A3 should be inside the matched range.", aMatchedRanges.In(aHit));
    6315           1 :     aHit.SetRow(4);
    6316           1 :     CPPUNIT_ASSERT_MESSAGE("A5 should be inside the matched range.", aMatchedRanges.In(aHit));
    6317             : 
    6318           2 :     m_pDoc->DeleteTab(0);
    6319           1 : }
    6320             : 
    6321           1 : void Test::testSharedFormulas()
    6322             : {
    6323           1 :     m_pDoc->InsertTab(0, "Test");
    6324             : 
    6325           1 :     ScAddress aPos(1, 9, 0); // B10
    6326           1 :     m_pDoc->SetString(aPos, "=A10*2"); // Insert into B10.
    6327           1 :     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
    6328           1 :     CPPUNIT_ASSERT_MESSAGE("Expected to be a non-shared cell.", pFC && !pFC->IsShared());
    6329             : 
    6330           1 :     aPos.SetRow(10); // B11
    6331           1 :     m_pDoc->SetString(aPos, "=A11*2");
    6332           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6333           1 :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6334           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
    6335           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
    6336             : 
    6337           1 :     aPos.SetRow(8); // B9
    6338           1 :     m_pDoc->SetString(aPos, "=A9*2");
    6339           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6340           1 :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6341           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
    6342           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
    6343             : 
    6344           1 :     aPos.SetRow(12); // B13
    6345           1 :     m_pDoc->SetString(aPos, "=A13*2");
    6346           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6347           1 :     CPPUNIT_ASSERT_MESSAGE("This formula cell shouldn't be shared yet.", pFC && !pFC->IsShared());
    6348             : 
    6349             :     // Insert a formula to B12, and B9:B13 should be shared.
    6350           1 :     aPos.SetRow(11); // B12
    6351           1 :     m_pDoc->SetString(aPos, "=A12*2");
    6352           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6353           1 :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6354           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
    6355           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), pFC->GetSharedLength());
    6356             : 
    6357             :     // Insert formulas to B15:B16.
    6358           1 :     aPos.SetRow(14); // B15
    6359           1 :     m_pDoc->SetString(aPos, "=A15*2");
    6360           1 :     aPos.SetRow(15); // B16
    6361           1 :     m_pDoc->SetString(aPos, "=A16*2");
    6362           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6363           1 :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6364           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(14), pFC->GetSharedTopRow());
    6365           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
    6366             : 
    6367             :     // Insert a formula to B14, and B9:B16 should be shared.
    6368           1 :     aPos.SetRow(13); // B14
    6369           1 :     m_pDoc->SetString(aPos, "=A14*2");
    6370           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6371           1 :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6372           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
    6373           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
    6374             : 
    6375             :     // Insert an incompatible formula to B12, to split the shared range to B9:B11 and B13:B16.
    6376           1 :     aPos.SetRow(11); // B12
    6377           1 :     m_pDoc->SetString(aPos, "=$A$1*4");
    6378           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6379           1 :     CPPUNIT_ASSERT_MESSAGE("This cell shouldn't be shared.", pFC && !pFC->IsShared());
    6380             : 
    6381           1 :     aPos.SetRow(8); // B9
    6382           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6383           1 :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6384           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedTopRow());
    6385           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
    6386             : 
    6387           1 :     aPos.SetRow(12); // B13
    6388           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6389           1 :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6390           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
    6391           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(4), pFC->GetSharedLength());
    6392             : 
    6393             :     // Extend B13:B16 to B13:B20.
    6394           1 :     aPos.SetRow(16); // B17
    6395           1 :     m_pDoc->SetString(aPos, "=A17*2");
    6396           1 :     aPos.IncRow();
    6397           1 :     m_pDoc->SetString(aPos, "=A18*2");
    6398           1 :     aPos.IncRow();
    6399           1 :     m_pDoc->SetString(aPos, "=A19*2");
    6400           1 :     aPos.IncRow();
    6401           1 :     m_pDoc->SetString(aPos, "=A20*2");
    6402           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
    6403           1 :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6404           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
    6405           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(8), pFC->GetSharedLength());
    6406             : 
    6407             : #if 0
    6408             :     // Insert empty rows at B16 to split B13:B20 into B13:B15 and B21:B25.
    6409             :     m_pDoc->InsertRow(1, 0, 1, 0, 15, 5);
    6410             : 
    6411             :     aPos.SetRow(12); // B13
    6412             :     pFC = m_pDoc->GetFormulaCell(aPos);
    6413             :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6414             :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), pFC->GetSharedTopRow());
    6415             :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), pFC->GetSharedLength());
    6416             : 
    6417             :     aPos.SetRow(23); // B24
    6418             :     pFC = m_pDoc->GetFormulaCell(aPos);
    6419             :     CPPUNIT_ASSERT_MESSAGE("This cell is expected to be a shared formula cell.", pFC && pFC->IsShared());
    6420             :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(20), pFC->GetSharedTopRow());
    6421             :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), pFC->GetSharedLength());
    6422             : #endif
    6423             : 
    6424           1 :     m_pDoc->DeleteTab(0);
    6425           1 : }
    6426             : 
    6427             : namespace {
    6428             : 
    6429           6 : bool checkFormulaPosition(ScDocument& rDoc, const ScAddress& rPos)
    6430             : {
    6431           6 :     OUString aStr;
    6432           6 :     rPos.Format(aStr, SCA_VALID);
    6433           6 :     const ScFormulaCell* pFC = rDoc.GetFormulaCell(rPos);
    6434           6 :     if (!pFC)
    6435             :     {
    6436           0 :         cerr << "Formula cell expected at " << aStr << " but not found." << endl;
    6437           0 :         return false;
    6438             :     }
    6439             : 
    6440           6 :     if (pFC->aPos != rPos)
    6441             :     {
    6442           0 :         OUString aStr2;
    6443           0 :         pFC->aPos.Format(aStr2, SCA_VALID);
    6444           0 :         cerr << "Formula cell at " << aStr << " has incorrect position of " << aStr2 << endl;
    6445           0 :         return false;
    6446             :     }
    6447             : 
    6448           6 :     return true;
    6449             : }
    6450             : 
    6451           2 : void checkFormulaPositions(ScDocument& rDoc, const ScAddress& rPos, const SCROW* pRows, size_t nRowCount)
    6452             : {
    6453           2 :     ScAddress aPos = rPos;
    6454           8 :     for (size_t i = 0; i < nRowCount; ++i)
    6455             :     {
    6456           6 :         SCROW nRow = pRows[i];
    6457           6 :         aPos.SetRow(nRow);
    6458             : 
    6459           6 :         if (!checkFormulaPosition(rDoc, aPos))
    6460             :         {
    6461           0 :             OUString aStr;
    6462           0 :             aPos.Format(aStr, SCA_VALID);
    6463           0 :             std::ostringstream os;
    6464           0 :             os << "Formula cell position failed at " << aStr;
    6465           0 :             CPPUNIT_FAIL(os.str().c_str());
    6466             :         }
    6467             :     }
    6468           2 : }
    6469             : 
    6470             : }
    6471             : 
    6472           1 : void Test::testFormulaPosition()
    6473             : {
    6474           1 :     m_pDoc->InsertTab(0, "Test");
    6475             : 
    6476           1 :     ScAddress aPos(0,0,0); // A1
    6477           1 :     m_pDoc->SetString(aPos, "=ROW()");
    6478           1 :     aPos.IncRow(); // A2
    6479           1 :     m_pDoc->SetString(aPos, "=ROW()");
    6480           1 :     aPos.SetRow(3); // A4;
    6481           1 :     m_pDoc->SetString(aPos, "=ROW()");
    6482             : 
    6483             :     {
    6484           1 :         SCROW aRows[] = { 0, 1, 3 };
    6485           1 :         checkFormulaPositions(*m_pDoc, aPos, aRows, SAL_N_ELEMENTS(aRows));
    6486             :     }
    6487             : 
    6488           1 :     m_pDoc->InsertRow(0,0,0,0,1,5); // Insert 5 rows at A2.
    6489             :     {
    6490           1 :         SCROW aRows[] = { 0, 6, 8 };
    6491           1 :         checkFormulaPositions(*m_pDoc, aPos, aRows, SAL_N_ELEMENTS(aRows));
    6492             :     }
    6493             : 
    6494           1 :     m_pDoc->DeleteTab(0);
    6495           1 : }
    6496             : 
    6497             : namespace {
    6498             : 
    6499           4 : bool hasRange(const std::vector<ScTokenRef>& rRefTokens, const ScRange& rRange)
    6500             : {
    6501           4 :     std::vector<ScTokenRef>::const_iterator it = rRefTokens.begin(), itEnd = rRefTokens.end();
    6502           5 :     for (; it != itEnd; ++it)
    6503             :     {
    6504           5 :         const ScTokenRef& p = *it;
    6505           5 :         if (!ScRefTokenHelper::isRef(p) || ScRefTokenHelper::isExternalRef(p))
    6506           0 :             continue;
    6507             : 
    6508           5 :         switch (p->GetType())
    6509             :         {
    6510             :             case formula::svSingleRef:
    6511             :             {
    6512           2 :                 ScSingleRefData aData = p->GetSingleRef();
    6513           2 :                 if (rRange.aStart != rRange.aEnd)
    6514           0 :                     break;
    6515             : 
    6516           2 :                 ScAddress aThis(aData.nCol, aData.nRow, aData.nTab);
    6517           2 :                 if (aThis == rRange.aStart)
    6518           2 :                     return true;
    6519             :             }
    6520           0 :             break;
    6521             :             case formula::svDoubleRef:
    6522             :             {
    6523           3 :                 ScComplexRefData aData = p->GetDoubleRef();
    6524           3 :                 ScRange aThis(aData.Ref1.nCol, aData.Ref1.nRow, aData.Ref1.nTab, aData.Ref2.nCol, aData.Ref2.nRow, aData.Ref2.nTab);
    6525           3 :                 if (aThis == rRange)
    6526           2 :                     return true;
    6527             :             }
    6528           1 :             break;
    6529             :             default:
    6530             :                 ;
    6531             :         }
    6532             :     }
    6533           0 :     return false;
    6534             : }
    6535             : 
    6536             : }
    6537             : 
    6538           1 : void Test::testJumpToPrecedentsDependents()
    6539             : {
    6540             :     // Precedent is another cell that the cell references, while dependent is
    6541             :     // another cell that references it.
    6542           1 :     m_pDoc->InsertTab(0, OUString("Test"));
    6543             : 
    6544           1 :     m_pDoc->SetString(2, 0, 0, OUString("=A1+A2+B3")); // C1
    6545           1 :     m_pDoc->SetString(2, 1, 0, OUString("=A1"));       // C2
    6546           1 :     m_pDoc->CalcAll();
    6547             : 
    6548           1 :     std::vector<ScTokenRef> aRefTokens;
    6549           1 :     ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
    6550             : 
    6551             :     {
    6552             :         // C1's precedent should be A1:A2,B3.
    6553           1 :         ScRangeList aRange(ScRange(2, 0, 0));
    6554           1 :         rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
    6555           2 :         CPPUNIT_ASSERT_MESSAGE("A1:A2 should be a precedent of C1.",
    6556           1 :                                hasRange(aRefTokens, ScRange(0, 0, 0, 0, 1, 0)));
    6557           2 :         CPPUNIT_ASSERT_MESSAGE("B3 should be a precedent of C1.",
    6558           2 :                                hasRange(aRefTokens, ScRange(1, 2, 0)));
    6559             :     }
    6560             : 
    6561             :     {
    6562             :         // C2's precedent should be A1 only.
    6563           1 :         ScRangeList aRange(ScRange(2, 1, 0));
    6564           1 :         rDocFunc.DetectiveCollectAllPreds(aRange, aRefTokens);
    6565           2 :         CPPUNIT_ASSERT_EQUAL_MESSAGE("there should only be one reference token.",
    6566           1 :                                aRefTokens.size(), static_cast<size_t>(1));
    6567           2 :         CPPUNIT_ASSERT_MESSAGE("A1 should be a precedent of C1.",
    6568           2 :                                hasRange(aRefTokens, ScRange(0, 0, 0)));
    6569             :     }
    6570             : 
    6571             :     {
    6572             :         // A1's dependent should be C1:C2.
    6573           1 :         ScRangeList aRange(ScRange(0, 0, 0));
    6574           1 :         rDocFunc.DetectiveCollectAllSuccs(aRange, aRefTokens);
    6575           2 :         CPPUNIT_ASSERT_MESSAGE("C1:C2 should be the only dependent of A1.",
    6576           2 :                                aRefTokens.size() == 1 && hasRange(aRefTokens, ScRange(2, 0, 0, 2, 1, 0)));
    6577             :     }
    6578             : 
    6579           1 :     m_pDoc->DeleteTab(0);
    6580           1 : }
    6581             : 
    6582           1 : void Test::testAutoFill()
    6583             : {
    6584           1 :     m_pDoc->InsertTab(0, "test");
    6585             : 
    6586           1 :     m_pDoc->SetValue(0,0,0,1);
    6587             : 
    6588           1 :     ScMarkData aMarkData;
    6589           1 :     aMarkData.SelectTable(0, true);
    6590             : 
    6591           1 :     m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 5);
    6592           7 :     for (SCROW i = 0; i< 6; ++i)
    6593           6 :         ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1.0), m_pDoc->GetValue(0, i, 0));
    6594             : 
    6595             :     // check that hidden rows are not affected by autofill
    6596             :     // set values for hidden rows
    6597           1 :     m_pDoc->SetValue(0,1,0,10);
    6598           1 :     m_pDoc->SetValue(0,2,0,10);
    6599             : 
    6600           1 :     m_pDoc->SetRowHidden(1, 2, 0, true);
    6601           1 :     m_pDoc->Fill( 0, 0, 0, 0, NULL, aMarkData, 8);
    6602             : 
    6603           1 :     ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,1,0));
    6604           1 :     ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,2,0));
    6605           6 :     for (SCROW i = 3; i< 8; ++i)
    6606           5 :         ASSERT_DOUBLES_EQUAL(static_cast<double>(i-1.0), m_pDoc->GetValue(0, i, 0));
    6607             : 
    6608           1 :     m_pDoc->Fill( 0, 0, 0, 8, NULL, aMarkData, 5, FILL_TO_RIGHT );
    6609           6 :     for (SCCOL i = 0; i < 5; ++i)
    6610             :     {
    6611          45 :         for(SCROW j = 0; j < 8; ++j)
    6612             :         {
    6613          40 :             if (j > 2)
    6614             :             {
    6615          25 :                 ASSERT_DOUBLES_EQUAL(static_cast<double>(j-1+i), m_pDoc->GetValue(i, j, 0));
    6616             :             }
    6617          15 :             else if (j == 0)
    6618             :             {
    6619           5 :                 ASSERT_DOUBLES_EQUAL(static_cast<double>(i+1), m_pDoc->GetValue(i, 0, 0));
    6620             :             }
    6621          10 :             else if (j == 1 || j== 2)
    6622             :             {
    6623          10 :                 if(i == 0)
    6624           2 :                     ASSERT_DOUBLES_EQUAL(10.0, m_pDoc->GetValue(0,j,0));
    6625             :                 else
    6626           8 :                     ASSERT_DOUBLES_EQUAL(0.0, m_pDoc->GetValue(i,j,0));
    6627             :             }
    6628             :         }
    6629             :     }
    6630             : 
    6631             :     // test auto fill user data lists
    6632           1 :     m_pDoc->SetString( 0, 100, 0, "January" );
    6633           1 :     m_pDoc->Fill( 0, 100, 0, 100, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
    6634           2 :     OUString aTestValue = m_pDoc->GetString( 0, 101, 0 );
    6635           1 :     CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("February") );
    6636           1 :     aTestValue = m_pDoc->GetString( 0, 102, 0 );
    6637           1 :     CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("March") );
    6638             : 
    6639             :     // test that two same user data list entries will not result in incremental fill
    6640           1 :     m_pDoc->SetString( 0, 101, 0, "January" );
    6641           1 :     m_pDoc->Fill( 0, 100, 0, 101, NULL, aMarkData, 2, FILL_TO_BOTTOM, FILL_AUTO );
    6642           3 :     for ( SCROW i = 102; i <= 103; ++i )
    6643             :     {
    6644           2 :         aTestValue = m_pDoc->GetString( 0, i, 0 );
    6645           2 :         CPPUNIT_ASSERT_EQUAL( aTestValue, OUString("January") );
    6646             :     }
    6647           2 :     m_pDoc->DeleteTab(0);
    6648           1 : }
    6649             : 
    6650           1 : void Test::testCopyPasteFormulas()
    6651             : {
    6652           1 :     m_pDoc->InsertTab(0, "Sheet1");
    6653           1 :     m_pDoc->InsertTab(1, "Sheet2");
    6654             : 
    6655           1 :     m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
    6656           1 :     m_pDoc->SetString(0,1,0, "=$A$1+B2" );
    6657           1 :     m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
    6658           1 :     m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
    6659           1 :     m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
    6660             : 
    6661             :     // to prevent ScEditableTester in ScDocFunc::MoveBlock
    6662           1 :     ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,0,0), 1.0);
    6663           1 :     ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(0,1,0), 1.0);
    6664           1 :     ScDocFunc& rDocFunc = m_xDocShRef->GetDocFunc();
    6665           1 :     bool bMoveDone = rDocFunc.MoveBlock(ScRange(0,0,0,0,4,0), ScAddress( 10, 10, 0), false, false, false, true);
    6666             : 
    6667             :     // check that moving was succesful, mainly for editable tester
    6668           1 :     CPPUNIT_ASSERT(bMoveDone);
    6669           1 :     ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,10,0), 1.0);
    6670           1 :     ASSERT_DOUBLES_EQUAL(m_pDoc->GetValue(10,11,0), 1.0);
    6671           1 :     OUString aFormula;
    6672           1 :     m_pDoc->GetFormula(10,10,0, aFormula);
    6673           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=COLUMN($A$1)"));
    6674           1 :     m_pDoc->GetFormula(10,11,0, aFormula);
    6675           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$A$1+L12"));
    6676           1 :     m_pDoc->GetFormula(10,12,0, aFormula);
    6677           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.K11"));
    6678           1 :     m_pDoc->GetFormula(10,13,0, aFormula);
    6679           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.$A$1"));
    6680           1 :     m_pDoc->GetFormula(10,14,0, aFormula);
    6681           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$Sheet2.K$1"));
    6682           1 : }
    6683             : 
    6684           1 : void Test::testCopyPasteFormulasExternalDoc()
    6685             : {
    6686           1 :     OUString aDocName("file:///source.fake");
    6687           1 :     SfxMedium* pMedium = new SfxMedium(aDocName, STREAM_STD_READWRITE);
    6688           1 :     m_xDocShRef->DoInitNew(pMedium);
    6689           1 :     m_pDoc = m_xDocShRef->GetDocument();
    6690             : 
    6691           2 :     ScDocShellRef xExtDocSh = new ScDocShell;
    6692           2 :     OUString aExtDocName("file:///extdata.fake");
    6693           2 :     OUString aExtSh1Name("ExtSheet1");
    6694           2 :     OUString aExtSh2Name("ExtSheet2");
    6695           1 :     SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
    6696           1 :     xExtDocSh->DoInitNew(pMed);
    6697           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
    6698           1 :                            findLoadedDocShellByName(aExtDocName) != NULL);
    6699             : 
    6700           1 :     ScDocument* pExtDoc = xExtDocSh->GetDocument();
    6701           1 :     pExtDoc->InsertTab(0, aExtSh1Name);
    6702           1 :     pExtDoc->InsertTab(1, aExtSh2Name);
    6703             : 
    6704           1 :     m_pDoc->InsertTab(0, "Sheet1");
    6705           1 :     m_pDoc->InsertTab(1, "Sheet2");
    6706             : 
    6707           1 :     m_pDoc->SetString(0,0,0, "=COLUMN($A$1)");
    6708           1 :     m_pDoc->SetString(0,1,0, "=$A$1+B2" );
    6709           1 :     m_pDoc->SetString(0,2,0, "=$Sheet2.A1");
    6710           1 :     m_pDoc->SetString(0,3,0, "=$Sheet2.$A$1");
    6711           1 :     m_pDoc->SetString(0,4,0, "=$Sheet2.A$1");
    6712           1 :     m_pDoc->SetString(0,5,0, "=$Sheet1.$A$1");
    6713             : 
    6714           1 :     ScRange aRange(0,0,0,0,5,0);
    6715           2 :     ScClipParam aClipParam(aRange, false);
    6716           2 :     ScMarkData aMark;
    6717           1 :     aMark.SetMarkArea(aRange);
    6718           1 :     ScDocument* pClipDoc = new ScDocument(SCDOCMODE_CLIP);
    6719           1 :     m_pDoc->CopyToClip(aClipParam, pClipDoc, &aMark);
    6720             : 
    6721           1 :     sal_uInt16 nFlags = IDF_ALL;
    6722           1 :     aRange = ScRange(1,1,1,1,6,1);
    6723           2 :     ScMarkData aMarkData2;
    6724           1 :     aMarkData2.SetMarkArea(aRange);
    6725           1 :     pExtDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, pClipDoc);
    6726             : 
    6727           2 :     OUString aFormula;
    6728           1 :     pExtDoc->GetFormula(1,1,1, aFormula);
    6729             :     //adjust absolute refs pointing to the copy area
    6730           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=COLUMN($B$2)"));
    6731           1 :     pExtDoc->GetFormula(1,2,1, aFormula);
    6732             :     //adjust absolute refs and keep relative refs
    6733           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$B$2+C3"));
    6734           1 :     pExtDoc->GetFormula(1,3,1, aFormula);
    6735             :     // make absolute sheet refs external refs
    6736           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.B2"));
    6737           1 :     pExtDoc->GetFormula(1,4,1, aFormula);
    6738           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.$A$1"));
    6739           1 :     pExtDoc->GetFormula(1,5,1, aFormula);
    6740           1 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("='file:///source.fake'#$Sheet2.B$1"));
    6741           1 :     pExtDoc->GetFormula(1,6,1, aFormula);
    6742           2 :     CPPUNIT_ASSERT_EQUAL(aFormula, OUString("=$ExtSheet2.$B$2"));
    6743           1 : }
    6744             : 
    6745           1 : void Test::testFindAreaPosVertical()
    6746             : {
    6747             :     const char* aData[][3] = {
    6748             :         {   0, "1", "1" },
    6749             :         { "1",   0, "1" },
    6750             :         { "1", "1", "1" },
    6751             :         {   0, "1", "1" },
    6752             :         { "1", "1", "1" },
    6753             :         { "1",   0, "1" },
    6754             :         { "1", "1", "1" },
    6755           1 :     };
    6756             : 
    6757           1 :     m_pDoc->InsertTab(0, "Test1");
    6758           1 :     clearRange( m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
    6759           1 :     ScAddress aPos(0,0,0);
    6760           1 :     ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    6761           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    6762             : 
    6763           1 :     m_pDoc->SetRowHidden(4,4,0,true);
    6764           1 :     bool bHidden = m_pDoc->RowHidden(4,0);
    6765           1 :     CPPUNIT_ASSERT(bHidden);
    6766             : 
    6767           1 :     SCCOL nCol = 0;
    6768           1 :     SCROW nRow = 0;
    6769           1 :     m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
    6770             : 
    6771           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
    6772           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
    6773             : 
    6774           1 :     m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
    6775             : 
    6776           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRow);
    6777           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
    6778             : 
    6779           1 :     m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
    6780             : 
    6781           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(5), nRow);
    6782           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
    6783             : 
    6784           1 :     m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
    6785             : 
    6786           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
    6787           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
    6788             : 
    6789           1 :     m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
    6790             : 
    6791           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(MAXROW), nRow);
    6792           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(0), nCol);
    6793             : 
    6794           1 :     nCol = 1;
    6795           1 :     nRow = 2;
    6796             : 
    6797           1 :     m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
    6798             : 
    6799           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRow);
    6800           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
    6801             : 
    6802           1 :     m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_DOWN);
    6803             : 
    6804           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(6), nRow);
    6805           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
    6806             : 
    6807           1 :     nCol = 2;
    6808           1 :     nRow = 6;
    6809           1 :     m_pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_UP);
    6810           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
    6811           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
    6812             : 
    6813             : 
    6814           1 :     m_pDoc->DeleteTab(0);
    6815           1 : }
    6816             : 
    6817           1 : void Test::testFindAreaPosColRight()
    6818             : {
    6819             :     const char* aData[][7] = {
    6820             :         { "", "1", "1", "", "1", "1", "1" },
    6821           1 :         { "", "", "1", "1", "1", "", "1" }, };
    6822             : 
    6823           1 :     ScDocument* pDoc = m_xDocShRef->GetDocument();
    6824           1 :     OUString aTabName1("test1");
    6825           1 :     pDoc->InsertTab(0, aTabName1);
    6826           1 :     clearRange( pDoc, ScRange(0, 0, 0, 7, SAL_N_ELEMENTS(aData), 0));
    6827           1 :     ScAddress aPos(0,0,0);
    6828           1 :     ScRange aDataRange = insertRangeData( pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    6829           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    6830             : 
    6831           1 :     pDoc->SetColHidden(4,4,0,true);
    6832           1 :     bool bHidden = pDoc->ColHidden(4,0);
    6833           1 :     CPPUNIT_ASSERT(bHidden);
    6834             : 
    6835           1 :     SCCOL nCol = 0;
    6836           1 :     SCROW nRow = 0;
    6837           1 :     pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
    6838             : 
    6839           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
    6840           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCol);
    6841             : 
    6842           1 :     pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
    6843             : 
    6844           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
    6845           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), nCol);
    6846             : 
    6847           1 :     pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
    6848             : 
    6849           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
    6850           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(5), nCol);
    6851             : 
    6852           1 :     pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
    6853             : 
    6854           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
    6855           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
    6856             : 
    6857           1 :     pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
    6858             : 
    6859           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), nRow);
    6860           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(MAXCOL), nCol);
    6861             : 
    6862           1 :     nCol = 2;
    6863           1 :     nRow = 1;
    6864             : 
    6865           1 :     pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
    6866             : 
    6867           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
    6868           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), nCol);
    6869             : 
    6870           1 :     pDoc->FindAreaPos(nCol, nRow, 0, SC_MOVE_RIGHT);
    6871             : 
    6872           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), nRow);
    6873           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(6), nCol);
    6874             : 
    6875           1 :     pDoc->DeleteTab(0);
    6876           1 : }
    6877             : 
    6878             : // regression test fo fdo#53814, sorting doens't work as expected
    6879             : // if cells in the sort are referenced by formulas
    6880           1 : void Test::testSortWithFormulaRefs()
    6881             : {
    6882           1 :     ScDocument* pDoc = m_xDocShRef->GetDocument();
    6883           1 :     OUString aTabName1("List1");
    6884           2 :     OUString aTabName2("List2");
    6885           1 :     pDoc->InsertTab(0, aTabName1);
    6886           1 :     pDoc->InsertTab(1, aTabName2);
    6887             : 
    6888             :     const char* aFormulaData[6] = {
    6889             :         "=IF($List1.A2<>\"\",$List1.A2,\"\")",
    6890             :         "=IF($List1.A3<>\"\",$List1.A3,\"\")",
    6891             :         "=IF($List1.A4<>\"\",$List1.A4,\"\")",
    6892             :         "=IF($List1.A5<>\"\",$List1.A5,\"\")",
    6893             :         "=IF($List1.A6<>\"\",$List1.A6,\"\")",
    6894             :         "=IF($List1.A7<>\"\",$List1.A7,\"\")",
    6895           1 :     };
    6896             : 
    6897             :     const char* aTextData[4] = {
    6898             :         "bob",
    6899             :         "tim",
    6900             :         "brian",
    6901             :         "larry",
    6902           1 :     };
    6903             : 
    6904             :     const char* aResults[ 6 ] = {
    6905             :         "bob",
    6906             :         "brian",
    6907             :         "larry",
    6908             :         "tim",
    6909             :         "",
    6910             :         "",
    6911           1 :     };
    6912             :     // insert data to sort
    6913           1 :     SCROW nStart = 1, nEnd = 4;
    6914           5 :     for ( SCROW i = nStart; i <= nEnd; ++i )
    6915           4 :         pDoc->SetString( 0, i, 0, OUString::createFromAscii(aTextData[i-1]) );
    6916             :     // insert forumulas
    6917           1 :     nStart = 0;
    6918           1 :     nEnd = SAL_N_ELEMENTS(aFormulaData);
    6919           7 :     for ( SCROW i = nStart; i < nEnd; ++i )
    6920           6 :         pDoc->SetString( 0, i, 1, OUString::createFromAscii(aFormulaData[i]) );
    6921             : 
    6922           2 :     ScSortParam aSortData;
    6923           1 :     aSortData.nCol1 = 0;
    6924           1 :     aSortData.nCol2 = 0;
    6925           1 :     aSortData.nRow1 = 1;
    6926           1 :     aSortData.nRow2 = 7;
    6927           1 :     aSortData.maKeyState[0].bDoSort = true;
    6928           1 :     aSortData.maKeyState[0].nField = 0;
    6929             : 
    6930           1 :     pDoc->Sort(0, aSortData, false, NULL);
    6931             : 
    6932           1 :     nEnd = SAL_N_ELEMENTS( aResults );
    6933           7 :     for ( SCROW i = nStart; i < nEnd; ++i )
    6934             :     {
    6935           6 :         OUString sResult = pDoc->GetString( 0, i + 1, 0);
    6936           6 :         CPPUNIT_ASSERT_EQUAL( OUString::createFromAscii( aResults[ i ] ), sResult );
    6937           6 :     }
    6938           1 :     pDoc->DeleteTab(0);
    6939           2 :     pDoc->DeleteTab(1);
    6940           1 : }
    6941             : 
    6942           1 : void Test::testSort()
    6943             : {
    6944           1 :     OUString aTabName1("test1");
    6945           1 :     m_pDoc->InsertTab(0, aTabName1);
    6946             : 
    6947           1 :     ScRange aDataRange;
    6948           1 :     ScAddress aPos(0,0,0);
    6949             :     {
    6950             :         const char* aData[][2] = {
    6951             :             { "2", "4" },
    6952             :             { "4", "1" },
    6953             :             { "1", "2" },
    6954             :             { "1", "23" },
    6955           1 :         };
    6956             : 
    6957           1 :         clearRange(m_pDoc, ScRange(0, 0, 0, 1, SAL_N_ELEMENTS(aData), 0));
    6958           1 :         aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    6959           1 :         CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    6960             :     }
    6961             : 
    6962             :     // Insert note in cell B2.
    6963           2 :     OUString aHello("Hello");
    6964           2 :     OUString aJimBob("Jim Bob");
    6965           1 :     ScAddress rAddr(1, 1, 0);
    6966           1 :     ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
    6967           1 :     pNote->SetText(rAddr, aHello);
    6968           1 :     pNote->SetAuthor(aJimBob);
    6969             : 
    6970           2 :     ScSortParam aSortData;
    6971           1 :     aSortData.nCol1 = 1;
    6972           1 :     aSortData.nCol2 = 1;
    6973           1 :     aSortData.nRow1 = 0;
    6974           1 :     aSortData.nRow2 = 2;
    6975           1 :     aSortData.maKeyState[0].bDoSort = true;
    6976           1 :     aSortData.maKeyState[0].nField = 1;
    6977           1 :     aSortData.maKeyState[0].bAscending = true;
    6978             : 
    6979           1 :     m_pDoc->Sort(0, aSortData, false, NULL);
    6980           1 :     double nVal = m_pDoc->GetValue(1,0,0);
    6981           1 :     ASSERT_DOUBLES_EQUAL(nVal, 1.0);
    6982             : 
    6983             :     // check that note is also moved
    6984           1 :     pNote = m_pDoc->GetNotes(0)->findByAddress( 1, 0 );
    6985           1 :     CPPUNIT_ASSERT(pNote);
    6986             : 
    6987           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 1, 9, 0)); // Clear A1:B10.
    6988             :     {
    6989             :         // 0 = empty cell
    6990             :         const char* aData[][1] = {
    6991             :             { "Title" },
    6992             :             { 0 },
    6993             :             { 0 },
    6994             :             { "12" },
    6995             :             { "b" },
    6996             :             { "1" },
    6997             :             { "9" },
    6998             :             { "123" }
    6999           1 :         };
    7000             : 
    7001           1 :         aDataRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    7002           1 :         CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    7003             :     }
    7004             : 
    7005           1 :     aSortData.nCol1 = aDataRange.aStart.Col();
    7006           1 :     aSortData.nCol2 = aDataRange.aEnd.Col();
    7007           1 :     aSortData.nRow1 = aDataRange.aStart.Row();
    7008           1 :     aSortData.nRow2 = aDataRange.aEnd.Row();
    7009           1 :     aSortData.bHasHeader = true;
    7010           1 :     aSortData.maKeyState[0].nField = 0;
    7011           1 :     m_pDoc->Sort(0, aSortData, false, NULL);
    7012             : 
    7013             :     // Title should stay at the top, numbers should be sorted numerically,
    7014             :     // numbers always come before strings, and empty cells always occur at the
    7015             :     // end.
    7016           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Title"), m_pDoc->GetString(aPos));
    7017           1 :     aPos.IncRow();
    7018           1 :     CPPUNIT_ASSERT_EQUAL(OUString("1"), m_pDoc->GetString(aPos));
    7019           1 :     aPos.IncRow();
    7020           1 :     CPPUNIT_ASSERT_EQUAL(OUString("9"), m_pDoc->GetString(aPos));
    7021           1 :     aPos.IncRow();
    7022           1 :     CPPUNIT_ASSERT_EQUAL(OUString("12"), m_pDoc->GetString(aPos));
    7023           1 :     aPos.IncRow();
    7024           1 :     CPPUNIT_ASSERT_EQUAL(OUString("123"), m_pDoc->GetString(aPos));
    7025           1 :     aPos.IncRow();
    7026           1 :     CPPUNIT_ASSERT_EQUAL(OUString("b"), m_pDoc->GetString(aPos));
    7027           1 :     aPos.IncRow();
    7028           1 :     CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPos));
    7029             : 
    7030           2 :     m_pDoc->DeleteTab(0);
    7031           1 : }
    7032             : 
    7033           1 : void Test::testShiftCells()
    7034             : {
    7035           1 :     m_pDoc->InsertTab(0, "foo");
    7036             : 
    7037           1 :     OUString aTestVal("Some Text");
    7038             : 
    7039             :     // Text into cell E5.
    7040           1 :     m_pDoc->SetString(4, 3, 0, aTestVal);
    7041             : 
    7042             :     // Insert cell at D5. This should shift the string cell to right.
    7043           1 :     m_pDoc->InsertCol(3, 0, 3, 0, 3, 1);
    7044           2 :     OUString aStr = m_pDoc->GetString(5, 3, 0);
    7045           1 :     CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
    7046           1 :     CPPUNIT_ASSERT_MESSAGE("D5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 3, 4, 3, 4));
    7047             : 
    7048             :     // Delete cell D5, to shift the text cell back into D5.
    7049           1 :     m_pDoc->DeleteCol(3, 0, 3, 0, 3, 1);
    7050           1 :     aStr = m_pDoc->GetString(4, 3, 0);
    7051           1 :     CPPUNIT_ASSERT_MESSAGE("We should have a string cell here.", aStr == aTestVal);
    7052           1 :     CPPUNIT_ASSERT_MESSAGE("E5 is supposed to be blank.", m_pDoc->IsBlockEmpty(0, 4, 4, 4, 4));
    7053             : 
    7054           2 :     m_pDoc->DeleteTab(0);
    7055           1 : }
    7056             : 
    7057           1 : void Test::testDeleteRow()
    7058             : {
    7059           1 :     ScDocument* pDoc = m_xDocShRef->GetDocument();
    7060           1 :     OUString aSheet1("Sheet1");
    7061           1 :     pDoc->InsertTab(0, aSheet1);
    7062             : 
    7063           2 :     OUString aHello("Hello");
    7064           2 :     OUString aJimBob("Jim Bob");
    7065           1 :     ScAddress rAddr(1, 1, 0);
    7066           1 :     ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
    7067           1 :     pNote->SetText(rAddr, aHello);
    7068           1 :     pNote->SetAuthor(aJimBob);
    7069             : 
    7070           1 :     pDoc->DeleteRow(0, 0, MAXCOL, 0, 1, 1);
    7071             : 
    7072           1 :     CPPUNIT_ASSERT(m_pDoc->GetNotes(0)->empty());
    7073           2 :     pDoc->DeleteTab(0);
    7074           1 : }
    7075             : 
    7076           1 : void Test::testDeleteCol()
    7077             : {
    7078           1 :     ScDocument* pDoc = m_xDocShRef->GetDocument();
    7079           1 :     OUString aSheet1("Sheet1");
    7080           1 :     pDoc->InsertTab(0, aSheet1);
    7081             : 
    7082           2 :     OUString aHello("Hello");
    7083           2 :     OUString aJimBob("Jim Bob");
    7084           1 :     ScAddress rAddr(1, 1, 0);
    7085           1 :     ScPostIt* pNote = m_pDoc->GetNotes(rAddr.Tab())->GetOrCreateNote(rAddr);
    7086           1 :     pNote->SetText(rAddr, aHello);
    7087           1 :     pNote->SetAuthor(aJimBob);
    7088             : 
    7089           1 :     pDoc->DeleteCol(0, 0, MAXROW, 0, 1, 1);
    7090             : 
    7091           1 :     CPPUNIT_ASSERT(m_pDoc->GetNotes(0)->empty());
    7092           2 :     pDoc->DeleteTab(0);
    7093           1 : }
    7094             : 
    7095           1 : void Test::testAnchoredRotatedShape()
    7096             : {
    7097           1 :     OUString aTabName("TestTab");
    7098           1 :     m_pDoc->InsertTab(0, aTabName);
    7099             :     SCROW nRow1, nRow2;
    7100           1 :     bool bHidden = m_pDoc->RowHidden(0, 0, &nRow1, &nRow2);
    7101           1 :     CPPUNIT_ASSERT_MESSAGE("new sheet should have all rows visible", !bHidden && nRow1 == 0 && nRow2 == MAXROW);
    7102             : 
    7103           1 :     m_pDoc->InitDrawLayer();
    7104           1 :     ScDrawLayer *pDrawLayer = m_pDoc->GetDrawLayer();
    7105           1 :     CPPUNIT_ASSERT_MESSAGE("must have a draw layer", pDrawLayer != NULL);
    7106           1 :     SdrPage* pPage = pDrawLayer->GetPage(0);
    7107           1 :     CPPUNIT_ASSERT_MESSAGE("must have a draw page", pPage != NULL);
    7108           1 :     m_pDoc->SetRowHeightRange( 0, MAXROW, 0, sc::HMMToTwips( 1000 ) );
    7109           1 :     const long TOLERANCE = 30; //30 hmm
    7110        1024 :     for ( SCCOL nCol = 0; nCol < MAXCOL; ++nCol )
    7111        1023 :         m_pDoc->SetColWidth( nCol, 0, sc::HMMToTwips( 1000 ) );
    7112             :     {
    7113             :         //Add a rect
    7114           1 :         Rectangle aRect( 4000, 5000, 10000, 7000 );
    7115             : 
    7116           1 :         Rectangle aRotRect( 6000, 3000, 8000, 9000 );
    7117           1 :         SdrRectObj *pObj = new SdrRectObj(aRect);
    7118           1 :         pPage->InsertObject(pObj);
    7119           1 :         Point aRef1(pObj->GetSnapRect().Center());
    7120           1 :         int nAngle = 9000; //90 deg.
    7121           1 :         double nSin=sin(nAngle*nPi180);
    7122           1 :         double nCos=cos(nAngle*nPi180);
    7123           1 :         pObj->Rotate(aRef1,nAngle,nSin,nCos);
    7124             : 
    7125           1 :         ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *m_pDoc, 0);
    7126             : 
    7127           1 :         Rectangle aSnap = pObj->GetSnapRect();
    7128           1 :         CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
    7129           1 :         CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
    7130           1 :         CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.Left(), aSnap.Left(), TOLERANCE ) );
    7131           1 :         CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.Top(), aSnap.Top(), TOLERANCE ) );
    7132             : 
    7133           1 :         ScDrawObjData aAnchor;
    7134           1 :         ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj );
    7135             : 
    7136           1 :         aAnchor.maStart = pData->maStart;
    7137           1 :         aAnchor.maEnd = pData->maEnd;
    7138             : 
    7139           1 :         m_pDoc->SetDrawPageSize(0);
    7140             : 
    7141             :         // increase row 5 by 2000 hmm
    7142           1 :         m_pDoc->SetRowHeight( 5, 0, sc::HMMToTwips( 3000 ) );
    7143             :         // increase col 6 by 1000 hmm
    7144           1 :         m_pDoc->SetColWidth( 6, 0, sc::HMMToTwips( 2000 ) );
    7145             : 
    7146           1 :         aRotRect.setWidth( aRotRect.GetWidth() + 1000 );
    7147           1 :         aRotRect.setHeight( aRotRect.GetHeight() + 2000 );
    7148             : 
    7149           1 :         m_pDoc->SetDrawPageSize(0);
    7150             : 
    7151           1 :         aSnap = pObj->GetSnapRect();
    7152             : 
    7153             :         // ensure that width and height have been adjusted accordingly
    7154           1 :         CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetHeight(), aSnap.GetHeight(), TOLERANCE ) );
    7155           1 :         CPPUNIT_ASSERT_EQUAL( true, testEqualsWithTolerance( aRotRect.GetWidth(), aSnap.GetWidth(), TOLERANCE ) );
    7156             : 
    7157             :         // ensure that anchor start and end addresses haven't changed
    7158           1 :         CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Row(), pData->maStart.Row() ); // start row 0
    7159           1 :         CPPUNIT_ASSERT_EQUAL( aAnchor.maStart.Col(), pData->maStart.Col() ); // start column 5
    7160           1 :         CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Row(), pData->maEnd.Row() ); // end row 3
    7161           1 :         CPPUNIT_ASSERT_EQUAL( aAnchor.maEnd.Col(), pData->maEnd.Col() ); // end col 7
    7162             :     }
    7163           1 :     m_pDoc->DeleteTab(0);
    7164           1 : }
    7165             : 
    7166           1 : void Test::testCellTextWidth()
    7167             : {
    7168           1 :     m_pDoc->InsertTab(0, "Test");
    7169             : 
    7170           1 :     ScAddress aTopCell(0, 0, 0);
    7171             : 
    7172             :     // Sheet is empty.
    7173           1 :     boost::scoped_ptr<ScColumnTextWidthIterator> pIter(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
    7174           1 :     CPPUNIT_ASSERT_MESSAGE("Column should have no text widths stored.", !pIter->hasCell());
    7175             : 
    7176             :     // Sheet only has one cell.
    7177           1 :     m_pDoc->SetString(0, 0, 0, "Only one cell");
    7178           1 :     pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
    7179           1 :     CPPUNIT_ASSERT_MESSAGE("Column should have a cell.", pIter->hasCell());
    7180           1 :     SCROW nTestRow = 0;
    7181           1 :     CPPUNIT_ASSERT_EQUAL(nTestRow, pIter->getPos());
    7182             : 
    7183             :     // Setting a text width here should commit it to the column.
    7184           1 :     sal_uInt16 nTestVal = 432;
    7185           1 :     pIter->setValue(nTestVal);
    7186           1 :     CPPUNIT_ASSERT_EQUAL(nTestVal, m_pDoc->GetTextWidth(aTopCell));
    7187             : 
    7188             :     // Set values to row 2 through 6.
    7189           6 :     for (SCROW i = 2; i <= 6; ++i)
    7190           5 :         m_pDoc->SetString(0, i, 0, "foo");
    7191             : 
    7192             :     // Set values to row 10 through 18.
    7193          10 :     for (SCROW i = 10; i <= 18; ++i)
    7194           9 :         m_pDoc->SetString(0, i, 0, "foo");
    7195             : 
    7196             :     {
    7197             :         // Full range.
    7198           1 :         pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
    7199           1 :         SCROW aRows[] = { 0, 2, 3, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 18 };
    7200           1 :         size_t n = SAL_N_ELEMENTS(aRows);
    7201          16 :         for (size_t i = 0; i < n; ++i, pIter->next())
    7202             :         {
    7203          15 :             CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
    7204          15 :             CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
    7205             :         }
    7206           1 :         CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
    7207             :     }
    7208             : 
    7209             :     {
    7210             :         // Specify start and end rows (6 - 16)
    7211           1 :         ScAddress aStart = aTopCell;
    7212           1 :         aStart.SetRow(6);
    7213           1 :         pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aStart, 16));
    7214           1 :         SCROW aRows[] = { 6, 10, 11, 12, 13, 14, 15, 16 };
    7215           1 :         size_t n = SAL_N_ELEMENTS(aRows);
    7216           9 :         for (size_t i = 0; i < n; ++i, pIter->next())
    7217             :         {
    7218           8 :             CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
    7219           8 :             CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
    7220             :         }
    7221           1 :         CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
    7222             :     }
    7223             : 
    7224             :     // Clear from row 3 to row 17. After this, we should only have cells at rows 0, 2 and 18.
    7225           1 :     clearRange(m_pDoc, ScRange(0, 3, 0, 0, 17, 0));
    7226             : 
    7227             :     {
    7228             :         // Full range again.
    7229           1 :         pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
    7230           1 :         SCROW aRows[] = { 0, 2, 18 };
    7231           1 :         size_t n = SAL_N_ELEMENTS(aRows);
    7232           4 :         for (size_t i = 0; i < n; ++i, pIter->next())
    7233             :         {
    7234           3 :             CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
    7235           3 :             CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
    7236             :         }
    7237           1 :         CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
    7238             :     }
    7239             : 
    7240             :     // Delete row 2 which shifts all cells below row 2 upward. After this, we
    7241             :     // should only have cells at rows 0 and 17.
    7242           1 :     m_pDoc->DeleteRow(0, 0, MAXCOL, MAXTAB, 2, 1);
    7243             :     {
    7244             :         // Full range again.
    7245           1 :         pIter.reset(new ScColumnTextWidthIterator(*m_pDoc, aTopCell, MAXROW));
    7246           1 :         SCROW aRows[] = { 0, 17 };
    7247           1 :         size_t n = SAL_N_ELEMENTS(aRows);
    7248           3 :         for (size_t i = 0; i < n; ++i, pIter->next())
    7249             :         {
    7250           2 :             CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", pIter->hasCell());
    7251           2 :             CPPUNIT_ASSERT_EQUAL(aRows[i], pIter->getPos());
    7252             :         }
    7253           1 :         CPPUNIT_ASSERT_MESSAGE("Iterator should have ended.", !pIter->hasCell());
    7254             :     }
    7255             : 
    7256           1 :     m_pDoc->DeleteTab(0);
    7257           1 : }
    7258             : 
    7259           1 : void Test::testFormulaGrouping()
    7260             : {
    7261           1 :     if ( !getenv("SC_FORMULAGROUP") )
    7262           2 :         return;
    7263             : 
    7264             :     static const struct {
    7265             :         const char *pFormula[3];
    7266             :         bool  bGroup[3];
    7267             :     } aGroupTests[] = {
    7268             :         { { "=B1", "=B2", "" }, // relative reference
    7269             :           { true, true, false } },
    7270             :         { { "=B1", "=B2", "=B3" },
    7271             :           { true, true, true } },
    7272             :         { { "=B1", "", "=B3" }, // a gap
    7273             :           { false, false, false } },
    7274             :         { { "=$B$1", "=$B$1", "" }, // absolute reference
    7275             :           { true, true, false } },
    7276             :         { { "=$Z$10", "=$Z$10", "=$Z$10" },
    7277             :           { true, true, true } },
    7278             :         { { "=C1+$Z$10", "=C2+$Z$10", "=C3+$Z$10" }, // mixture
    7279             :           { true, true, true } },
    7280             :         { { "=C1+$Z$11", "=C2+$Z$12", "=C3+$Z$12" }, // mixture
    7281             :           { false, true, true } },
    7282             :         { { "=SUM(B1)",  "", "=SUM(B3)" },    // a gap
    7283             :           { false, false, false } },
    7284             :     };
    7285             : 
    7286           0 :     m_pDoc->InsertTab( 0, "sheet" );
    7287             : 
    7288           0 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aGroupTests); ++i)
    7289             :     {
    7290           0 :         for (size_t j = 0; j < SAL_N_ELEMENTS(aGroupTests[0].pFormula); ++j)
    7291             :         {
    7292           0 :             OUString aFormula = OUString::createFromAscii(aGroupTests[i].pFormula[j]);
    7293           0 :             m_pDoc->SetString(0, static_cast<SCROW>(j), 0, aFormula);
    7294           0 :         }
    7295           0 :         m_pDoc->RebuildFormulaGroups();
    7296             : 
    7297           0 :         for (size_t j = 0; j < SAL_N_ELEMENTS(aGroupTests[0].pFormula); ++j)
    7298             :         {
    7299           0 :             ScRefCellValue aCell;
    7300           0 :             aCell.assign(*m_pDoc, ScAddress(0, static_cast<SCROW>(j), 0));
    7301           0 :             if (aCell.isEmpty())
    7302             :             {
    7303           0 :                 CPPUNIT_ASSERT_MESSAGE("invalid empty cell", !aGroupTests[i].bGroup[j]);
    7304           0 :                 continue;
    7305             :             }
    7306           0 :             CPPUNIT_ASSERT_MESSAGE("Cell expected, but not there.", !aCell.isEmpty());
    7307           0 :             CPPUNIT_ASSERT_MESSAGE("Cell wrong type.",
    7308           0 :                                    aCell.meType == CELLTYPE_FORMULA);
    7309           0 :             ScFormulaCell *pCur = aCell.mpFormula;
    7310             : 
    7311           0 :             if( !!pCur->GetCellGroup().get() ^ aGroupTests[i].bGroup[j] )
    7312             :             {
    7313           0 :                 cout << "expected group test " << i << " at row " << j << " to be "
    7314           0 :                     << aGroupTests[i].bGroup[j] << " but is " << !!pCur->GetCellGroup().get() << endl;
    7315           0 :                 CPPUNIT_FAIL("Failed");
    7316             :             }
    7317           0 :         }
    7318             :     }
    7319             : }
    7320             : 
    7321           1 : void Test::testCondFormatINSDEL()
    7322             : {
    7323             :     // fdo#62206
    7324           1 :     m_pDoc->InsertTab(0, "Test");
    7325           1 :     ScConditionalFormatList* pList = m_pDoc->GetCondFormList(0);
    7326             : 
    7327           1 :     ScConditionalFormat* pFormat = new ScConditionalFormat(1, m_pDoc);
    7328           1 :     ScRangeList aRangeList(ScRange(0,0,0,0,3,0));
    7329           1 :     pFormat->AddRange(aRangeList);
    7330           3 :     ScCondFormatEntry* pEntry = new ScCondFormatEntry(SC_COND_DIRECT,"=B2","",m_pDoc,ScAddress(0,0,0),ScGlobal::GetRscString(STR_STYLENAME_RESULT));
    7331           1 :     pFormat->AddEntry(pEntry);
    7332             : 
    7333           1 :     m_pDoc->AddCondFormatData(pFormat->GetRange(), 0, 1);
    7334           1 :     pList->InsertNew(pFormat);
    7335             : 
    7336           1 :     m_pDoc->InsertCol(0,0,MAXROW,0,0,2);
    7337           1 :     const ScRangeList& rRange = pFormat->GetRange();
    7338           1 :     CPPUNIT_ASSERT(rRange == ScRange(2,0,0,2,3,0));
    7339             : 
    7340           2 :     OUString aExpr = pEntry->GetExpression(ScAddress(2,0,0), 0);
    7341           1 :     CPPUNIT_ASSERT_EQUAL(aExpr, OUString("D2"));
    7342             : 
    7343           2 :     m_pDoc->DeleteTab(0);
    7344           1 : }
    7345             : 
    7346           1 : CPPUNIT_TEST_SUITE_REGISTRATION(Test);
    7347             : 
    7348             : }
    7349             : 
    7350           4 : CPPUNIT_PLUGIN_IMPLEMENT();
    7351             : 
    7352             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10