LCOV - code coverage report
Current view: top level - sc/qa/unit - ucalc_formula.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 2628 2849 92.2 %
Date: 2015-06-13 12:38:46 Functions: 74 74 100.0 %
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             : 
      10             : #include "ucalc.hxx"
      11             : #include "markdata.hxx"
      12             : #include "calcconfig.hxx"
      13             : #include "interpre.hxx"
      14             : #include "compiler.hxx"
      15             : #include "tokenarray.hxx"
      16             : #include "refdata.hxx"
      17             : #include "scopetools.hxx"
      18             : #include "formulacell.hxx"
      19             : #include "formulagroup.hxx"
      20             : #include "scmod.hxx"
      21             : #include "docsh.hxx"
      22             : #include "docfunc.hxx"
      23             : #include "paramisc.hxx"
      24             : #include "tokenstringcontext.hxx"
      25             : #include "dbdata.hxx"
      26             : #include <validat.hxx>
      27             : #include <scitems.hxx>
      28             : #include <patattr.hxx>
      29             : #include <docpool.hxx>
      30             : 
      31             : #include <formula/vectortoken.hxx>
      32             : #include <svl/broadcast.hxx>
      33             : 
      34             : #include <boost/scoped_ptr.hpp>
      35             : 
      36             : using namespace formula;
      37             : 
      38             : namespace {
      39             : 
      40           2 : ScRange getCachedRange(const ScExternalRefCache::TableTypeRef& pCacheTab)
      41             : {
      42           2 :     ScRange aRange;
      43             : 
      44           2 :     vector<SCROW> aRows;
      45           2 :     pCacheTab->getAllRows(aRows);
      46           2 :     vector<SCROW>::const_iterator itrRow = aRows.begin(), itrRowEnd = aRows.end();
      47           2 :     bool bFirst = true;
      48          11 :     for (; itrRow != itrRowEnd; ++itrRow)
      49             :     {
      50           9 :         SCROW nRow = *itrRow;
      51           9 :         vector<SCCOL> aCols;
      52           9 :         pCacheTab->getAllCols(nRow, aCols);
      53           9 :         vector<SCCOL>::const_iterator itrCol = aCols.begin(), itrColEnd = aCols.end();
      54          27 :         for (; itrCol != itrColEnd; ++itrCol)
      55             :         {
      56          18 :             SCCOL nCol = *itrCol;
      57          18 :             if (bFirst)
      58             :             {
      59           2 :                 aRange.aStart = ScAddress(nCol, nRow, 0);
      60           2 :                 aRange.aEnd = aRange.aStart;
      61           2 :                 bFirst = false;
      62             :             }
      63             :             else
      64             :             {
      65          16 :                 if (nCol < aRange.aStart.Col())
      66           0 :                     aRange.aStart.SetCol(nCol);
      67          16 :                 else if (aRange.aEnd.Col() < nCol)
      68           2 :                     aRange.aEnd.SetCol(nCol);
      69             : 
      70          16 :                 if (nRow < aRange.aStart.Row())
      71           0 :                     aRange.aStart.SetRow(nRow);
      72          16 :                 else if (aRange.aEnd.Row() < nRow)
      73           7 :                     aRange.aEnd.SetRow(nRow);
      74             :             }
      75             :         }
      76           9 :     }
      77           2 :     return aRange;
      78             : }
      79             : 
      80             : }
      81             : 
      82           1 : void Test::testFormulaCreateStringFromTokens()
      83             : {
      84             :     // Insert sheets.
      85           1 :     OUString aTabName1("Test");
      86           2 :     OUString aTabName2("Kevin's Data");
      87           2 :     OUString aTabName3("Past Data");
      88           2 :     OUString aTabName4("2013");
      89           1 :     m_pDoc->InsertTab(0, aTabName1);
      90           1 :     m_pDoc->InsertTab(1, aTabName2);
      91           1 :     m_pDoc->InsertTab(2, aTabName3);
      92           1 :     m_pDoc->InsertTab(3, aTabName4);
      93             : 
      94             :     // Insert named ranges.
      95             :     struct {
      96             :         bool bGlobal;
      97             :         const char* pName;
      98             :         const char* pExpr;
      99             :     } aNames[] = {
     100             :         { true, "x", "Test.H1" },
     101             :         { true, "y", "Test.H2" },
     102             :         { true, "z", "Test.H3" },
     103             : 
     104             :         { false, "sheetx", "Test.J1" }
     105           1 :     };
     106             : 
     107           1 :     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
     108           1 :     ScRangeName* pSheetNames = m_pDoc->GetRangeName(0);
     109           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
     110           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to obtain sheet-local named expression object.", pSheetNames);
     111             : 
     112           5 :     for (size_t i = 0, n = SAL_N_ELEMENTS(aNames); i < n; ++i)
     113             :     {
     114             :         ScRangeData* pName = new ScRangeData(
     115             :             m_pDoc, OUString::createFromAscii(aNames[i].pName), OUString::createFromAscii(aNames[i].pExpr),
     116           4 :             ScAddress(0,0,0), RT_NAME, formula::FormulaGrammar::GRAM_NATIVE);
     117             : 
     118           4 :         if (aNames[i].bGlobal)
     119             :         {
     120           3 :             bool bInserted = pGlobalNames->insert(pName);
     121           3 :             CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
     122             :         }
     123             :         else
     124             :         {
     125           1 :             bool bInserted = pSheetNames->insert(pName);
     126           1 :             CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
     127             :         }
     128             :     }
     129             : 
     130             :     // Insert DB ranges.
     131             :     struct {
     132             :         const char* pName;
     133             :         SCTAB nTab;
     134             :         SCCOL nCol1;
     135             :         SCROW nRow1;
     136             :         SCCOL nCol2;
     137             :         SCROW nRow2;
     138             :     } aDBs[] = {
     139             :         { "Table1", 0, 0, 0, 10, 10 },
     140             :         { "Table2", 1, 0, 0, 10, 10 },
     141             :         { "Table3", 2, 0, 0, 10, 10 }
     142           1 :     };
     143             : 
     144           1 :     ScDBCollection* pDBs = m_pDoc->GetDBCollection();
     145           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch DB collection object.", pDBs);
     146             : 
     147           4 :     for (size_t i = 0, n = SAL_N_ELEMENTS(aDBs); i < n; ++i)
     148             :     {
     149             :         ScDBData* pData = new ScDBData(
     150             :             OUString::createFromAscii(
     151           3 :                 aDBs[i].pName), aDBs[i].nTab, aDBs[i].nCol1, aDBs[i].nRow1, aDBs[i].nCol2,aDBs[i].nRow2);
     152           3 :         bool bInserted = pDBs->getNamedDBs().insert(pData);
     153           6 :         CPPUNIT_ASSERT_MESSAGE(
     154             :             OString(
     155             :                 "Failed to insert \"" + OString(aDBs[i].pName) + "\"").getStr(),
     156           3 :             bInserted);
     157             :     }
     158             : 
     159             :     const char* aTests[] = {
     160             :         "1+2",
     161             :         "SUM(A1:A10;B1:B10;C5;D6)",
     162             :         "IF(Test.B10<>10;\"Good\";\"Bad\")",
     163             :         "AVERAGE('2013'.B10:C20)",
     164             :         "'Kevin''s Data'.B10",
     165             :         "'Past Data'.B1+'2013'.B2*(1+'Kevin''s Data'.C10)",
     166             :         "x+y*z", // named ranges
     167             :         "SUM(sheetx;x;y;z)", // sheet local and global named ranges mixed
     168             :         "MAX(Table1)+MIN(Table2)*SUM(Table3)", // database ranges
     169             :         "{1;TRUE;3|FALSE;5;\"Text\"|;;}", // inline matrix
     170             :         "SUM('file:///path/to/fake.file'#$Sheet.A1:B10)",
     171           1 :     };
     172             :     (void) aTests;
     173             : 
     174           2 :     boost::scoped_ptr<ScTokenArray> pArray;
     175             : 
     176           2 :     sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
     177             : 
     178             :     // Artificially add external refererence data after the context object is
     179             :     // initialized.
     180           1 :     aCxt.maExternalFileNames.push_back("file:///path/to/fake.file");
     181           2 :     std::vector<OUString> aExtTabNames;
     182           1 :     aExtTabNames.push_back("Sheet");
     183             :     aCxt.maExternalCachedTabNames.insert(
     184           1 :         sc::TokenStringContext::IndexNamesMapType::value_type(0, aExtTabNames));
     185             : 
     186           1 :     ScAddress aPos(0,0,0);
     187             : 
     188          12 :     for (size_t i = 0, n = SAL_N_ELEMENTS(aTests); i < n; ++i)
     189             :     {
     190             : #if 0
     191             :         OUString aFormula = OUString::createFromAscii(aTests[i]);
     192             : #endif
     193          11 :         ScCompiler aComp(m_pDoc, aPos);
     194          11 :         aComp.SetGrammar(FormulaGrammar::GRAM_ENGLISH);
     195             : #if 0 // TODO: This call to CompileString() causes the cppunittester to somehow fail on Windows.
     196             :         pArray.reset(aComp.CompileString(aFormula));
     197             :         CPPUNIT_ASSERT_MESSAGE("Failed to compile formula string.", pArray.get());
     198             : 
     199             :         OUString aCheck = pArray->CreateString(aCxt, aPos);
     200             :         CPPUNIT_ASSERT_EQUAL(aFormula, aCheck);
     201             : #endif
     202          11 :     }
     203             : 
     204           1 :     m_pDoc->DeleteTab(3);
     205           1 :     m_pDoc->DeleteTab(2);
     206           1 :     m_pDoc->DeleteTab(1);
     207           2 :     m_pDoc->DeleteTab(0);
     208           1 : }
     209             : 
     210             : namespace {
     211             : 
     212          18 : bool isEmpty( const formula::VectorRefArray& rArray, size_t nPos )
     213             : {
     214          18 :     if (rArray.mpStringArray)
     215             :     {
     216           7 :         if (rArray.mpStringArray[nPos])
     217           0 :             return false;
     218             :     }
     219             : 
     220          18 :     if (rArray.mpNumericArray)
     221          17 :         return rtl::math::isNan(rArray.mpNumericArray[nPos]);
     222             :     else
     223           1 :         return true;
     224             : }
     225             : 
     226          35 : bool equals( const formula::VectorRefArray& rArray, size_t nPos, double fVal )
     227             : {
     228          35 :     if (rArray.mpStringArray && rArray.mpStringArray[nPos])
     229             :         // This is a string cell.
     230           0 :         return false;
     231             : 
     232          35 :     if (rArray.mpNumericArray && rArray.mpNumericArray[nPos] == fVal)
     233          35 :         return true;
     234             : 
     235           0 :     return false;
     236             : }
     237             : 
     238          16 : bool equals( const formula::VectorRefArray& rArray, size_t nPos, const OUString& rVal )
     239             : {
     240          16 :     if (!rArray.mpStringArray)
     241           0 :         return false;
     242             : 
     243          16 :     bool bEquals = OUString(rArray.mpStringArray[nPos]).equalsIgnoreAsciiCase(rVal);
     244          16 :     if (!bEquals)
     245             :     {
     246           0 :         cerr << "Expected: " << rVal.toAsciiUpperCase() << " (upcased)" << endl;
     247           0 :         cerr << "Actual: " << OUString(rArray.mpStringArray[nPos]) << " (upcased)" << endl;
     248             :     }
     249          16 :     return bEquals;
     250             : }
     251             : 
     252             : }
     253             : 
     254           1 : void Test::testFormulaParseReference()
     255             : {
     256           2 :     OUString aTab1("90's Music"), aTab2("90's and 70's"), aTab3("All Others"), aTab4("NoQuote");
     257           1 :     m_pDoc->InsertTab(0, "Dummy"); // just to shift the sheet indices...
     258           1 :     m_pDoc->InsertTab(1, aTab1); // name with a single quote.
     259           1 :     m_pDoc->InsertTab(2, aTab2); // name with 2 single quotes.
     260           1 :     m_pDoc->InsertTab(3, aTab3); // name without single quotes.
     261           1 :     m_pDoc->InsertTab(4, aTab4); // name that doesn't require to be quoted.
     262             : 
     263           2 :     OUString aTabName;
     264           1 :     m_pDoc->GetName(1, aTabName);
     265           1 :     CPPUNIT_ASSERT_EQUAL(aTab1, aTabName);
     266           1 :     m_pDoc->GetName(2, aTabName);
     267           1 :     CPPUNIT_ASSERT_EQUAL(aTab2, aTabName);
     268           1 :     m_pDoc->GetName(3, aTabName);
     269           1 :     CPPUNIT_ASSERT_EQUAL(aTab3, aTabName);
     270           1 :     m_pDoc->GetName(4, aTabName);
     271           1 :     CPPUNIT_ASSERT_EQUAL(aTab4, aTabName);
     272             : 
     273             :     // Make sure the formula input and output match.
     274             :     {
     275             :         const char* aChecks[] = {
     276             :             "'90''s Music'.B12",
     277             :             "'90''s and 70''s'.$AB$100",
     278             :             "'All Others'.Z$100",
     279             :             "NoQuote.$C111"
     280           1 :         };
     281             : 
     282           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
     283             :         {
     284             :             // Use the 'Dummy' sheet for this.
     285           4 :             OUString aInput("=");
     286           4 :             aInput += OUString::createFromAscii(aChecks[i]);
     287           4 :             m_pDoc->SetString(ScAddress(0,0,0), aInput);
     288           4 :             if (!checkFormula(*m_pDoc, ScAddress(0,0,0), aChecks[i]))
     289           0 :                 CPPUNIT_FAIL("Wrong formula");
     290           4 :         }
     291             :     }
     292             : 
     293           1 :     ScAddress aPos;
     294           2 :     ScAddress::ExternalInfo aExtInfo;
     295           1 :     sal_uInt16 nRes = aPos.Parse("'90''s Music'.D10", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
     296           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & SCA_VALID) != 0);
     297           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(1), aPos.Tab());
     298           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(3), aPos.Col());
     299           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), aPos.Row());
     300           1 :     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
     301             : 
     302           1 :     nRes = aPos.Parse("'90''s and 70''s'.C100", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
     303           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & SCA_VALID) != 0);
     304           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(2), aPos.Tab());
     305           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(2), aPos.Col());
     306           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(99), aPos.Row());
     307           1 :     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
     308             : 
     309           1 :     nRes = aPos.Parse("'All Others'.B3", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
     310           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & SCA_VALID) != 0);
     311           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(3), aPos.Tab());
     312           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), aPos.Col());
     313           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), aPos.Row());
     314           1 :     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
     315             : 
     316           1 :     nRes = aPos.Parse("NoQuote.E13", m_pDoc, formula::FormulaGrammar::CONV_OOO, &aExtInfo);
     317           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to parse.", (nRes & SCA_VALID) != 0);
     318           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCTAB>(4), aPos.Tab());
     319           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(4), aPos.Col());
     320           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(12), aPos.Row());
     321           1 :     CPPUNIT_ASSERT_MESSAGE("This is not an external address.", !aExtInfo.mbExternal);
     322             : 
     323           1 :     m_pDoc->DeleteTab(4);
     324           1 :     m_pDoc->DeleteTab(3);
     325           1 :     m_pDoc->DeleteTab(2);
     326           1 :     m_pDoc->DeleteTab(1);
     327           2 :     m_pDoc->DeleteTab(0);
     328           1 : }
     329             : 
     330           1 : void Test::testFetchVectorRefArray()
     331             : {
     332           1 :     m_pDoc->InsertTab(0, "Test");
     333             : 
     334             :     // All numeric cells in Column A.
     335           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1);
     336           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 2);
     337           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 3);
     338           1 :     m_pDoc->SetValue(ScAddress(0,3,0), 4);
     339             : 
     340           1 :     formula::VectorRefArray aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 4);
     341           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     342           1 :     CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
     343           1 :     CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
     344           1 :     CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
     345           1 :     CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
     346           1 :     CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
     347             : 
     348           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 5);
     349           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     350           1 :     CPPUNIT_ASSERT_MESSAGE("Array is expected to be numeric cells only.", !aArray.mpStringArray);
     351           1 :     CPPUNIT_ASSERT_EQUAL(1.0, aArray.mpNumericArray[0]);
     352           1 :     CPPUNIT_ASSERT_EQUAL(2.0, aArray.mpNumericArray[1]);
     353           1 :     CPPUNIT_ASSERT_EQUAL(3.0, aArray.mpNumericArray[2]);
     354           1 :     CPPUNIT_ASSERT_EQUAL(4.0, aArray.mpNumericArray[3]);
     355           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
     356             : 
     357             :     // All string cells in Column B.  Note that the fetched string arrays are
     358             :     // only to be compared case-insensitively.  Right now, we use upper cased
     359             :     // strings to achieve case-insensitive-ness, but that may change. So,
     360             :     // don't count on that.
     361           1 :     m_pDoc->SetString(ScAddress(1,0,0), "Andy");
     362           1 :     m_pDoc->SetString(ScAddress(1,1,0), "Bruce");
     363           1 :     m_pDoc->SetString(ScAddress(1,2,0), "Charlie");
     364           1 :     m_pDoc->SetString(ScAddress(1,3,0), "David");
     365           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 5);
     366           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     367           1 :     CPPUNIT_ASSERT_MESSAGE("Array is expected to be string cells only.", !aArray.mpNumericArray);
     368           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Andy"));
     369           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Bruce"));
     370           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "Charlie"));
     371           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "David"));
     372           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
     373             : 
     374             :     // Mixture of numeric, string, and empty cells in Column C.
     375           1 :     m_pDoc->SetString(ScAddress(2,0,0), "Header");
     376           1 :     m_pDoc->SetValue(ScAddress(2,1,0), 11);
     377           1 :     m_pDoc->SetValue(ScAddress(2,2,0), 12);
     378           1 :     m_pDoc->SetValue(ScAddress(2,3,0), 13);
     379           1 :     m_pDoc->SetString(ScAddress(2,5,0), "=SUM(C2:C4)");
     380           1 :     m_pDoc->CalcAll();
     381             : 
     382           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(2,0,0), 7);
     383           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     384           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
     385           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "Header"));
     386           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 11));
     387           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 12));
     388           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 13));
     389           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 4));
     390           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 36));
     391           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
     392             : 
     393             :     // Mixed type again in Column D, but it starts with a numeric cell.
     394           1 :     m_pDoc->SetValue(ScAddress(3,0,0), 10);
     395           1 :     m_pDoc->SetString(ScAddress(3,1,0), "Below 10");
     396             :     // Leave 2 empty cells.
     397           1 :     m_pDoc->SetValue(ScAddress(3,4,0), 11);
     398           1 :     m_pDoc->SetString(ScAddress(3,5,0), "=12");
     399           1 :     m_pDoc->SetString(ScAddress(3,6,0), "=13");
     400           1 :     m_pDoc->SetString(ScAddress(3,7,0), "=CONCATENATE(\"A\";\"B\";\"C\")");
     401           1 :     m_pDoc->CalcAll();
     402             : 
     403           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(3,0,0), 8);
     404           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     405           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
     406           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 10));
     407           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "Below 10"));
     408           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 2));
     409           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
     410           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 11));
     411           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 12));
     412           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 13));
     413           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 7, "ABC"));
     414             : 
     415             :     // Column E consists of formula cells whose results are all numeric.
     416           8 :     for (SCROW i = 0; i <= 6; ++i)
     417           7 :         m_pDoc->SetString(ScAddress(4,i,0), "=ROW()");
     418           1 :     m_pDoc->CalcAll();
     419             : 
     420             :     // Leave row 7 empty.
     421           1 :     m_pDoc->SetString(ScAddress(4,8,0), "Andy");
     422           1 :     m_pDoc->SetValue(ScAddress(4,9,0), 123);
     423             : 
     424             :     // This array fits within a single formula block.
     425           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 5);
     426           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     427           1 :     CPPUNIT_ASSERT_MESSAGE("Array should be purely numeric.", aArray.mpNumericArray && !aArray.mpStringArray);
     428           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
     429           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
     430           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
     431           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
     432           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
     433             : 
     434             :     // This array spans over multiple blocks.
     435           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,0,0), 11);
     436           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     437           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have both numeric and string arrays.", aArray.mpNumericArray && aArray.mpStringArray);
     438           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1));
     439           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 2));
     440           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 3));
     441           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 4));
     442           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 5));
     443           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 5, 6));
     444           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 6, 7));
     445           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
     446           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 8, "Andy"));
     447           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 9, 123));
     448           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 10));
     449             : 
     450             :     // Hit the cache but at a different start row.
     451           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(4,2,0), 3);
     452           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     453           1 :     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
     454           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 3));
     455           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 4));
     456           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 5));
     457             : 
     458             :     // Column F begins with empty rows at the top.
     459           1 :     m_pDoc->SetValue(ScAddress(5,2,0), 1.1);
     460           1 :     m_pDoc->SetValue(ScAddress(5,3,0), 1.2);
     461           1 :     m_pDoc->SetString(ScAddress(5,4,0), "=2*8");
     462           1 :     m_pDoc->CalcAll();
     463             : 
     464           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 4);
     465           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     466           1 :     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
     467           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 0, 1.1));
     468           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 1, 1.2));
     469           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 16));
     470           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 3));
     471             : 
     472           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 3);
     473           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     474           1 :     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
     475           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
     476           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
     477           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
     478             : 
     479           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,0,0), 10);
     480           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     481           1 :     CPPUNIT_ASSERT_MESSAGE("Array should at least have a numeric array.", aArray.mpNumericArray);
     482           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 0));
     483           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 1));
     484           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 2, 1.1));
     485           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 3, 1.2));
     486           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected numeric cell.", equals(aArray, 4, 16));
     487           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 5));
     488           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 6));
     489           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 7));
     490           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 8));
     491           1 :     CPPUNIT_ASSERT_MESSAGE("This should be empty.", isEmpty(aArray, 9));
     492             : 
     493             :     // Get the array for F3:F4. This array should only consist of numeric array.
     494           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(5,2,0), 3);
     495           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     496           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
     497           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
     498             : 
     499             :     // Column G consists only of strings.
     500           1 :     m_pDoc->SetString(ScAddress(6,0,0), "Title");
     501           1 :     m_pDoc->SetString(ScAddress(6,1,0), "foo");
     502           1 :     m_pDoc->SetString(ScAddress(6,2,0), "bar");
     503           1 :     m_pDoc->SetString(ScAddress(6,3,0), "foo");
     504           1 :     m_pDoc->SetString(ScAddress(6,4,0), "baz");
     505           1 :     m_pDoc->SetString(ScAddress(6,5,0), "quack");
     506           1 :     m_pDoc->SetString(ScAddress(6,6,0), "beep");
     507           1 :     m_pDoc->SetString(ScAddress(6,7,0), "kerker");
     508             : 
     509           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,1,0), 4); // G2:G5
     510           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     511           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
     512           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
     513           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "foo"));
     514           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "bar"));
     515           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "foo"));
     516           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "baz"));
     517             : 
     518           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(6,2,0), 4); // G3:G6
     519           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     520           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a numeric array.", !aArray.mpNumericArray);
     521           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a string array.", aArray.mpStringArray);
     522           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, "bar"));
     523           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, "foo"));
     524           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, "baz"));
     525           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 3, "quack"));
     526             : 
     527             :     // Column H starts with formula cells.
     528          11 :     for (SCROW i = 0; i < 10; ++i)
     529          10 :         m_pDoc->SetString(ScAddress(7,i,0), "=ROW()");
     530             : 
     531           1 :     m_pDoc->CalcAll();
     532           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,3,0), 3); // H4:H6
     533           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     534           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
     535           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
     536           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 4.0));
     537           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 1, 5.0));
     538           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 2, 6.0));
     539             : 
     540           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(7,4,0), 10); // H5:H15
     541           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to fetch vector ref array.", aArray.isValid());
     542           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
     543           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
     544           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected string cell.", equals(aArray, 0, 5.0));
     545             : 
     546             :     // Clear everything and start over.
     547           1 :     clearRange(m_pDoc, ScRange(0,0,0,MAXCOL,MAXROW,0));
     548           1 :     m_pDoc->ClearFormulaContext();
     549             : 
     550             :     // Totally empty range in a totally empty column (Column A).
     551           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(0,0,0), 3); // A1:A3
     552           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
     553           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
     554           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
     555           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
     556           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
     557             : 
     558             :     // Totally empty range in a non-empty column (Column B).
     559           1 :     m_pDoc->SetString(ScAddress(1,10,0), "Some text"); // B11
     560           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,0,0), 3); // B1:B3
     561           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
     562           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
     563           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
     564           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
     565           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
     566             : 
     567           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,12,0), 3); // B13:B15
     568           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
     569           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
     570           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
     571           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
     572           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
     573             : 
     574             :     // These values come from a cache because of the call above.
     575           1 :     aArray = m_pDoc->FetchVectorRefArray(ScAddress(1,1,0), 3); // B2:B4
     576           1 :     CPPUNIT_ASSERT_MESSAGE("Array should have a numeric array.", aArray.mpNumericArray);
     577           1 :     CPPUNIT_ASSERT_MESSAGE("Array should NOT have a string array.", !aArray.mpStringArray);
     578           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[0]));
     579           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[1]));
     580           1 :     CPPUNIT_ASSERT(rtl::math::isNan(aArray.mpNumericArray[2]));
     581             : 
     582           1 :     m_pDoc->DeleteTab(0);
     583           1 : }
     584             : 
     585           1 : void Test::testFormulaHashAndTag()
     586             : {
     587           1 :     m_pDoc->InsertTab(0, "Test");
     588             : 
     589           1 :     ScAddress aPos1(0,0,0), aPos2(1,0,0);
     590             : 
     591             :     // Test formula hashing.
     592             : 
     593             :     struct {
     594             :         const char* pFormula1; const char* pFormula2; bool bEqual;
     595             :     } aHashTests[] = {
     596             :         { "=1", "=2", false }, // different constants
     597             :         { "=SUM(1;2;3;4;5)", "=AVERAGE(1;2;3;4;5)", false }, // different functions
     598             :         { "=C2*3", "=D2*3", true },  // relative references
     599             :         { "=C2*3", "=D2*4", false }, // different constants
     600             :         { "=C2*4", "=D2*4", true },  // relative references
     601             :         { "=3*4*5", "=3*4*\"foo\"", false }, // numeric vs string constants
     602             :         { "=$C3/2", "=$C3/2", true }, // absolute column references
     603             :         { "=C$3/2", "=D$3/2", true }, // absolute row references
     604             :         { "=$E$30/2", "=$E$30/2", true }, // absolute references
     605             :         { "=X20", "=$X$20", false }, // absolute vs relative
     606             :         { "=X20", "=X$20", false }, // absolute vs relative
     607             :         { "=X20", "=$X20", false }, // absolute vs relative
     608             :         { "=X$20", "=$X20", false }, // column absolute vs row absolute
     609             :         // similar enough for merging ...
     610             :         { "=A1", "=B1", true },
     611             :         { "=$A$1", "=$B$1", true },
     612             :         { "=A1", "=C2", true },
     613             :         { "=SUM(A1)", "=SUM(B1)", true },
     614             :         { "=A1+3", "=B1+3", true },
     615             :         { "=A1+7", "=B1+42", false },
     616           1 :     };
     617             : 
     618          20 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aHashTests); ++i)
     619             :     {
     620          19 :         m_pDoc->SetString(aPos1, OUString::createFromAscii(aHashTests[i].pFormula1));
     621          19 :         m_pDoc->SetString(aPos2, OUString::createFromAscii(aHashTests[i].pFormula2));
     622          19 :         size_t nHashVal1 = m_pDoc->GetFormulaHash(aPos1);
     623          19 :         size_t nHashVal2 = m_pDoc->GetFormulaHash(aPos2);
     624             : 
     625          19 :         std::ostringstream os;
     626          19 :         os << "(expr1:" << aHashTests[i].pFormula1 << "; expr2:" << aHashTests[i].pFormula2 << ")";
     627          19 :         if (aHashTests[i].bEqual)
     628             :         {
     629          10 :             os << " Error: these hashes should be equal." << endl;
     630          10 :             CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 == nHashVal2);
     631             :         }
     632             :         else
     633             :         {
     634           9 :             os << " Error: these hashes should differ." << endl;
     635           9 :             CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), nHashVal1 != nHashVal2);
     636             :         }
     637             : 
     638          19 :         aPos1.IncRow();
     639          19 :         aPos2.IncRow();
     640          19 :     }
     641             : 
     642             :     // Go back to row 1.
     643           1 :     aPos1.SetRow(0);
     644           1 :     aPos2.SetRow(0);
     645             : 
     646             :     // Test formula vectorization state.
     647             : 
     648             :     struct {
     649             :         const char* pFormula; ScFormulaVectorState eState;
     650             :     } aVectorTests[] = {
     651             :         { "=SUM(1;2;3;4;5)", FormulaVectorEnabled },
     652             :         { "=NOW()", FormulaVectorDisabled },
     653             :         { "=AVERAGE(X1:Y200)", FormulaVectorCheckReference },
     654             :         { "=MAX(X1:Y200;10;20)", FormulaVectorCheckReference },
     655             :         { "=MIN(10;11;22)", FormulaVectorEnabled },
     656             :         { "=H4", FormulaVectorCheckReference },
     657           1 :     };
     658             : 
     659           7 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aVectorTests); ++i)
     660             :     {
     661           6 :         m_pDoc->SetString(aPos1, OUString::createFromAscii(aVectorTests[i].pFormula));
     662           6 :         ScFormulaVectorState eState = m_pDoc->GetFormulaVectorState(aPos1);
     663             : 
     664           6 :         if (eState != aVectorTests[i].eState)
     665             :         {
     666           0 :             std::ostringstream os;
     667           0 :             os << "Unexpected vectorization state: expr:" << aVectorTests[i].pFormula;
     668           0 :             CPPUNIT_ASSERT_MESSAGE(os.str().c_str(), false);
     669             :         }
     670           6 :         aPos1.IncRow();
     671             :     }
     672             : 
     673           1 :     m_pDoc->DeleteTab(0);
     674           1 : }
     675             : 
     676           1 : void Test::testFormulaTokenEquality()
     677             : {
     678             :     struct FormulaTokenEqualityTest
     679             :     {
     680             :         const char* mpFormula1;
     681             :         const char* mpFormula2;
     682             :         bool mbEqual;
     683             :     };
     684             : 
     685             :     FormulaTokenEqualityTest aTests[] = {
     686             :         { "R1C2", "R1C2", true },
     687             :         { "R1C2", "R1C3", false },
     688             :         { "R1C2", "R2C2", false },
     689             :         { "RC2",  "RC[1]", false },
     690             :         { "R1C2:R10C2", "R1C2:R10C2", true },
     691             :         { "R1C2:R10C2", "R1C2:R11C2", false },
     692             :         { "1", "2", false },
     693             :         { "RC[1]+1.2", "RC[1]+1.2", true },
     694             :         { "RC[1]*0.2", "RC[1]*0.5", false },
     695             :         { "\"Test1\"", "\"Test2\"", false },
     696             :         { "\"Test\"", "\"Test\"", true },
     697             :         { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test1\")", true },
     698             :         { "CONCATENATE(\"Test1\")", "CONCATENATE(\"Test2\")", false },
     699           1 :     };
     700             : 
     701           1 :     formula::FormulaGrammar::Grammar eGram = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
     702          14 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
     703             :     {
     704          13 :         ScFormulaCell aCell1(m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula1), eGram);
     705          26 :         ScFormulaCell aCell2(m_pDoc, ScAddress(), OUString::createFromAscii(aTests[i].mpFormula2), eGram);
     706             : 
     707          13 :         ScFormulaCell::CompareState eComp = aCell1.CompareByTokenArray(aCell2);
     708          13 :         if (aTests[i].mbEqual)
     709             :         {
     710           5 :             if (eComp == ScFormulaCell::NotEqual)
     711             :             {
     712           0 :                 std::ostringstream os;
     713           0 :                 os << "These two formulas should be evaluated equal: '"
     714           0 :                     << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
     715           0 :                 CPPUNIT_FAIL(os.str().c_str());
     716             :             }
     717             :         }
     718             :         else
     719             :         {
     720           8 :             if (eComp != ScFormulaCell::NotEqual)
     721             :             {
     722           0 :                 std::ostringstream os;
     723           0 :                 os << "These two formulas should be evaluated non-equal: '"
     724           0 :                     << aTests[i].mpFormula1 << "' vs '" << aTests[i].mpFormula2 << "'" << endl;
     725           0 :                 CPPUNIT_FAIL(os.str().c_str());
     726             :             }
     727             :         }
     728          13 :     }
     729           1 : }
     730             : 
     731           1 : void Test::testFormulaRefData()
     732             : {
     733           1 :     ScAddress aAddr(4,5,3), aPos(2,2,2);
     734             :     ScSingleRefData aRef;
     735           1 :     aRef.InitAddress(aAddr);
     736           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong ref data state.", !aRef.IsRowRel() && !aRef.IsColRel() && !aRef.IsTabRel());
     737           1 :     ASSERT_EQUAL_TYPE(SCCOL, 4, aRef.Col());
     738           1 :     ASSERT_EQUAL_TYPE(SCROW, 5, aRef.Row());
     739           1 :     ASSERT_EQUAL_TYPE(SCTAB, 3, aRef.Tab());
     740             : 
     741           1 :     aRef.SetRowRel(true);
     742           1 :     aRef.SetColRel(true);
     743           1 :     aRef.SetTabRel(true);
     744           1 :     aRef.SetAddress(aAddr, aPos);
     745           1 :     ASSERT_EQUAL_TYPE(SCCOL, 2, aRef.Col());
     746           1 :     ASSERT_EQUAL_TYPE(SCROW, 3, aRef.Row());
     747           1 :     ASSERT_EQUAL_TYPE(SCTAB, 1, aRef.Tab());
     748             : 
     749             :     // Test extension of range reference.
     750             : 
     751             :     ScComplexRefData aDoubleRef;
     752           1 :     aDoubleRef.InitRange(ScRange(2,2,0,4,4,0));
     753             : 
     754           1 :     aRef.InitAddress(ScAddress(6,5,0));
     755             : 
     756           1 :     aDoubleRef.Extend(aRef, ScAddress());
     757           1 :     ScRange aTest = aDoubleRef.toAbs(ScAddress());
     758           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong start position of extended range.", aTest.aStart == ScAddress(2,2,0));
     759           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong end position of extended range.", aTest.aEnd == ScAddress(6,5,0));
     760             : 
     761             :     ScComplexRefData aDoubleRef2;
     762           1 :     aDoubleRef2.InitRangeRel(ScRange(1,2,0,8,6,0), ScAddress(5,5,0));
     763           1 :     aDoubleRef.Extend(aDoubleRef2, ScAddress(5,5,0));
     764           1 :     aTest = aDoubleRef.toAbs(ScAddress(5,5,0));
     765             : 
     766           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong start position of extended range.", aTest.aStart == ScAddress(1,2,0));
     767           1 :     CPPUNIT_ASSERT_MESSAGE("Wrong end position of extended range.", aTest.aEnd == ScAddress(8,6,0));
     768           1 : }
     769             : 
     770           1 : void Test::testFormulaCompiler()
     771             : {
     772             :     struct {
     773             :         const char* pInput; FormulaGrammar::Grammar eInputGram;
     774             :         const char* pOutput; FormulaGrammar::Grammar eOutputGram;
     775             :     } aTests[] = {
     776             :         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "[.B1]-[.$C2]+[.D$3]-[.$E$4]", FormulaGrammar::GRAM_ODFF },
     777             :         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE },
     778             :         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE_XL_A1 },
     779             :         { "=B1-$C2+D$3-$E$4", FormulaGrammar::GRAM_NATIVE, "RC[1]-R[1]C3+R3C[3]-R4C5", FormulaGrammar::GRAM_NATIVE_XL_R1C1 },
     780           1 :     };
     781             : 
     782           5 :     for (size_t i = 0, n = SAL_N_ELEMENTS(aTests); i < n; ++i)
     783             :     {
     784           4 :         boost::scoped_ptr<ScTokenArray> pArray;
     785             :         {
     786           4 :             pArray.reset(compileFormula(m_pDoc, OUString::createFromAscii(aTests[i].pInput), NULL, aTests[i].eInputGram));
     787           4 :             CPPUNIT_ASSERT_MESSAGE("Token array shouldn't be NULL!", pArray.get());
     788             :         }
     789             : 
     790           8 :         OUString aFormula = toString(*m_pDoc, ScAddress(), *pArray, aTests[i].eOutputGram);
     791           4 :         CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(aTests[i].pOutput), aFormula);
     792           4 :     }
     793           1 : }
     794             : 
     795           1 : void Test::testFormulaCompilerJumpReordering()
     796             : {
     797             :     struct TokenCheck
     798             :     {
     799             :         OpCode meOp;
     800             :         StackVar meType;
     801             :     };
     802             : 
     803             :     // Set separators first.
     804           1 :     ScFormulaOptions aOptions;
     805           1 :     aOptions.SetFormulaSepArg(";");
     806           1 :     aOptions.SetFormulaSepArrayCol(";");
     807           1 :     aOptions.SetFormulaSepArrayRow("|");
     808           1 :     getDocShell().SetFormulaOptions(aOptions);
     809             : 
     810             :     {
     811           1 :         OUString aInput("=IF(B1;12;\"text\")");
     812             : 
     813             :         // Compile formula string first.
     814           2 :         boost::scoped_ptr<ScTokenArray> pCode(compileFormula(m_pDoc, aInput));
     815           1 :         CPPUNIT_ASSERT(pCode.get());
     816             : 
     817             :         // Then generate RPN tokens.
     818           2 :         ScCompiler aCompRPN(m_pDoc, ScAddress(), *pCode);
     819           1 :         aCompRPN.SetGrammar(FormulaGrammar::GRAM_NATIVE);
     820           1 :         aCompRPN.CompileTokenArray();
     821             : 
     822             :         // RPN tokens should be ordered: B1, ocIf, C1, ocSep, D1, ocClose.
     823             :         TokenCheck aCheckRPN[] =
     824             :         {
     825             :             { ocPush,  svSingleRef },
     826             :             { ocIf,    static_cast<formula::StackVar>(0) },
     827             :             { ocPush,  svDouble    },
     828             :             { ocSep,   static_cast<formula::StackVar>(0) },
     829             :             { ocPush,  svString    },
     830             :             { ocClose, static_cast<formula::StackVar>(0) },
     831           1 :         };
     832             : 
     833           1 :         sal_uInt16 nLen = pCode->GetCodeLen();
     834           1 :         CPPUNIT_ASSERT_MESSAGE("Wrong RPN token count.", nLen == SAL_N_ELEMENTS(aCheckRPN));
     835             : 
     836           1 :         FormulaToken** ppTokens = pCode->GetCode();
     837           7 :         for (sal_uInt16 i = 0; i < nLen; ++i)
     838             :         {
     839           6 :             const FormulaToken* p = ppTokens[i];
     840           6 :             CPPUNIT_ASSERT_EQUAL(aCheckRPN[i].meOp, p->GetOpCode());
     841           6 :             if (aCheckRPN[i].meOp == ocPush)
     842           3 :                 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN[i].meType), static_cast<int>(p->GetType()));
     843             :         }
     844             : 
     845             :         // Generate RPN tokens again, but this time no jump command reordering.
     846           1 :         pCode->DelRPN();
     847           2 :         ScCompiler aCompRPN2(m_pDoc, ScAddress(), *pCode);
     848           1 :         aCompRPN2.SetGrammar(FormulaGrammar::GRAM_NATIVE);
     849           1 :         aCompRPN2.EnableJumpCommandReorder(false);
     850           1 :         aCompRPN2.CompileTokenArray();
     851             : 
     852             :         TokenCheck aCheckRPN2[] =
     853             :         {
     854             :             { ocPush,  svSingleRef },
     855             :             { ocPush,  svDouble    },
     856             :             { ocPush,  svString    },
     857             :             { ocIf,    static_cast<formula::StackVar>(0) },
     858           1 :         };
     859             : 
     860           1 :         nLen = pCode->GetCodeLen();
     861           1 :         CPPUNIT_ASSERT_MESSAGE("Wrong RPN token count.", nLen == SAL_N_ELEMENTS(aCheckRPN2));
     862           1 :         ppTokens = pCode->GetCode();
     863           5 :         for (sal_uInt16 i = 0; i < nLen; ++i)
     864             :         {
     865           4 :             const FormulaToken* p = ppTokens[i];
     866           4 :             CPPUNIT_ASSERT_EQUAL(aCheckRPN2[i].meOp, p->GetOpCode());
     867           4 :             if (aCheckRPN[i].meOp == ocPush)
     868           2 :                 CPPUNIT_ASSERT_EQUAL(static_cast<int>(aCheckRPN2[i].meType), static_cast<int>(p->GetType()));
     869           1 :         }
     870           1 :     }
     871           1 : }
     872             : 
     873           1 : void Test::testFormulaRefUpdate()
     874             : {
     875           1 :     m_pDoc->InsertTab(0, "Formula");
     876             : 
     877           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
     878             : 
     879           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 2.0); // A1
     880           1 :     m_pDoc->SetString(ScAddress(2,2,0), "=A1");   // C3
     881           1 :     m_pDoc->SetString(ScAddress(2,3,0), "=$A$1"); // C4
     882             : 
     883           1 :     ScAddress aPos(2,2,0);
     884           1 :     if (!checkFormula(*m_pDoc, aPos, "A1"))
     885           0 :         CPPUNIT_FAIL("Wrong formula in C3.");
     886             : 
     887           1 :     aPos = ScAddress(2,3,0);
     888           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$1"))
     889           0 :         CPPUNIT_FAIL("Wrong formula in C4.");
     890             : 
     891             :     // Delete row 2 to push formula cells up (to C2:C3).
     892           1 :     m_pDoc->DeleteRow(ScRange(0,1,0,MAXCOL,1,0));
     893             : 
     894           1 :     aPos = ScAddress(2,1,0);
     895           1 :     if (!checkFormula(*m_pDoc, aPos, "A1"))
     896           0 :         CPPUNIT_FAIL("Wrong formula in C2.");
     897             : 
     898           1 :     aPos = ScAddress(2,2,0);
     899           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$1"))
     900           0 :         CPPUNIT_FAIL("Wrong formula in C3.");
     901             : 
     902             :     // Insert one row at row 2 to move them back.
     903           1 :     m_pDoc->InsertRow(ScRange(0,1,0,MAXCOL,1,0));
     904             : 
     905           1 :     aPos = ScAddress(2,2,0);
     906           1 :     if (!checkFormula(*m_pDoc, aPos, "A1"))
     907           0 :         CPPUNIT_FAIL("Wrong formula in C3.");
     908             : 
     909           1 :     aPos = ScAddress(2,3,0);
     910           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$1"))
     911           0 :         CPPUNIT_FAIL("Wrong formula in C4.");
     912             : 
     913             :     // Insert 2 rows at row 1 to shift all of A1 and C3:C4 down.
     914           1 :     m_pDoc->InsertRow(ScRange(0,0,0,MAXCOL,1,0));
     915             : 
     916           1 :     aPos = ScAddress(2,4,0);
     917           1 :     if (!checkFormula(*m_pDoc, aPos, "A3"))
     918           0 :         CPPUNIT_FAIL("Wrong formula in C5.");
     919             : 
     920           1 :     aPos = ScAddress(2,5,0);
     921           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$3"))
     922           0 :         CPPUNIT_FAIL("Wrong formula in C6.");
     923             : 
     924             :     // Delete 2 rows at row 1 to shift them back.
     925           1 :     m_pDoc->DeleteRow(ScRange(0,0,0,MAXCOL,1,0));
     926             : 
     927           1 :     aPos = ScAddress(2,2,0);
     928           1 :     if (!checkFormula(*m_pDoc, aPos, "A1"))
     929           0 :         CPPUNIT_FAIL("Wrong formula in C3.");
     930             : 
     931           1 :     aPos = ScAddress(2,3,0);
     932           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$1"))
     933           0 :         CPPUNIT_FAIL("Wrong formula in C4.");
     934             : 
     935             :     // Insert 3 columns at column B. to shift C3:C4 to F3:F4.
     936           1 :     m_pDoc->InsertCol(ScRange(1,0,0,3,MAXROW,0));
     937             : 
     938           1 :     aPos = ScAddress(5,2,0);
     939           1 :     if (!checkFormula(*m_pDoc, aPos, "A1"))
     940           0 :         CPPUNIT_FAIL("Wrong formula in F3.");
     941             : 
     942           1 :     aPos = ScAddress(5,3,0);
     943           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$1"))
     944           0 :         CPPUNIT_FAIL("Wrong formula in F4.");
     945             : 
     946             :     // Delete columns B:D to shift them back.
     947           1 :     m_pDoc->DeleteCol(ScRange(1,0,0,3,MAXROW,0));
     948             : 
     949           1 :     aPos = ScAddress(2,2,0);
     950           1 :     if (!checkFormula(*m_pDoc, aPos, "A1"))
     951           0 :         CPPUNIT_FAIL("Wrong formula in C3.");
     952             : 
     953           1 :     aPos = ScAddress(2,3,0);
     954           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$1"))
     955           0 :         CPPUNIT_FAIL("Wrong formula in C4.");
     956             : 
     957             :     // Insert cells over A1:A3 to only shift A1 down to A4.
     958           1 :     m_pDoc->InsertRow(ScRange(0,0,0,0,2,0));
     959             : 
     960           1 :     aPos = ScAddress(2,2,0);
     961           1 :     if (!checkFormula(*m_pDoc, aPos, "A4"))
     962           0 :         CPPUNIT_FAIL("Wrong formula in C3.");
     963             : 
     964           1 :     aPos = ScAddress(2,3,0);
     965           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$4"))
     966           0 :         CPPUNIT_FAIL("Wrong formula in C4.");
     967             : 
     968             :     // .. and back.
     969           1 :     m_pDoc->DeleteRow(ScRange(0,0,0,0,2,0));
     970             : 
     971           1 :     aPos = ScAddress(2,2,0);
     972           1 :     if (!checkFormula(*m_pDoc, aPos, "A1"))
     973           0 :         CPPUNIT_FAIL("Wrong formula in C3.");
     974             : 
     975           1 :     aPos = ScAddress(2,3,0);
     976           1 :     if (!checkFormula(*m_pDoc, aPos, "$A$1"))
     977           0 :         CPPUNIT_FAIL("Wrong formula in C4.");
     978             : 
     979             :     // Delete row 1 which will delete the value cell (A1).
     980           1 :     m_pDoc->DeleteRow(ScRange(0,0,0,MAXCOL,0,0));
     981             : 
     982           1 :     aPos = ScAddress(2,1,0);
     983           1 :     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(aPos);
     984           1 :     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
     985           1 :     CPPUNIT_ASSERT_EQUAL(ScErrorCodes::errNoRef, pFC->GetErrCode());
     986           1 :     aPos = ScAddress(2,2,0);
     987           1 :     pFC = m_pDoc->GetFormulaCell(aPos);
     988           1 :     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
     989           1 :     CPPUNIT_ASSERT_EQUAL(ScErrorCodes::errNoRef, pFC->GetErrCode());
     990             : 
     991             :     // Clear all and start over.
     992           1 :     clearRange(m_pDoc, ScRange(0,0,0,10,10,0));
     993             : 
     994             :     // Test range updates
     995             : 
     996             :     // Fill B2:C3 with values.
     997           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 1);
     998           1 :     m_pDoc->SetValue(ScAddress(1,2,0), 2);
     999           1 :     m_pDoc->SetValue(ScAddress(2,1,0), 3);
    1000           1 :     m_pDoc->SetValue(ScAddress(2,2,0), 4);
    1001             : 
    1002           1 :     m_pDoc->SetString(ScAddress(0,5,0), "=SUM(B2:C3)");
    1003           1 :     m_pDoc->SetString(ScAddress(0,6,0), "=SUM($B$2:$C$3)");
    1004             : 
    1005           1 :     aPos = ScAddress(0,5,0);
    1006           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
    1007           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1008             : 
    1009           1 :     aPos = ScAddress(0,6,0);
    1010           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
    1011           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1012             : 
    1013             :     // Insert a row at row 1.
    1014           1 :     m_pDoc->InsertRow(ScRange(0,0,0,MAXCOL,0,0));
    1015             : 
    1016           1 :     aPos = ScAddress(0,6,0);
    1017           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(B3:C4)"))
    1018           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1019             : 
    1020           1 :     aPos = ScAddress(0,7,0);
    1021           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM($B$3:$C$4)"))
    1022           0 :         CPPUNIT_FAIL("Wrong formula in A8.");
    1023             : 
    1024             :     // ... and back.
    1025           1 :     m_pDoc->DeleteRow(ScRange(0,0,0,MAXCOL,0,0));
    1026             : 
    1027           1 :     aPos = ScAddress(0,5,0);
    1028           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
    1029           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1030             : 
    1031           1 :     aPos = ScAddress(0,6,0);
    1032           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
    1033           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1034             : 
    1035             :     // Insert columns B:C to shift only the value range.
    1036           1 :     m_pDoc->InsertCol(ScRange(1,0,0,2,MAXROW,0));
    1037             : 
    1038           1 :     aPos = ScAddress(0,5,0);
    1039           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(D2:E3)"))
    1040           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1041             : 
    1042           1 :     aPos = ScAddress(0,6,0);
    1043           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM($D$2:$E$3)"))
    1044           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1045             : 
    1046             :     // ... and back.
    1047           1 :     m_pDoc->DeleteCol(ScRange(1,0,0,2,MAXROW,0));
    1048             : 
    1049           1 :     aPos = ScAddress(0,5,0);
    1050           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
    1051           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1052             : 
    1053           1 :     aPos = ScAddress(0,6,0);
    1054           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
    1055           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1056             : 
    1057             :     // Insert rows 5:6 to shift the formula cells only.
    1058           1 :     m_pDoc->InsertRow(ScRange(0,4,0,MAXCOL,5,0));
    1059             : 
    1060           1 :     aPos = ScAddress(0,7,0);
    1061           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
    1062           0 :         CPPUNIT_FAIL("Wrong formula in A8.");
    1063             : 
    1064           1 :     aPos = ScAddress(0,8,0);
    1065           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
    1066           0 :         CPPUNIT_FAIL("Wrong formula in A9.");
    1067             : 
    1068             :     // ... and back.
    1069           1 :     m_pDoc->DeleteRow(ScRange(0,4,0,MAXCOL,5,0));
    1070             : 
    1071           1 :     aPos = ScAddress(0,5,0);
    1072           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(B2:C3)"))
    1073           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1074             : 
    1075           1 :     aPos = ScAddress(0,6,0);
    1076           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM($B$2:$C$3)"))
    1077           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1078             : 
    1079             :     // Check the values of the formula cells in A6:A7.
    1080           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1081           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1082             : 
    1083             :     // Insert cells over B1:B2 to partially shift value range.
    1084           1 :     m_pDoc->InsertRow(ScRange(1,0,0,1,1,0));
    1085             : 
    1086             :     // Check the values of the formula cells in A6:A7 again.
    1087           1 :     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1088           1 :     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1089             : 
    1090             :     // ... and shift them back.
    1091           1 :     m_pDoc->DeleteRow(ScRange(1,0,0,1,1,0));
    1092             : 
    1093             :     // The formula cell results should be back too.
    1094           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1095           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1096             : 
    1097             :     // Delete rows 2:3 to completely remove the referenced range.
    1098           1 :     m_pDoc->DeleteRow(ScRange(0,1,0,MAXCOL,2,0));
    1099             : 
    1100             :     // Both A4 and A5 should show #REF! errors.
    1101           1 :     pFC = m_pDoc->GetFormulaCell(ScAddress(0,3,0));
    1102           1 :     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    1103           1 :     CPPUNIT_ASSERT_EQUAL(ScErrorCodes::errNoRef, pFC->GetErrCode());
    1104             : 
    1105           1 :     pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
    1106           1 :     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    1107           1 :     CPPUNIT_ASSERT_EQUAL(ScErrorCodes::errNoRef, pFC->GetErrCode());
    1108             : 
    1109           1 :     m_pDoc->DeleteTab(0);
    1110           1 : }
    1111             : 
    1112           1 : void Test::testFormulaRefUpdateRange()
    1113             : {
    1114           1 :     m_pDoc->InsertTab(0, "Formula");
    1115             : 
    1116           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    1117             : 
    1118           1 :     setExpandRefs(false);
    1119             : 
    1120             :     // Set values to B2:C5.
    1121           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 1);
    1122           1 :     m_pDoc->SetValue(ScAddress(1,2,0), 2);
    1123           1 :     m_pDoc->SetValue(ScAddress(1,3,0), 3);
    1124           1 :     m_pDoc->SetValue(ScAddress(1,4,0), 4);
    1125           1 :     m_pDoc->SetValue(ScAddress(2,1,0), 5);
    1126           1 :     m_pDoc->SetValue(ScAddress(2,2,0), 6);
    1127           1 :     m_pDoc->SetValue(ScAddress(2,3,0), 7);
    1128           1 :     m_pDoc->SetValue(ScAddress(2,4,0), 8);
    1129             : 
    1130             :     // Set formula cells to A7 and A8.
    1131           1 :     m_pDoc->SetString(ScAddress(0,6,0), "=SUM(B2:C5)");
    1132           1 :     m_pDoc->SetString(ScAddress(0,7,0), "=SUM($B$2:$C$5)");
    1133             : 
    1134           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "SUM(B2:C5)"))
    1135           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1136             : 
    1137           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "SUM($B$2:$C$5)"))
    1138           0 :         CPPUNIT_FAIL("Wrong formula in A8.");
    1139             : 
    1140           1 :     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1141           1 :     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,7,0)));
    1142             : 
    1143             :     // Delete row 3. This should shrink the range references by one row.
    1144           1 :     m_pDoc->DeleteRow(ScRange(0,2,0,MAXCOL,2,0));
    1145             : 
    1146           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM(B2:C4)"))
    1147           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1148             : 
    1149           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "SUM($B$2:$C$4)"))
    1150           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1151             : 
    1152           1 :     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1153           1 :     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1154             : 
    1155             :     // Delete row 4 - bottom of range
    1156           1 :     m_pDoc->DeleteRow(ScRange(0,3,0,MAXCOL,3,0));
    1157             : 
    1158           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(B2:C3)"))
    1159           0 :         CPPUNIT_FAIL("Wrong formula in A5.");
    1160             : 
    1161           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($B$2:$C$3)"))
    1162           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1163             : 
    1164           1 :     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,4,0)));
    1165           1 :     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1166             : 
    1167             :     // Delete row 2 - top of range
    1168           1 :     m_pDoc->DeleteRow(ScRange(0,1,0,MAXCOL,1,0));
    1169             : 
    1170           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,3,0), "SUM(B2:C2)"))
    1171           0 :         CPPUNIT_FAIL("Wrong formula in A4.");
    1172             : 
    1173           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM($B$2:$C$2)"))
    1174           0 :         CPPUNIT_FAIL("Wrong formula in A5.");
    1175             : 
    1176           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,3,0)));
    1177           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,4,0)));
    1178             : 
    1179             :     // Clear the range and start over.
    1180           1 :     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
    1181             : 
    1182             :     // Fill C2:F3 with values.
    1183           1 :     m_pDoc->SetValue(ScAddress(2,1,0), 1);
    1184           1 :     m_pDoc->SetValue(ScAddress(3,1,0), 2);
    1185           1 :     m_pDoc->SetValue(ScAddress(4,1,0), 3);
    1186           1 :     m_pDoc->SetValue(ScAddress(5,1,0), 4);
    1187           1 :     m_pDoc->SetValue(ScAddress(2,2,0), 5);
    1188           1 :     m_pDoc->SetValue(ScAddress(3,2,0), 6);
    1189           1 :     m_pDoc->SetValue(ScAddress(4,2,0), 7);
    1190           1 :     m_pDoc->SetValue(ScAddress(5,2,0), 8);
    1191             : 
    1192             :     // Set formulas to A2 and A3.
    1193           1 :     m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C2:F3)");
    1194           1 :     m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$2:$F$3)");
    1195             : 
    1196           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C2:F3)"))
    1197           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1198             : 
    1199           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$F$3)"))
    1200           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1201             : 
    1202           1 :     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    1203           1 :     CPPUNIT_ASSERT_EQUAL(36.0, m_pDoc->GetValue(ScAddress(0,2,0)));
    1204             : 
    1205             :     // Delete column D.
    1206           1 :     m_pDoc->DeleteCol(ScRange(3,0,0,3,MAXROW,0));
    1207             : 
    1208           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C2:E3)"))
    1209           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1210             : 
    1211           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$E$3)"))
    1212           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1213             : 
    1214           1 :     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    1215           1 :     CPPUNIT_ASSERT_EQUAL(28.0, m_pDoc->GetValue(ScAddress(0,2,0)));
    1216             : 
    1217             :     // Delete column E - the right edge of reference range.
    1218           1 :     m_pDoc->DeleteCol(ScRange(4,0,0,4,MAXROW,0));
    1219             : 
    1220           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C2:D3)"))
    1221           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1222             : 
    1223           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$D$3)"))
    1224           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1225             : 
    1226           1 :     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    1227           1 :     CPPUNIT_ASSERT_EQUAL(16.0, m_pDoc->GetValue(ScAddress(0,2,0)));
    1228             : 
    1229             :     // Delete column C - the left edge of reference range.
    1230           1 :     m_pDoc->DeleteCol(ScRange(2,0,0,2,MAXROW,0));
    1231             : 
    1232           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C2:C3)"))
    1233           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1234             : 
    1235           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$2:$C$3)"))
    1236           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1237             : 
    1238           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    1239           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,2,0)));
    1240             : 
    1241             :     // Clear the range and start over.
    1242           1 :     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
    1243             : 
    1244             :     // Disable expansion of range reference on insertion in adjacent areas.
    1245           1 :     setExpandRefs(false);
    1246             : 
    1247             :     // Fill C2:D3 with values.
    1248           1 :     m_pDoc->SetValue(ScAddress(2,1,0), 1);
    1249           1 :     m_pDoc->SetValue(ScAddress(3,1,0), 2);
    1250           1 :     m_pDoc->SetValue(ScAddress(2,2,0), 3);
    1251           1 :     m_pDoc->SetValue(ScAddress(3,2,0), 4);
    1252             : 
    1253             :     // Set formulas at A5 and A6.
    1254           1 :     m_pDoc->SetString(ScAddress(0,4,0), "=SUM(C2:D3)");
    1255           1 :     m_pDoc->SetString(ScAddress(0,5,0), "=SUM($C$2:$D$3)");
    1256             : 
    1257           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)"))
    1258           0 :         CPPUNIT_FAIL("Wrong formula in A5.");
    1259             : 
    1260           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)"))
    1261           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1262             : 
    1263             :     // Insert a column at column C. This should simply shift the reference without expansion.
    1264           1 :     m_pDoc->InsertCol(ScRange(2,0,0,2,MAXROW,0));
    1265             : 
    1266           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(D2:E3)"))
    1267           0 :         CPPUNIT_FAIL("Wrong formula in A5.");
    1268             : 
    1269           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($D$2:$E$3)"))
    1270           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1271             : 
    1272             :     // Shift it back.
    1273           1 :     m_pDoc->DeleteCol(ScRange(2,0,0,2,MAXROW,0));
    1274             : 
    1275           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(C2:D3)"))
    1276           0 :         CPPUNIT_FAIL("Wrong formula in A5.");
    1277             : 
    1278           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$D$3)"))
    1279           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1280             : 
    1281             :     // Insert at column D. This should expand the reference by one column length.
    1282           1 :     m_pDoc->InsertCol(ScRange(3,0,0,3,MAXROW,0));
    1283             : 
    1284           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)"))
    1285           0 :         CPPUNIT_FAIL("Wrong formula in A5.");
    1286             : 
    1287           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)"))
    1288           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1289             : 
    1290             :     // Insert at column F. No expansion should occur since the edge expansion is turned off.
    1291           1 :     m_pDoc->InsertCol(ScRange(5,0,0,5,MAXROW,0));
    1292             : 
    1293           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,4,0), "SUM(C2:E3)"))
    1294           0 :         CPPUNIT_FAIL("Wrong formula in A5.");
    1295             : 
    1296           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM($C$2:$E$3)"))
    1297           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1298             : 
    1299             :     // Insert at row 2. No expansion should occur with edge expansion turned off.
    1300           1 :     m_pDoc->InsertRow(ScRange(0,1,0,MAXCOL,1,0));
    1301             : 
    1302           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "SUM(C3:E4)"))
    1303           0 :         CPPUNIT_FAIL("Wrong formula in A6.");
    1304             : 
    1305           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "SUM($C$3:$E$4)"))
    1306           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1307             : 
    1308             :     // Insert at row 4 to expand the reference range.
    1309           1 :     m_pDoc->InsertRow(ScRange(0,3,0,MAXCOL,3,0));
    1310             : 
    1311           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "SUM(C3:E5)"))
    1312           0 :         CPPUNIT_FAIL("Wrong formula in A7.");
    1313             : 
    1314           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "SUM($C$3:$E$5)"))
    1315           0 :         CPPUNIT_FAIL("Wrong formula in A8.");
    1316             : 
    1317             :     // Insert at row 6. No expansion with edge expansion turned off.
    1318           1 :     m_pDoc->InsertRow(ScRange(0,5,0,MAXCOL,5,0));
    1319             : 
    1320           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "SUM(C3:E5)"))
    1321           0 :         CPPUNIT_FAIL("Wrong formula in A8.");
    1322             : 
    1323           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,8,0), "SUM($C$3:$E$5)"))
    1324           0 :         CPPUNIT_FAIL("Wrong formula in A9.");
    1325             : 
    1326             :     // Clear the range and start over.
    1327           1 :     clearRange(m_pDoc, ScRange(0,0,0,20,20,0));
    1328             : 
    1329             :     // Turn edge expansion on.
    1330           1 :     setExpandRefs(true);
    1331             : 
    1332             :     // Fill C6:D7 with values.
    1333           1 :     m_pDoc->SetValue(ScAddress(2,5,0), 1);
    1334           1 :     m_pDoc->SetValue(ScAddress(2,6,0), 2);
    1335           1 :     m_pDoc->SetValue(ScAddress(3,5,0), 3);
    1336           1 :     m_pDoc->SetValue(ScAddress(3,6,0), 4);
    1337             : 
    1338             :     // Set formulas at A2 and A3.
    1339           1 :     m_pDoc->SetString(ScAddress(0,1,0), "=SUM(C6:D7)");
    1340           1 :     m_pDoc->SetString(ScAddress(0,2,0), "=SUM($C$6:$D$7)");
    1341             : 
    1342           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:D7)"))
    1343           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1344             : 
    1345           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$D$7)"))
    1346           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1347             : 
    1348             :     // Insert at column E. This should expand the reference range by one column.
    1349           1 :     m_pDoc->InsertCol(ScRange(4,0,0,4,MAXROW,0));
    1350             : 
    1351           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:E7)"))
    1352           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1353             : 
    1354           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$E$7)"))
    1355           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1356             : 
    1357             :     // Insert at column C to edge-expand the reference range.
    1358           1 :     m_pDoc->InsertCol(ScRange(2,0,0,2,MAXROW,0));
    1359             : 
    1360           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F7)"))
    1361           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1362             : 
    1363           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$7)"))
    1364           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1365             : 
    1366             :     // Insert at row 8 to edge-expand.
    1367           1 :     m_pDoc->InsertRow(ScRange(0,7,0,MAXCOL,7,0));
    1368             : 
    1369           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F8)"))
    1370           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1371             : 
    1372           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$8)"))
    1373           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1374             : 
    1375             :     // Insert at row 6 to edge-expand.
    1376           1 :     m_pDoc->InsertRow(ScRange(0,5,0,MAXCOL,5,0));
    1377             : 
    1378           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "SUM(C6:F9)"))
    1379           0 :         CPPUNIT_FAIL("Wrong formula in A2.");
    1380             : 
    1381           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,2,0), "SUM($C$6:$F$9)"))
    1382           0 :         CPPUNIT_FAIL("Wrong formula in A3.");
    1383             : 
    1384           1 :     m_pDoc->DeleteTab(0);
    1385           1 : }
    1386             : 
    1387           1 : void Test::testFormulaRefUpdateSheets()
    1388             : {
    1389           1 :     m_pDoc->InsertTab(0, "Sheet1");
    1390           1 :     m_pDoc->InsertTab(1, "Sheet2");
    1391             : 
    1392           1 :     OUString aName;
    1393           1 :     m_pDoc->GetName(0, aName);
    1394           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
    1395           1 :     m_pDoc->GetName(1, aName);
    1396           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
    1397             : 
    1398           2 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    1399             : 
    1400             :     // Set values to B2:C3 on sheet Sheet1.
    1401           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 1);
    1402           1 :     m_pDoc->SetValue(ScAddress(1,2,0), 2);
    1403           1 :     m_pDoc->SetValue(ScAddress(2,1,0), 3);
    1404           1 :     m_pDoc->SetValue(ScAddress(2,2,0), 4);
    1405             : 
    1406             :     // Set formulas to B2 and B3 on sheet Sheet2.
    1407           1 :     m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.B2:C3)");
    1408           1 :     m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.$B$2:$C$3)");
    1409             : 
    1410           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
    1411           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1412             : 
    1413           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
    1414           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1415             : 
    1416             :     // Swap the sheets.
    1417           1 :     m_pDoc->MoveTab(0, 1);
    1418           1 :     m_pDoc->GetName(0, aName);
    1419           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
    1420           1 :     m_pDoc->GetName(1, aName);
    1421           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
    1422             : 
    1423           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)"))
    1424           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1425             : 
    1426           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)"))
    1427           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1428             : 
    1429             :     // Swap back.
    1430           1 :     m_pDoc->MoveTab(0, 1);
    1431           1 :     m_pDoc->GetName(0, aName);
    1432           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
    1433           1 :     m_pDoc->GetName(1, aName);
    1434           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
    1435             : 
    1436           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
    1437           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1438             : 
    1439           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
    1440           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1441             : 
    1442             :     // Insert a new sheet between the two.
    1443           1 :     m_pDoc->InsertTab(1, "Temp");
    1444             : 
    1445           1 :     m_pDoc->GetName(1, aName);
    1446           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Temp"), aName);
    1447           1 :     m_pDoc->GetName(2, aName);
    1448           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
    1449             : 
    1450           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)"))
    1451           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1452             : 
    1453           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)"))
    1454           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1455             : 
    1456             :     // Move the last sheet (Sheet2) to the first position.
    1457           1 :     m_pDoc->MoveTab(2, 0);
    1458             : 
    1459           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet1.B2:C3)"))
    1460           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1461             : 
    1462           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet1.$B$2:$C$3)"))
    1463           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1464             : 
    1465             :     // Move back.
    1466           1 :     m_pDoc->MoveTab(0, 2);
    1467             : 
    1468           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)"))
    1469           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1470             : 
    1471           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)"))
    1472           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1473             : 
    1474             :     // Move the "Temp" sheet to the last position.
    1475           1 :     m_pDoc->MoveTab(1, 2);
    1476             : 
    1477           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
    1478           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1479             : 
    1480           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
    1481           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1482             : 
    1483             :     // Move back.
    1484           1 :     m_pDoc->MoveTab(2, 1);
    1485             : 
    1486             :     // Delete the temporary sheet.
    1487           1 :     m_pDoc->DeleteTab(1);
    1488             : 
    1489           1 :     m_pDoc->GetName(1, aName);
    1490           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
    1491             : 
    1492           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
    1493           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1494             : 
    1495           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
    1496           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1497             : 
    1498             :     // Insert a new sheet before the first one.
    1499           1 :     m_pDoc->InsertTab(0, "Temp");
    1500             : 
    1501           1 :     m_pDoc->GetName(1, aName);
    1502           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet1"), aName);
    1503           1 :     m_pDoc->GetName(2, aName);
    1504           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
    1505             : 
    1506           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,2), "SUM(Sheet1.B2:C3)"))
    1507           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1508             : 
    1509           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,2), "SUM($Sheet1.$B$2:$C$3)"))
    1510           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1511             : 
    1512             :     // Delete the temporary sheet.
    1513           1 :     m_pDoc->DeleteTab(0);
    1514             : 
    1515           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
    1516           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1517             : 
    1518           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
    1519           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1520             : 
    1521             :     // Append a bunch of sheets.
    1522           1 :     m_pDoc->InsertTab(2, "Temp1");
    1523           1 :     m_pDoc->InsertTab(3, "Temp2");
    1524           1 :     m_pDoc->InsertTab(4, "Temp3");
    1525             : 
    1526             :     // Move these tabs around. This shouldn't affects the first 2 sheets.
    1527           1 :     m_pDoc->MoveTab(2, 4);
    1528           1 :     m_pDoc->MoveTab(3, 2);
    1529             : 
    1530           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.B2:C3)"))
    1531           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1532             : 
    1533           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.$B$2:$C$3)"))
    1534           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1535             : 
    1536             :     // Delete the temp sheets.
    1537           1 :     m_pDoc->DeleteTab(4);
    1538           1 :     m_pDoc->DeleteTab(3);
    1539           1 :     m_pDoc->DeleteTab(2);
    1540             : 
    1541             :     // Delete Sheet1.
    1542           1 :     m_pDoc->DeleteTab(0);
    1543           1 :     m_pDoc->GetName(0, aName);
    1544           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Sheet2"), aName);
    1545             : 
    1546           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "SUM(#REF!.B2:C3)"))
    1547           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B2.");
    1548             : 
    1549           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "SUM($#REF!.$B$2:$C$3)"))
    1550           0 :         CPPUNIT_FAIL("Wrong formula in Sheet2.B3.");
    1551             : 
    1552           2 :     m_pDoc->DeleteTab(0);
    1553           1 : }
    1554             : 
    1555           1 : void Test::testFormulaRefUpdateInsertRows()
    1556             : {
    1557           1 :     setExpandRefs(false);
    1558             : 
    1559           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    1560           1 :     m_pDoc->InsertTab(0, "Formula");
    1561             : 
    1562             :     // Insert raw values in B2:B4.
    1563           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
    1564           1 :     m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
    1565           1 :     m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
    1566             : 
    1567             :     // Insert a formula in B5 to sum up B2:B4.
    1568           1 :     m_pDoc->SetString(ScAddress(1,4,0), "=SUM(B2:B4)");
    1569             : 
    1570           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,4,0)));
    1571             : 
    1572             :     // Insert rows over rows 1:2.
    1573           2 :     ScMarkData aMark;
    1574           1 :     aMark.SelectOneTable(0);
    1575           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    1576           1 :     rFunc.InsertCells(ScRange(0,0,0,MAXCOL,1,0), &aMark, INS_INSROWS_BEFORE, false, true, false);
    1577             : 
    1578             :     // The raw data should have shifted to B4:B6.
    1579           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
    1580           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,4,0)));
    1581           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,5,0)));
    1582             : 
    1583           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,6,0), "SUM(B4:B6)"))
    1584           0 :         CPPUNIT_FAIL("Wrong formula!");
    1585             : 
    1586             :     // Clear and start over.
    1587           1 :     clearSheet(m_pDoc, 0);
    1588             : 
    1589             :     // Set raw values in A4:A6.
    1590           1 :     m_pDoc->SetValue(ScAddress(0,3,0), 1.0);
    1591           1 :     m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
    1592           1 :     m_pDoc->SetValue(ScAddress(0,5,0), 3.0);
    1593             : 
    1594             :     // Set formula in A3 to reference A4:A6.
    1595           1 :     m_pDoc->SetString(ScAddress(0,2,0), "=MAX(A4:A6)");
    1596             : 
    1597           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,2,0)));
    1598             : 
    1599             :     // Insert 3 rows over 2:4.  This should push A3:A6 to A6:A9.
    1600           1 :     rFunc.InsertCells(ScRange(0,1,0,MAXCOL,3,0), &aMark, INS_INSROWS_BEFORE, false, true, false);
    1601           1 :     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
    1602           1 :     CPPUNIT_ASSERT(pFC);
    1603           1 :     CPPUNIT_ASSERT_MESSAGE("This formula cell should not be an error.", pFC->GetErrCode() == 0);
    1604           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1605             : 
    1606           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "MAX(A7:A9)"))
    1607           0 :         CPPUNIT_FAIL("Wrong formula!");
    1608             : 
    1609           2 :     m_pDoc->DeleteTab(0);
    1610           1 : }
    1611             : 
    1612           1 : void Test::testFormulaRefUpdateSheetsDelete()
    1613             : {
    1614           1 :     m_pDoc->InsertTab(0, "Sheet1");
    1615           1 :     m_pDoc->InsertTab(1, "Sheet2");
    1616           1 :     m_pDoc->InsertTab(2, "Sheet3");
    1617           1 :     m_pDoc->InsertTab(3, "Sheet4");
    1618             : 
    1619           1 :     m_pDoc->SetString(ScAddress(4,1,0), "=SUM(Sheet2.A4:Sheet4.A4)");
    1620           1 :     m_pDoc->SetString(ScAddress(4,2,0), "=SUM($Sheet2.A4:$Sheet4.A4)");
    1621           1 :     m_pDoc->DeleteTab(1);
    1622           1 :     if (!checkFormula(*m_pDoc, ScAddress(4,1,0), "SUM(Sheet3.A4:Sheet4.A4)"))
    1623           0 :         CPPUNIT_FAIL("Wrong Formula");
    1624           1 :     if (!checkFormula(*m_pDoc, ScAddress(4,2,0), "SUM($Sheet3.A4:$Sheet4.A4)"))
    1625           0 :         CPPUNIT_FAIL("Wrong Formula");
    1626           1 :     m_pDoc->InsertTab(1, "Sheet2");
    1627             : 
    1628           1 :     m_pDoc->SetString(ScAddress(5,1,3), "=SUM(Sheet1.A5:Sheet3.A5)");
    1629           1 :     m_pDoc->SetString(ScAddress(5,2,3), "=SUM($Sheet1.A5:$Sheet3.A5)");
    1630           1 :     m_pDoc->DeleteTab(2);
    1631           1 :     if (!checkFormula(*m_pDoc, ScAddress(5,1,2), "SUM(Sheet1.A5:Sheet2.A5)"))
    1632           0 :         CPPUNIT_FAIL("Wrong Formula");
    1633           1 :     if (!checkFormula(*m_pDoc, ScAddress(5,2,2), "SUM($Sheet1.A5:$Sheet2.A5)"))
    1634           0 :         CPPUNIT_FAIL("Wrong Formula");
    1635           1 :     m_pDoc->InsertTab(2, "Sheet3");
    1636             : 
    1637           1 :     m_pDoc->SetString(ScAddress(6,1,3), "=SUM(Sheet1.A6:Sheet3.A6)");
    1638           1 :     m_pDoc->SetString(ScAddress(6,2,3), "=SUM($Sheet1.A6:$Sheet3.A6)");
    1639           1 :     m_pDoc->DeleteTabs(0,3);
    1640           1 :     if (!checkFormula(*m_pDoc, ScAddress(6,1,0), "SUM(#REF!.A6:#REF!.A6)"))
    1641           0 :         CPPUNIT_FAIL("Wrong Formula");
    1642           1 :     if (!checkFormula(*m_pDoc, ScAddress(6,2,0), "SUM($#REF!.A6:$#REF!.A6)"))
    1643           0 :         CPPUNIT_FAIL("Wrong Formula");
    1644           1 :     m_pDoc->InsertTab(0, "Sheet1");
    1645           1 :     m_pDoc->InsertTab(1, "Sheet2");
    1646           1 :     m_pDoc->InsertTab(2, "Sheet3");
    1647             : 
    1648           1 :     m_pDoc->SetString(ScAddress(1,1,1), "=SUM(Sheet1.A2:Sheet3.A2");
    1649           1 :     m_pDoc->SetString(ScAddress(2,1,1), "=SUM(Sheet1.A1:Sheet2.A1");
    1650           1 :     m_pDoc->SetString(ScAddress(3,1,1), "=SUM(Sheet2.A3:Sheet4.A3");
    1651             : 
    1652           1 :     m_pDoc->SetString(ScAddress(1,2,1), "=SUM($Sheet1.A2:$Sheet3.A2");
    1653           1 :     m_pDoc->SetString(ScAddress(2,2,1), "=SUM($Sheet1.A1:$Sheet2.A1");
    1654           1 :     m_pDoc->SetString(ScAddress(3,2,1), "=SUM($Sheet2.A3:$Sheet4.A3");
    1655             : 
    1656           1 :     m_pDoc->DeleteTab(2);
    1657             : 
    1658           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,1), "SUM(Sheet1.A2:Sheet2.A2)"))
    1659           0 :         CPPUNIT_FAIL("Wrong Formula");
    1660             : 
    1661           1 :     if (!checkFormula(*m_pDoc, ScAddress(2,1,1), "SUM(Sheet1.A1:Sheet2.A1)"))
    1662           0 :         CPPUNIT_FAIL("Wrong Formula");
    1663             : 
    1664           1 :     if (!checkFormula(*m_pDoc, ScAddress(3,1,1), "SUM(Sheet2.A3:Sheet4.A3)"))
    1665           0 :         CPPUNIT_FAIL("Wrong Formula");
    1666             : 
    1667           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,1), "SUM($Sheet1.A2:$Sheet2.A2)"))
    1668           0 :         CPPUNIT_FAIL("Wrong Formula");
    1669             : 
    1670           1 :     if (!checkFormula(*m_pDoc, ScAddress(2,2,1), "SUM($Sheet1.A1:$Sheet2.A1)"))
    1671           0 :         CPPUNIT_FAIL("Wrong Formula");
    1672             : 
    1673           1 :     if (!checkFormula(*m_pDoc, ScAddress(3,2,1), "SUM($Sheet2.A3:$Sheet4.A3)"))
    1674           0 :         CPPUNIT_FAIL("Wrong Formula");
    1675             : 
    1676           1 :     m_pDoc->DeleteTab(0);
    1677             : 
    1678           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "SUM(Sheet2.A2:Sheet2.A2)"))
    1679           0 :         CPPUNIT_FAIL("Wrong Formula");
    1680             : 
    1681           1 :     if (!checkFormula(*m_pDoc, ScAddress(2,1,0), "SUM(Sheet2.A1:Sheet2.A1)"))
    1682           0 :         CPPUNIT_FAIL("Wrong Formula");
    1683             : 
    1684           1 :     if (!checkFormula(*m_pDoc, ScAddress(3,1,0), "SUM(Sheet2.A3:Sheet4.A3)"))
    1685           0 :         CPPUNIT_FAIL("Wrong Formula");
    1686             : 
    1687           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "SUM($Sheet2.A2:$Sheet2.A2)"))
    1688           0 :         CPPUNIT_FAIL("Wrong Formula");
    1689             : 
    1690           1 :     if (!checkFormula(*m_pDoc, ScAddress(2,2,0), "SUM($Sheet2.A1:$Sheet2.A1)"))
    1691           0 :         CPPUNIT_FAIL("Wrong Formula");
    1692             : 
    1693           1 :     if (!checkFormula(*m_pDoc, ScAddress(3,2,0), "SUM($Sheet2.A3:$Sheet4.A3)"))
    1694           0 :         CPPUNIT_FAIL("Wrong Formula");
    1695             : 
    1696           1 :     m_pDoc->DeleteTab(0);
    1697           1 :     m_pDoc->DeleteTab(0);
    1698           1 : }
    1699             : 
    1700           1 : void Test::testFormulaRefUpdateInsertColumns()
    1701             : {
    1702           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    1703           1 :     setExpandRefs(false);
    1704             : 
    1705           1 :     m_pDoc->InsertTab(0, "Formula");
    1706             : 
    1707             :     // Set values in B1:B3.
    1708           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
    1709           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
    1710           1 :     m_pDoc->SetValue(ScAddress(1,2,0), 3.0);
    1711             : 
    1712             :     // Reference them in B4.
    1713           1 :     m_pDoc->SetString(ScAddress(1,3,0), "=SUM(B1:B3)");
    1714           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,3,0)));
    1715             : 
    1716             :     // Inert columns over A:B.
    1717           2 :     ScMarkData aMark;
    1718           1 :     aMark.SelectOneTable(0);
    1719           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    1720           1 :     rFunc.InsertCells(ScRange(0,0,0,1,MAXROW,0), &aMark, INS_INSCOLS_BEFORE, false, true, false);
    1721             : 
    1722             :     // Now, the original column B has moved to column D.
    1723           1 :     if (!checkFormula(*m_pDoc, ScAddress(3,3,0), "SUM(D1:D3)"))
    1724           0 :         CPPUNIT_FAIL("Wrong formula in D4 after column insertion.");
    1725             : 
    1726           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(3,3,0)));
    1727             : 
    1728           2 :     m_pDoc->DeleteTab(0);
    1729           1 : }
    1730             : 
    1731           1 : void Test::testFormulaRefUpdateMove()
    1732             : {
    1733           1 :     m_pDoc->InsertTab(0, "Sheet1");
    1734             : 
    1735           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    1736             : 
    1737             :     // Set value to B4:B6.
    1738           1 :     m_pDoc->SetValue(ScAddress(1,3,0), 1);
    1739           1 :     m_pDoc->SetValue(ScAddress(1,4,0), 2);
    1740           1 :     m_pDoc->SetValue(ScAddress(1,5,0), 3);
    1741             : 
    1742             :     // Set formulas to A9:A12 that references B4:B6.
    1743           1 :     m_pDoc->SetString(ScAddress(0,8,0), "=SUM(B4:B6)");
    1744           1 :     m_pDoc->SetString(ScAddress(0,9,0), "=SUM($B$4:$B$6)");
    1745           1 :     m_pDoc->SetString(ScAddress(0,10,0), "=B5");
    1746           1 :     m_pDoc->SetString(ScAddress(0,11,0), "=$B$6");
    1747             : 
    1748           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
    1749           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
    1750           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
    1751           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
    1752             : 
    1753             :     // Move B4:B6 to D4 (two columsn to the right).
    1754           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    1755           1 :     bool bMoved = rFunc.MoveBlock(ScRange(1,3,0,1,5,0), ScAddress(3,3,0), true, false, false, false);
    1756           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to move B4:B6.", bMoved);
    1757             : 
    1758             :     // The results of the formula cells that reference the moved range should remain the same.
    1759           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,8,0));
    1760           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,9,0));
    1761           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(0,10,0));
    1762           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(0,11,0));
    1763             : 
    1764           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,8,0), "SUM(D4:D6)"))
    1765           0 :         CPPUNIT_FAIL("Wrong formula.");
    1766             : 
    1767           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,9,0), "SUM($D$4:$D$6)"))
    1768           0 :         CPPUNIT_FAIL("Wrong formula.");
    1769             : 
    1770           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,10,0), "D5"))
    1771           0 :         CPPUNIT_FAIL("Wrong formula.");
    1772             : 
    1773           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,11,0), "$D$6"))
    1774           0 :         CPPUNIT_FAIL("Wrong formula.");
    1775             : 
    1776             :     // Move A9:A12 to B10:B13.
    1777           1 :     bMoved = rFunc.MoveBlock(ScRange(0,8,0,0,11,0), ScAddress(1,9,0), true, false, false, false);
    1778           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to move A9:A12 to B10:B13", bMoved);
    1779             : 
    1780             :     // The results of these formula cells should still stay the same.
    1781           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,9,0));
    1782           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(1,10,0));
    1783           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
    1784           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
    1785             : 
    1786             :     // Displayed formulas should stay the same since the referenced range hasn't moved.
    1787           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)"))
    1788           0 :         CPPUNIT_FAIL("Wrong formula.");
    1789             : 
    1790           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)"))
    1791           0 :         CPPUNIT_FAIL("Wrong formula.");
    1792             : 
    1793           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,11,0), "D5"))
    1794           0 :         CPPUNIT_FAIL("Wrong formula.");
    1795             : 
    1796           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,12,0), "$D$6"))
    1797           0 :         CPPUNIT_FAIL("Wrong formula.");
    1798             : 
    1799             :     // The value cells are in D4:D6. Move D4:D5 to the right but leave D6
    1800             :     // where it is.
    1801           1 :     bMoved = rFunc.MoveBlock(ScRange(3,3,0,3,4,0), ScAddress(4,3,0), true, false, false, false);
    1802           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to move D4:D5 to E4:E5", bMoved);
    1803             : 
    1804             :     // Only the values of B10 and B11 should be updated.
    1805           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,9,0));
    1806           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,10,0));
    1807           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1,11,0));
    1808           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(1,12,0));
    1809             : 
    1810           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,9,0), "SUM(D4:D6)"))
    1811           0 :         CPPUNIT_FAIL("Wrong formula.");
    1812             : 
    1813           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,10,0), "SUM($D$4:$D$6)"))
    1814           0 :         CPPUNIT_FAIL("Wrong formula.");
    1815             : 
    1816           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,11,0), "E5"))
    1817           0 :         CPPUNIT_FAIL("Wrong formula.");
    1818             : 
    1819           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,12,0), "$D$6"))
    1820           0 :         CPPUNIT_FAIL("Wrong formula.");
    1821             : 
    1822           1 :     m_pDoc->DeleteTab(0);
    1823           1 : }
    1824             : 
    1825           1 : void Test::testFormulaRefUpdateMoveUndo()
    1826             : {
    1827           1 :     m_pDoc->InsertTab(0, "Test");
    1828             : 
    1829           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    1830             : 
    1831             :     // Set values in A1:A4.
    1832           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
    1833           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
    1834           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
    1835           1 :     m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
    1836             : 
    1837             :     // Set formulas with single cell references in A6:A8.
    1838           1 :     m_pDoc->SetString(ScAddress(0,5,0), "=A1");
    1839           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1840           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "A1"))
    1841           0 :         CPPUNIT_FAIL("Wrong formula.");
    1842             : 
    1843           1 :     m_pDoc->SetString(ScAddress(0,6,0), "=A1+A2+A3");
    1844           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1845           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3"))
    1846           0 :         CPPUNIT_FAIL("Wrong formula.");
    1847             : 
    1848           1 :     m_pDoc->SetString(ScAddress(0,7,0), "=A1+A3+A4");
    1849           1 :     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
    1850           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4"))
    1851           0 :         CPPUNIT_FAIL("Wrong formula.");
    1852             : 
    1853             :     // Set formulas with range references in A10:A12.
    1854           1 :     m_pDoc->SetString(ScAddress(0,9,0), "=SUM(A1:A2)");
    1855           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    1856           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)"))
    1857           0 :         CPPUNIT_FAIL("Wrong formula.");
    1858             : 
    1859           1 :     m_pDoc->SetString(ScAddress(0,10,0), "=SUM(A1:A3)");
    1860           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
    1861           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)"))
    1862           0 :         CPPUNIT_FAIL("Wrong formula.");
    1863             : 
    1864           1 :     m_pDoc->SetString(ScAddress(0,11,0), "=SUM(A1:A4)");
    1865           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
    1866           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)"))
    1867           0 :         CPPUNIT_FAIL("Wrong formula.");
    1868             : 
    1869             :     // Move A1:A3 to C1:C3. Note that A4 remains.
    1870           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    1871           1 :     bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,2,0), ScAddress(2,0,0), true, true, false, true);
    1872           1 :     CPPUNIT_ASSERT(bMoved);
    1873             : 
    1874           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1875           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "C1"))
    1876           0 :         CPPUNIT_FAIL("Wrong formula.");
    1877             : 
    1878           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1879           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "C1+C2+C3"))
    1880           0 :         CPPUNIT_FAIL("Wrong formula.");
    1881             : 
    1882           1 :     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
    1883           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "C1+C3+A4"))
    1884           0 :         CPPUNIT_FAIL("Wrong formula.");
    1885             : 
    1886           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    1887           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,9,0), "SUM(C1:C2)"))
    1888           0 :         CPPUNIT_FAIL("Wrong formula.");
    1889             : 
    1890           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
    1891           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,10,0), "SUM(C1:C3)"))
    1892           0 :         CPPUNIT_FAIL("Wrong formula.");
    1893             : 
    1894           1 :     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,11,0)));
    1895           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)"))
    1896           0 :         CPPUNIT_FAIL("Wrong formula.");
    1897             : 
    1898             :     // Undo the move.
    1899           1 :     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
    1900           1 :     CPPUNIT_ASSERT(pUndoMgr);
    1901           1 :     pUndoMgr->Undo();
    1902             : 
    1903           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1904           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,5,0), "A1"))
    1905           0 :         CPPUNIT_FAIL("Wrong formula.");
    1906             : 
    1907           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1908           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,6,0), "A1+A2+A3"))
    1909           0 :         CPPUNIT_FAIL("Wrong formula.");
    1910             : 
    1911           1 :     CPPUNIT_ASSERT_EQUAL(8.0, m_pDoc->GetValue(ScAddress(0,7,0)));
    1912           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,7,0), "A1+A3+A4"))
    1913           0 :         CPPUNIT_FAIL("Wrong formula.");
    1914             : 
    1915           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    1916           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,9,0), "SUM(A1:A2)"))
    1917           0 :         CPPUNIT_FAIL("Wrong formula.");
    1918             : 
    1919           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
    1920           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,10,0), "SUM(A1:A3)"))
    1921           0 :         CPPUNIT_FAIL("Wrong formula.");
    1922             : 
    1923           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,11,0)));
    1924           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,11,0), "SUM(A1:A4)"))
    1925           0 :         CPPUNIT_FAIL("Wrong formula.");
    1926             : 
    1927             :     // Make sure the broadcasters are still valid by changing the value of A1.
    1928           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 20);
    1929             : 
    1930           1 :     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    1931           1 :     CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    1932           1 :     CPPUNIT_ASSERT_EQUAL(27.0, m_pDoc->GetValue(ScAddress(0,7,0)));
    1933             : 
    1934           1 :     CPPUNIT_ASSERT_EQUAL(22.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    1935           1 :     CPPUNIT_ASSERT_EQUAL(25.0, m_pDoc->GetValue(ScAddress(0,10,0)));
    1936           1 :     CPPUNIT_ASSERT_EQUAL(29.0, m_pDoc->GetValue(ScAddress(0,11,0)));
    1937             : 
    1938           1 :     m_pDoc->DeleteTab(0);
    1939           1 : }
    1940             : 
    1941           1 : void Test::testFormulaRefUpdateMoveToSheet()
    1942             : {
    1943           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    1944             : 
    1945           1 :     m_pDoc->InsertTab(0, "Sheet1");
    1946           1 :     m_pDoc->InsertTab(1, "Sheet2");
    1947             : 
    1948             :     // Set values to A1:A2 on Sheet1, and B1:B2 to reference them.
    1949           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 11);
    1950           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 12);
    1951           1 :     m_pDoc->SetString(ScAddress(1,0,0), "=A1");
    1952           1 :     m_pDoc->SetString(ScAddress(1,1,0), "=A2");
    1953             : 
    1954           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,0,0), "A1"))
    1955           0 :         CPPUNIT_FAIL("Wrong formula");
    1956             : 
    1957           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "A2"))
    1958           0 :         CPPUNIT_FAIL("Wrong formula");
    1959             : 
    1960           1 :     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
    1961           1 :     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(ScAddress(1,1,0)));
    1962             : 
    1963             :     // Move A1:A2 on Sheet1 to B3:B4 on Sheet2.
    1964           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    1965           1 :     bool bMoved = rFunc.MoveBlock(ScRange(0,0,0,0,1,0), ScAddress(1,2,1), true, true, false, true);
    1966           1 :     CPPUNIT_ASSERT(bMoved);
    1967             : 
    1968           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,0,0), "Sheet2.B3"))
    1969           0 :         CPPUNIT_FAIL("Wrong formula");
    1970             : 
    1971           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "Sheet2.B4"))
    1972           0 :         CPPUNIT_FAIL("Wrong formula");
    1973             : 
    1974             :     // Undo and check again.
    1975           1 :     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
    1976           1 :     pUndoMgr->Undo();
    1977             : 
    1978           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,0,0), "A1"))
    1979           0 :         CPPUNIT_FAIL("Wrong formula");
    1980             : 
    1981           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "A2"))
    1982           0 :         CPPUNIT_FAIL("Wrong formula");
    1983             : 
    1984             :     // Redo and check.
    1985           1 :     pUndoMgr->Redo();
    1986             : 
    1987           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,0,0), "Sheet2.B3"))
    1988           0 :         CPPUNIT_FAIL("Wrong formula");
    1989             : 
    1990           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "Sheet2.B4"))
    1991           0 :         CPPUNIT_FAIL("Wrong formula");
    1992             : 
    1993           1 :     m_pDoc->DeleteTab(1);
    1994           1 :     m_pDoc->DeleteTab(0);
    1995           1 : }
    1996             : 
    1997           1 : void Test::testFormulaRefUpdateDeleteContent()
    1998             : {
    1999           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2000             : 
    2001           1 :     m_pDoc->InsertTab(0, "Test");
    2002             : 
    2003             :     // Set value in B2.
    2004           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
    2005             :     // Set formula in C2 to reference B2.
    2006           1 :     m_pDoc->SetString(ScAddress(2,1,0), "=B2");
    2007             : 
    2008           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    2009             : 
    2010             :     // Delete B2.
    2011           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    2012           2 :     ScMarkData aMark;
    2013           1 :     aMark.SetMarkArea(ScAddress(1,1,0));
    2014           1 :     rFunc.DeleteContents(aMark, IDF_CONTENTS, true, true);
    2015             : 
    2016           1 :     CPPUNIT_ASSERT_MESSAGE("B2 should be empty.", m_pDoc->GetCellType(ScAddress(1,1,0)) == CELLTYPE_NONE);
    2017           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    2018             : 
    2019           1 :     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
    2020           1 :     CPPUNIT_ASSERT(pUndoMgr);
    2021             : 
    2022             :     // Undo and check the result of C2.
    2023           1 :     pUndoMgr->Undo();
    2024           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,1,0))); // B2
    2025           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,1,0))); // C2
    2026             : 
    2027             :     // Redo and check.
    2028           1 :     pUndoMgr->Redo();
    2029           1 :     CPPUNIT_ASSERT_MESSAGE("B2 should be empty.", m_pDoc->GetCellType(ScAddress(1,1,0)) == CELLTYPE_NONE);
    2030           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    2031             : 
    2032           2 :     m_pDoc->DeleteTab(0);
    2033           1 : }
    2034             : 
    2035           1 : void Test::testFormulaRefUpdateDeleteAndShiftLeft()
    2036             : {
    2037           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2038             : 
    2039           1 :     m_pDoc->InsertTab(0, "Test");
    2040             : 
    2041             :     // Insert 1,2,3,4,5 in C1:G1.
    2042           6 :     for (SCCOL i = 0; i <= 4; ++i)
    2043           5 :         m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
    2044             : 
    2045             :     // Insert formula in H1.
    2046           1 :     ScAddress aPos(7,0,0);
    2047           1 :     m_pDoc->SetString(aPos, "=SUM(C1:G1)");
    2048             : 
    2049           1 :     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
    2050             : 
    2051             :     // Delete columns D:E (middle of the reference).
    2052           2 :     ScMarkData aMark;
    2053           1 :     aMark.SelectOneTable(0);
    2054           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    2055           1 :     bool bDeleted = rFunc.DeleteCells(ScRange(3,0,0,4,MAXROW,0), &aMark, DEL_CELLSLEFT, true, true);
    2056           1 :     CPPUNIT_ASSERT(bDeleted);
    2057             : 
    2058           1 :     aPos.IncCol(-2);
    2059           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
    2060           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:E1)"))
    2061           0 :         CPPUNIT_FAIL("Wrong formula!");
    2062             : 
    2063             :     // Undo and check.
    2064           1 :     SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
    2065           1 :     CPPUNIT_ASSERT(pUndo);
    2066             : 
    2067           1 :     pUndo->Undo();
    2068           1 :     aPos.IncCol(2);
    2069           1 :     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
    2070           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:G1)"))
    2071           0 :         CPPUNIT_FAIL("Wrong formula!");
    2072             : 
    2073             :     // Delete columns C:D (left end of the reference).
    2074           1 :     bDeleted = rFunc.DeleteCells(ScRange(2,0,0,3,MAXROW,0), &aMark, DEL_CELLSLEFT, true, true);
    2075           1 :     CPPUNIT_ASSERT(bDeleted);
    2076             : 
    2077           1 :     aPos.IncCol(-2);
    2078           1 :     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
    2079           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:E1)"))
    2080           0 :         CPPUNIT_FAIL("Wrong formula!");
    2081             : 
    2082             :     // Undo and check again.
    2083           1 :     pUndo->Undo();
    2084           1 :     aPos.IncCol(2);
    2085           1 :     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
    2086           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:G1)"))
    2087           0 :         CPPUNIT_FAIL("Wrong formula!");
    2088             : 
    2089             :     // Delete columns B:E (overlaps on the left).
    2090           1 :     bDeleted = rFunc.DeleteCells(ScRange(1,0,0,4,MAXROW,0), &aMark, DEL_CELLSLEFT, true, true);
    2091           1 :     CPPUNIT_ASSERT(bDeleted);
    2092             : 
    2093           1 :     aPos.IncCol(-4);
    2094           1 :     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
    2095           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(B1:C1)"))
    2096           0 :         CPPUNIT_FAIL("Wrong formula!");
    2097             : 
    2098             :     // Undo and check again.
    2099           1 :     pUndo->Undo();
    2100           1 :     aPos.IncCol(4);
    2101           1 :     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
    2102           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:G1)"))
    2103           0 :         CPPUNIT_FAIL("Wrong formula!");
    2104             : 
    2105             :     // Start over with a new scenario.
    2106           1 :     clearSheet(m_pDoc, 0);
    2107             : 
    2108             :     // Insert 1,2,3,4,5,6 into C1:H1.
    2109           7 :     for (SCCOL i = 0; i <= 5; ++i)
    2110           6 :         m_pDoc->SetValue(ScAddress(i+2,0,0), i+1);
    2111             : 
    2112             :     // Set formula in B1.
    2113           1 :     aPos = ScAddress(1,0,0);
    2114           1 :     m_pDoc->SetString(aPos, "=SUM(C1:H1)");
    2115           1 :     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
    2116             : 
    2117             :     // Delete columns F:H (right end of the reference).
    2118           1 :     bDeleted = rFunc.DeleteCells(ScRange(5,0,0,7,MAXROW,0), &aMark, DEL_CELLSLEFT, true, true);
    2119           1 :     CPPUNIT_ASSERT(bDeleted);
    2120             : 
    2121           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
    2122           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:E1)"))
    2123           0 :         CPPUNIT_FAIL("Wrong formula!");
    2124             : 
    2125             :     // Undo and check.
    2126           1 :     pUndo->Undo();
    2127           1 :     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
    2128           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:H1)"))
    2129           0 :         CPPUNIT_FAIL("Wrong formula!");
    2130             : 
    2131             :     // Delete columns G:I (overlaps on the right).
    2132           1 :     bDeleted = rFunc.DeleteCells(ScRange(6,0,0,8,MAXROW,0), &aMark, DEL_CELLSLEFT, true, true);
    2133           1 :     CPPUNIT_ASSERT(bDeleted);
    2134             : 
    2135           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
    2136           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:F1)"))
    2137           0 :         CPPUNIT_FAIL("Wrong formula!");
    2138             : 
    2139             :     // Undo and check again.
    2140           1 :     pUndo->Undo();
    2141           1 :     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
    2142           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(C1:H1)"))
    2143           0 :         CPPUNIT_FAIL("Wrong formula!");
    2144             : 
    2145           2 :     m_pDoc->DeleteTab(0);
    2146           1 : }
    2147             : 
    2148           1 : void Test::testFormulaRefUpdateDeleteAndShiftUp()
    2149             : {
    2150           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2151             : 
    2152           1 :     m_pDoc->InsertTab(0, "Test");
    2153             : 
    2154             :     // Insert 1,2,3,4,5 in A3:A7.
    2155           6 :     for (SCROW i = 0; i <= 4; ++i)
    2156           5 :         m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
    2157             : 
    2158             :     // Insert formula in A8.
    2159           1 :     ScAddress aPos(0,7,0);
    2160           1 :     m_pDoc->SetString(aPos, "=SUM(A3:A7)");
    2161             : 
    2162           1 :     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
    2163             : 
    2164             :     // Delete rows 4:5 (middle of the reference).
    2165           2 :     ScMarkData aMark;
    2166           1 :     aMark.SelectOneTable(0);
    2167           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    2168           1 :     bool bDeleted = rFunc.DeleteCells(ScRange(0,3,0,MAXCOL,4,0), &aMark, DEL_CELLSUP, true, true);
    2169           1 :     CPPUNIT_ASSERT(bDeleted);
    2170             : 
    2171           1 :     aPos.IncRow(-2);
    2172           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
    2173           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A5)"))
    2174           0 :         CPPUNIT_FAIL("Wrong formula!");
    2175             : 
    2176             :     // Undo and check.
    2177           1 :     SfxUndoManager* pUndo = m_pDoc->GetUndoManager();
    2178           1 :     CPPUNIT_ASSERT(pUndo);
    2179             : 
    2180           1 :     pUndo->Undo();
    2181           1 :     aPos.IncRow(2);
    2182           1 :     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
    2183           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A7)"))
    2184           0 :         CPPUNIT_FAIL("Wrong formula!");
    2185             : 
    2186             :     // Delete rows 3:4 (top end of the reference).
    2187           1 :     bDeleted = rFunc.DeleteCells(ScRange(0,2,0,MAXCOL,3,0), &aMark, DEL_CELLSUP, true, true);
    2188           1 :     CPPUNIT_ASSERT(bDeleted);
    2189             : 
    2190           1 :     aPos.IncRow(-2);
    2191           1 :     CPPUNIT_ASSERT_EQUAL(12.0, m_pDoc->GetValue(aPos));
    2192           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A5)"))
    2193           0 :         CPPUNIT_FAIL("Wrong formula!");
    2194             : 
    2195             :     // Undo and check again.
    2196           1 :     pUndo->Undo();
    2197           1 :     aPos.IncRow(2);
    2198           1 :     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
    2199           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A7)"))
    2200           0 :         CPPUNIT_FAIL("Wrong formula!");
    2201             : 
    2202             :     // Delete rows 2:5 (overlaps on the top).
    2203           1 :     bDeleted = rFunc.DeleteCells(ScRange(0,1,0,MAXCOL,4,0), &aMark, DEL_CELLSUP, true, true);
    2204           1 :     CPPUNIT_ASSERT(bDeleted);
    2205             : 
    2206           1 :     aPos.IncRow(-4);
    2207           1 :     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(aPos));
    2208           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A2:A3)"))
    2209           0 :         CPPUNIT_FAIL("Wrong formula!");
    2210             : 
    2211             :     // Undo and check again.
    2212           1 :     pUndo->Undo();
    2213           1 :     aPos.IncRow(4);
    2214           1 :     CPPUNIT_ASSERT_EQUAL(15.0, m_pDoc->GetValue(aPos));
    2215           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A7)"))
    2216           0 :         CPPUNIT_FAIL("Wrong formula!");
    2217             : 
    2218             :     // Start over with a new scenario.
    2219           1 :     clearSheet(m_pDoc, 0);
    2220             : 
    2221             :     // Insert 1,2,3,4,5,6 into A3:A8.
    2222           7 :     for (SCROW i = 0; i <= 5; ++i)
    2223           6 :         m_pDoc->SetValue(ScAddress(0,i+2,0), i+1);
    2224             : 
    2225             :     // Set formula in B1.
    2226           1 :     aPos = ScAddress(0,1,0);
    2227           1 :     m_pDoc->SetString(aPos, "=SUM(A3:A8)");
    2228           1 :     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
    2229             : 
    2230             :     // Delete rows 6:8 (bottom end of the reference).
    2231           1 :     bDeleted = rFunc.DeleteCells(ScRange(0,5,0,MAXCOL,7,0), &aMark, DEL_CELLSUP, true, true);
    2232           1 :     CPPUNIT_ASSERT(bDeleted);
    2233             : 
    2234           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
    2235           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A5)"))
    2236           0 :         CPPUNIT_FAIL("Wrong formula!");
    2237             : 
    2238             :     // Undo and check.
    2239           1 :     pUndo->Undo();
    2240           1 :     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
    2241           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A8)"))
    2242           0 :         CPPUNIT_FAIL("Wrong formula!");
    2243             : 
    2244             :     // Delete rows 7:9 (overlaps on the bottom).
    2245           1 :     bDeleted = rFunc.DeleteCells(ScRange(0,6,0,MAXCOL,8,0), &aMark, DEL_CELLSUP, true, true);
    2246           1 :     CPPUNIT_ASSERT(bDeleted);
    2247             : 
    2248           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(aPos));
    2249           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A6)"))
    2250           0 :         CPPUNIT_FAIL("Wrong formula!");
    2251             : 
    2252             :     // Undo and check again.
    2253           1 :     pUndo->Undo();
    2254           1 :     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(aPos));
    2255           1 :     if (!checkFormula(*m_pDoc, aPos, "SUM(A3:A8)"))
    2256           0 :         CPPUNIT_FAIL("Wrong formula!");
    2257             : 
    2258           2 :     m_pDoc->DeleteTab(0);
    2259           1 : }
    2260             : 
    2261           1 : void Test::testFormulaRefUpdateName()
    2262             : {
    2263           1 :     m_pDoc->InsertTab(0, "Formula");
    2264             : 
    2265           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2266             : 
    2267             :     // Fill C2:C5 with values.
    2268           1 :     m_pDoc->SetValue(ScAddress(2,1,0), 1);
    2269           1 :     m_pDoc->SetValue(ScAddress(2,2,0), 2);
    2270           1 :     m_pDoc->SetValue(ScAddress(2,3,0), 3);
    2271           1 :     m_pDoc->SetValue(ScAddress(2,4,0), 4);
    2272             : 
    2273             :     // Add a named expression that references the immediate left cell.
    2274           1 :     ScRangeName* pGlobalNames = m_pDoc->GetRangeName();
    2275           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to obtain global named expression object.", pGlobalNames);
    2276             :     ScRangeData* pName = new ScRangeData(
    2277           1 :         m_pDoc, "ToLeft", "RC[-1]", ScAddress(2,1,0), RT_NAME, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
    2278             : 
    2279           1 :     bool bInserted = pGlobalNames->insert(pName);
    2280           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
    2281             : 
    2282             :     // Insert formulas in D2:D5 using the named expression.
    2283           1 :     m_pDoc->SetString(ScAddress(3,1,0), "=ToLeft");
    2284           1 :     m_pDoc->SetString(ScAddress(3,2,0), "=ToLeft");
    2285           1 :     m_pDoc->SetString(ScAddress(3,3,0), "=ToLeft");
    2286           1 :     m_pDoc->SetString(ScAddress(3,4,0), "=ToLeft");
    2287             : 
    2288             :     // Make sure the results are correct.
    2289           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
    2290           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
    2291           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
    2292           1 :     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
    2293             : 
    2294             :     // Push cells in column C down by one cell.
    2295           1 :     m_pDoc->InsertRow(ScRange(2,0,0,2,0,0));
    2296             : 
    2297             :     // Make sure the results change accordingly.
    2298           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(3,1,0));
    2299           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,2,0));
    2300           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,3,0));
    2301           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,4,0));
    2302             : 
    2303             :     // Move cells back.
    2304           1 :     m_pDoc->DeleteRow(ScRange(2,0,0,2,0,0));
    2305             : 
    2306             :     // Make sure the results are back as well.
    2307           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(3,1,0));
    2308           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(3,2,0));
    2309           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3,3,0));
    2310           1 :     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(3,4,0));
    2311             : 
    2312             :     // Fill B10:B12 with values.
    2313           1 :     m_pDoc->SetValue(ScAddress(1,9,0), 10);
    2314           1 :     m_pDoc->SetValue(ScAddress(1,10,0), 11);
    2315           1 :     m_pDoc->SetValue(ScAddress(1,11,0), 12);
    2316             : 
    2317             :     // Insert a new named expression that references these values as absolute range.
    2318             :     pName = new ScRangeData(
    2319           1 :         m_pDoc, "MyRange", "$B$10:$B$12", ScAddress(0,0,0), RT_NAME, formula::FormulaGrammar::GRAM_NATIVE);
    2320           1 :     bInserted = pGlobalNames->insert(pName);
    2321           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
    2322             : 
    2323             :     // Set formula at C8 that references this named expression.
    2324           1 :     m_pDoc->SetString(ScAddress(2,7,0), "=SUM(MyRange)");
    2325           1 :     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
    2326             : 
    2327             :     // Shift B10:B12 to right by 2 columns.
    2328           1 :     m_pDoc->InsertCol(ScRange(1,9,0,2,11,0));
    2329             : 
    2330             :     // This should shift the absolute range B10:B12 that MyRange references.
    2331           1 :     pName = pGlobalNames->findByUpperName("MYRANGE");
    2332           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
    2333           2 :     OUString aExpr;
    2334           1 :     pName->GetSymbol(aExpr);
    2335           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$D$10:$D$12"), aExpr);
    2336             : 
    2337             :     // This move shouldn't affect the value of C8.
    2338           1 :     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,7,0));
    2339           1 :     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    2340           1 :     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,0)));
    2341             : 
    2342             :     // Update the value of D10 and make sure C8 gets updated.
    2343           1 :     m_pDoc->SetValue(ScAddress(3,9,0), 20);
    2344           1 :     CPPUNIT_ASSERT_EQUAL(43.0, m_pDoc->GetValue(ScAddress(2,7,0)));
    2345             : 
    2346             :     // Insert a new sheet before the current.
    2347           1 :     m_pDoc->InsertTab(0, "New");
    2348           2 :     OUString aName;
    2349           1 :     m_pDoc->GetName(1, aName);
    2350           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
    2351             : 
    2352           1 :     pName = pGlobalNames->findByUpperName("MYRANGE");
    2353           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
    2354             : 
    2355           1 :     m_pDoc->SetValue(ScAddress(3,9,1), 10);
    2356           1 :     CPPUNIT_ASSERT_EQUAL(33.0, m_pDoc->GetValue(ScAddress(2,7,1)));
    2357             : 
    2358             :     // Delete the inserted sheet, which will shift the 'Formula' sheet to the left.
    2359           1 :     m_pDoc->DeleteTab(0);
    2360             : 
    2361           1 :     aName.clear();
    2362           1 :     m_pDoc->GetName(0, aName);
    2363           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Formula"), aName);
    2364             : 
    2365           1 :     pName = pGlobalNames->findByUpperName("MYRANGE");
    2366           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to find named expression 'MyRange' in the global scope.", pName);
    2367             : 
    2368           1 :     m_pDoc->SetValue(ScAddress(3,9,0), 11);
    2369           1 :     CPPUNIT_ASSERT_EQUAL(34.0, m_pDoc->GetValue(ScAddress(2,7,0)));
    2370             : 
    2371             :     // Clear all and start over.
    2372           1 :     clearRange(m_pDoc, ScRange(0,0,0,100,100,0));
    2373           1 :     pGlobalNames->clear();
    2374             : 
    2375             :     pName = new ScRangeData(
    2376           1 :         m_pDoc, "MyRange", "$B$1:$C$6", ScAddress(0,0,0), RT_NAME, formula::FormulaGrammar::GRAM_NATIVE);
    2377           1 :     bInserted = pGlobalNames->insert(pName);
    2378           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to insert a new name.", bInserted);
    2379           1 :     pName->GetSymbol(aExpr);
    2380           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
    2381             : 
    2382             :     // Insert range of cells to shift right. The range partially overlaps the named range.
    2383           1 :     m_pDoc->InsertCol(ScRange(2,4,0,3,8,0));
    2384             : 
    2385             :     // This should not alter the range.
    2386           1 :     pName->GetSymbol(aExpr);
    2387           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$1:$C$6"), aExpr);
    2388             : 
    2389           2 :     m_pDoc->DeleteTab(0);
    2390           1 : }
    2391             : 
    2392           1 : void Test::testFormulaRefUpdateNameMove()
    2393             : {
    2394           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2395             : 
    2396           1 :     m_pDoc->InsertTab(0, "Test");
    2397             : 
    2398             :     // Set values to B2:B4.
    2399           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 1.0);
    2400           1 :     m_pDoc->SetValue(ScAddress(1,2,0), 2.0);
    2401           1 :     m_pDoc->SetValue(ScAddress(1,3,0), 3.0);
    2402             : 
    2403             :     // Set named range for B2:B4.
    2404           1 :     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$2:$B$4");
    2405           1 :     CPPUNIT_ASSERT(bInserted);
    2406             : 
    2407             :     // Set formula in A10.
    2408           1 :     m_pDoc->SetString(ScAddress(0,9,0), "=SUM(MyRange)");
    2409           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    2410             : 
    2411           1 :     ScRangeData* pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2412           1 :     CPPUNIT_ASSERT(pData);
    2413           2 :     OUString aSymbol;
    2414           1 :     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
    2415           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
    2416             : 
    2417             :     // Move B2:B4 to D3.
    2418           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    2419           1 :     bool bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
    2420           1 :     CPPUNIT_ASSERT(bMoved);
    2421             : 
    2422             :     // The named range should have moved as well.
    2423           1 :     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
    2424           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
    2425             : 
    2426             :     // The value of A10 should remain unchanged.
    2427           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    2428             : 
    2429           1 :     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
    2430           1 :     CPPUNIT_ASSERT(pUndoMgr);
    2431             : 
    2432             :     // Undo and check.
    2433           1 :     pUndoMgr->Undo();
    2434             : 
    2435           1 :     pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2436           1 :     CPPUNIT_ASSERT(pData);
    2437           1 :     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
    2438           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$2:$B$4"), aSymbol);
    2439           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    2440             : 
    2441             :     // Redo and check.
    2442           1 :     pUndoMgr->Redo();
    2443             : 
    2444           1 :     pData = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2445           1 :     CPPUNIT_ASSERT(pData);
    2446           1 :     pData->GetSymbol(aSymbol, m_pDoc->GetGrammar());
    2447           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$D$3:$D$5"), aSymbol);
    2448           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    2449             : 
    2450             :     // Undo again to bring it back to the initial condition, and clear the undo buffer.
    2451           1 :     pUndoMgr->Undo();
    2452           1 :     pUndoMgr->Clear();
    2453             : 
    2454             :     // Add an identical formula to A11 and make a formula group over A10:A11.
    2455           1 :     m_pDoc->SetString(ScAddress(0,10,0), "=SUM(MyRange)");
    2456           1 :     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,9,0));
    2457           1 :     CPPUNIT_ASSERT(pFC);
    2458           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(9), pFC->GetSharedTopRow());
    2459           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
    2460             : 
    2461             :     // Move B2:B4 to D3 again.
    2462           1 :     bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,3,0), ScAddress(3,2,0), true, true, false, true);
    2463           1 :     CPPUNIT_ASSERT(bMoved);
    2464             : 
    2465             :     // Values of A10 and A11 should remain the same.
    2466           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,9,0)));
    2467           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,10,0)));
    2468             : 
    2469             :     // Clear and start over.
    2470           1 :     clearSheet(m_pDoc, 0);
    2471           1 :     m_pDoc->GetRangeName()->clear();
    2472             : 
    2473             :     // Set value to B2.
    2474           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
    2475             : 
    2476             :     // Define B2 as 'MyCell'.
    2477           1 :     bInserted = m_pDoc->InsertNewRangeName("MyCell", ScAddress(0,0,0), "$Test.$B$2");
    2478           1 :     CPPUNIT_ASSERT(bInserted);
    2479             : 
    2480             :     // Set formula to B3 that references B2 via MyCell.
    2481           1 :     m_pDoc->SetString(ScAddress(1,2,0), "=MyCell*2");
    2482           1 :     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
    2483             : 
    2484             :     // Move B2 to D2.
    2485           1 :     bMoved = rFunc.MoveBlock(ScRange(1,1,0,1,1,0), ScAddress(3,1,0), true, true, false, true);
    2486           1 :     CPPUNIT_ASSERT(bMoved);
    2487             : 
    2488             :     // Value in B3 should remain unchanged.
    2489           1 :     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(1,2,0)));
    2490             : 
    2491           2 :     m_pDoc->DeleteTab(0);
    2492           1 : }
    2493             : 
    2494           1 : void Test::testFormulaRefUpdateNameExpandRef()
    2495             : {
    2496           1 :     setExpandRefs(true);
    2497             : 
    2498           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2499             : 
    2500           1 :     m_pDoc->InsertTab(0, "Test");
    2501             : 
    2502           1 :     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$A$1:$A$3");
    2503           1 :     CPPUNIT_ASSERT(bInserted);
    2504             : 
    2505             :     // Set values to A1:A3.
    2506           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
    2507           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
    2508           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
    2509             : 
    2510           1 :     m_pDoc->SetString(ScAddress(0,5,0), "=SUM(MyRange)");
    2511           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    2512             : 
    2513             :     // Insert a new row at row 4, which should expand the named range to A1:A4.
    2514           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    2515           2 :     ScMarkData aMark;
    2516           1 :     aMark.SelectOneTable(0);
    2517           1 :     rFunc.InsertCells(ScRange(0,3,0,MAXCOL,3,0), &aMark, INS_INSROWS_BEFORE, false, true, false);
    2518           1 :     ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2519           1 :     CPPUNIT_ASSERT(pName);
    2520           2 :     OUString aSymbol;
    2521           1 :     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
    2522           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$A$1:$A$4"), aSymbol);
    2523             : 
    2524             :     // Make sure the listening area has been expanded as well.  Note the
    2525             :     // formula cell has been pushed downward by one cell.
    2526           1 :     m_pDoc->SetValue(ScAddress(0,3,0), 4.0);
    2527           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(0,6,0)));
    2528             : 
    2529             :     // Clear the document and start over.
    2530           1 :     m_pDoc->GetRangeName()->clear();
    2531           1 :     clearSheet(m_pDoc, 0);
    2532             : 
    2533             :     // Set values to B4:B6.
    2534           1 :     m_pDoc->SetValue(ScAddress(1,3,0), 1.0);
    2535           1 :     m_pDoc->SetValue(ScAddress(1,4,0), 2.0);
    2536           1 :     m_pDoc->SetValue(ScAddress(1,5,0), 3.0);
    2537             : 
    2538           1 :     bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$4:$B$6");
    2539           1 :     CPPUNIT_ASSERT(bInserted);
    2540             : 
    2541             :     // Set formula to A1.
    2542           1 :     m_pDoc->SetString(ScAddress(0,0,0), "=SUM(MyRange)");
    2543           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(0,0,0));
    2544             : 
    2545             :     // Insert rows over 3:5 which should expand the range by 3 rows.
    2546           1 :     rFunc.InsertCells(ScRange(0,2,0,MAXCOL,4,0), &aMark, INS_INSROWS_BEFORE, false, true, false);
    2547             : 
    2548           1 :     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2549           1 :     CPPUNIT_ASSERT(pName);
    2550             : 
    2551           1 :     pName->GetSymbol(aSymbol, m_pDoc->GetGrammar());
    2552           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$4:$B$9"), aSymbol);
    2553             : 
    2554             :     // Clear the document and start over.
    2555           1 :     m_pDoc->GetRangeName()->clear();
    2556           1 :     clearSheet(m_pDoc, 0);
    2557             : 
    2558             :     // Set values to A1:A3.
    2559           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
    2560           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 2.0);
    2561           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 3.0);
    2562             : 
    2563             :     // Name A1:A3 'MyData'.
    2564           1 :     bInserted = m_pDoc->InsertNewRangeName("MyData", ScAddress(0,0,0), "$A$1:$A$3");
    2565           1 :     CPPUNIT_ASSERT(bInserted);
    2566             : 
    2567             :     // Set formulas to C1:C2 and E1.
    2568           1 :     m_pDoc->SetString(ScAddress(2,0,0), "=SUM(MyData)");
    2569           1 :     m_pDoc->SetString(ScAddress(2,1,0), "=SUM(MyData)");
    2570           1 :     m_pDoc->SetString(ScAddress(4,0,0), "=SUM(MyData)");
    2571             : 
    2572             :     // C1:C2 should be shared.
    2573           1 :     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
    2574           1 :     CPPUNIT_ASSERT(pFC);
    2575           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(0), pFC->GetSharedTopRow());
    2576           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
    2577             : 
    2578             :     // E1 should not be shared.
    2579           1 :     pFC = m_pDoc->GetFormulaCell(ScAddress(4,0,0));
    2580           1 :     CPPUNIT_ASSERT(pFC);
    2581           1 :     CPPUNIT_ASSERT(!pFC->IsShared());
    2582             : 
    2583             :     // Check the results.
    2584           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,0,0)));
    2585           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    2586           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(4,0,0)));
    2587             : 
    2588             :     // Insert a new row at row 3.  This should expand MyData to A1:A4.
    2589           1 :     rFunc.InsertCells(ScRange(0,2,0,MAXCOL,2,0), &aMark, INS_INSROWS_BEFORE, false, true, false);
    2590             : 
    2591             :     // Set new value to A3.
    2592           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 4.0);
    2593             : 
    2594             :     // Check the results again.
    2595           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
    2596           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    2597           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(4,0,0)));
    2598             : 
    2599           2 :     m_pDoc->DeleteTab(0);
    2600           1 : }
    2601             : 
    2602           1 : void Test::testFormulaRefUpdateNameDeleteRow()
    2603             : {
    2604           1 :     m_pDoc->InsertTab(0, "Test");
    2605             : 
    2606             :     // Insert a new name 'MyRange' to reference B2:B4.
    2607           1 :     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$B$2:$B$4");
    2608           1 :     CPPUNIT_ASSERT(bInserted);
    2609             : 
    2610           1 :     const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2611           1 :     CPPUNIT_ASSERT(pName);
    2612             : 
    2613           1 :     sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
    2614           1 :     const ScTokenArray* pCode = pName->GetCode();
    2615           2 :     OUString aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2616           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
    2617             : 
    2618           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    2619             : 
    2620             :     // Delete row 3.
    2621           2 :     ScMarkData aMark;
    2622           1 :     aMark.SelectOneTable(0);
    2623           1 :     rFunc.DeleteCells(ScRange(0,2,0,MAXCOL,2,0), &aMark, DEL_CELLSUP, true, true);
    2624             : 
    2625             :     // The reference in the name should get updated to B2:B3.
    2626           1 :     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2627           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
    2628             : 
    2629             :     // Delete row 3 again.
    2630           1 :     rFunc.DeleteCells(ScRange(0,2,0,MAXCOL,2,0), &aMark, DEL_CELLSUP, true, true);
    2631           1 :     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2632           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
    2633             : 
    2634             :     // Undo and check.
    2635           1 :     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
    2636           1 :     CPPUNIT_ASSERT(pUndoMgr);
    2637             : 
    2638           1 :     pUndoMgr->Undo();
    2639             : 
    2640           1 :     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2641           1 :     CPPUNIT_ASSERT(pName);
    2642           1 :     pCode = pName->GetCode();
    2643             : 
    2644           1 :     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2645           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$3"), aExpr);
    2646             : 
    2647             :     // Undo again and check.
    2648           1 :     pUndoMgr->Undo();
    2649             : 
    2650           1 :     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2651           1 :     CPPUNIT_ASSERT(pName);
    2652           1 :     pCode = pName->GetCode();
    2653             : 
    2654           1 :     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2655           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
    2656             : 
    2657             :     // Delete row 2-3.
    2658           1 :     rFunc.DeleteCells(ScRange(0,1,0,MAXCOL,2,0), &aMark, DEL_CELLSUP, true, true);
    2659             : 
    2660           1 :     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2661           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$2"), aExpr);
    2662             : 
    2663             :     // Undo and check.
    2664           1 :     pUndoMgr->Undo();
    2665             : 
    2666           1 :     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2667           1 :     CPPUNIT_ASSERT(pName);
    2668           1 :     pCode = pName->GetCode();
    2669             : 
    2670           1 :     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2671           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
    2672             : 
    2673           1 :     m_pDoc->InsertTab(1, "test2");
    2674             : 
    2675           2 :     ScMarkData aMark2;
    2676           1 :     aMark2.SelectOneTable(1);
    2677           1 :     rFunc.DeleteCells(ScRange(0,2,1,MAXCOL,2,1), &aMark2, DEL_CELLSUP, true, true);
    2678             : 
    2679           1 :     pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2680           1 :     CPPUNIT_ASSERT(pName);
    2681           1 :     pCode = pName->GetCode();
    2682             : 
    2683           1 :     aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2684           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$B$2:$B$4"), aExpr);
    2685             : 
    2686           1 :     m_pDoc->DeleteTab(1);
    2687           2 :     m_pDoc->DeleteTab(0);
    2688           1 : }
    2689             : 
    2690           1 : void Test::testFormulaRefUpdateNameCopySheet()
    2691             : {
    2692           1 :     m_pDoc->InsertTab(0, "Test");
    2693           1 :     m_pDoc->InsertTab(1, "Test2");
    2694             : 
    2695           1 :     bool bInserted = m_pDoc->InsertNewRangeName("RED", ScAddress(0,0,0), "$Test.$B$2");
    2696           1 :     CPPUNIT_ASSERT(bInserted);
    2697           1 :     bInserted = m_pDoc->InsertNewRangeName("BLUE", ScAddress(0,0,0), "$Test.$B$3");
    2698           1 :     CPPUNIT_ASSERT(bInserted);
    2699           1 :     m_pDoc->SetValue(1, 1, 0, 1);
    2700           1 :     m_pDoc->SetValue(1, 2, 0, 2);
    2701             : 
    2702             :     // insert formula into Test2 that is =RED+BLUE
    2703           1 :     m_pDoc->SetString(ScAddress(2,2,1), "=RED+BLUE");
    2704             : 
    2705           1 :     double nVal = m_pDoc->GetValue(2, 2, 1);
    2706           1 :     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
    2707           1 :     m_pDoc->CopyTab(1, 0);
    2708             : 
    2709           1 :     nVal = m_pDoc->GetValue(2, 2, 2);
    2710           1 :     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
    2711             : 
    2712           1 :     nVal = m_pDoc->GetValue(2, 2, 0);
    2713           1 :     CPPUNIT_ASSERT_EQUAL(3.0, nVal);
    2714             : 
    2715           1 :     m_pDoc->SetValue(1, 1, 1, 3);
    2716             : 
    2717           1 :     nVal = m_pDoc->GetValue(2, 2, 2);
    2718           1 :     CPPUNIT_ASSERT_EQUAL(5.0, nVal);
    2719             : 
    2720           1 :     nVal = m_pDoc->GetValue(2, 2, 0);
    2721           1 :     CPPUNIT_ASSERT_EQUAL(5.0, nVal);
    2722             : 
    2723           1 :     m_pDoc->DeleteTab(1);
    2724           1 :     m_pDoc->DeleteTab(0);
    2725           1 : }
    2726             : 
    2727           1 : void Test::testFormulaRefUpdateNameDelete()
    2728             : {
    2729           1 :     m_pDoc->InsertTab(0, "Test");
    2730             : 
    2731             :     // Insert a new name 'MyRange' to reference B1
    2732           1 :     bool bInserted = m_pDoc->InsertNewRangeName("MyRange", ScAddress(0,0,0), "$Test.$B$1");
    2733           1 :     CPPUNIT_ASSERT(bInserted);
    2734             : 
    2735           1 :     const ScRangeData* pName = m_pDoc->GetRangeName()->findByUpperName("MYRANGE");
    2736           1 :     CPPUNIT_ASSERT(pName);
    2737             : 
    2738           1 :     m_pDoc->DeleteCol(1, 0, 3, 0, 0, 1);
    2739           1 :     const ScTokenArray* pCode = pName->GetCode();
    2740           1 :     sc::TokenStringContext aCxt(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH);
    2741           2 :     OUString aExpr = pCode->CreateString(aCxt, ScAddress(0,0,0));
    2742           1 :     CPPUNIT_ASSERT_EQUAL(OUString("$Test.$B$1"), aExpr);
    2743             : 
    2744           2 :     m_pDoc->DeleteTab(0);
    2745           1 : }
    2746             : 
    2747           1 : void Test::testFormulaRefUpdateValidity()
    2748             : {
    2749             :     struct {
    2750             : 
    2751           5 :         bool checkList( std::vector<ScTypedStrData>& rList )
    2752             :         {
    2753           5 :             double aExpected[] = { 1.0, 2.0, 3.0 }; // must be sorted.
    2754           5 :             size_t nCheckSize = SAL_N_ELEMENTS(aExpected);
    2755             : 
    2756           5 :             if (rList.size() != nCheckSize)
    2757             :             {
    2758           0 :                 cerr << "List size is not what is expected." << endl;
    2759           0 :                 return false;
    2760             :             }
    2761             : 
    2762           5 :             std::sort(rList.begin(), rList.end(), ScTypedStrData::LessCaseSensitive());
    2763             : 
    2764          20 :             for (size_t i = 0; i < nCheckSize; ++i)
    2765             :             {
    2766          15 :                 if (aExpected[i] != rList[i].GetValue())
    2767             :                 {
    2768           0 :                     cerr << "Incorrect value at position " << i
    2769           0 :                         << ": expected=" << aExpected[i] << ", actual=" << rList[i].GetValue() << endl;
    2770           0 :                     return false;
    2771             :                 }
    2772             :             }
    2773             : 
    2774           5 :             return true;
    2775             :         }
    2776             : 
    2777             :     } aCheck;
    2778             : 
    2779           1 :     setExpandRefs(false);
    2780           1 :     setCalcAsShown(m_pDoc, true);
    2781             : 
    2782           1 :     m_pDoc->InsertTab(0, "Formula");
    2783             : 
    2784             :     // Set values in C2:C4.
    2785           1 :     m_pDoc->SetValue(ScAddress(2,1,0), 1.0);
    2786           1 :     m_pDoc->SetValue(ScAddress(2,2,0), 2.0);
    2787           1 :     m_pDoc->SetValue(ScAddress(2,3,0), 3.0);
    2788             : 
    2789             :     // Set validity in A2.
    2790             :     ScValidationData aData(
    2791             :         SC_VALID_LIST, SC_COND_EQUAL, "C2:C4", "", m_pDoc, ScAddress(0,1,0), "", "",
    2792           1 :         m_pDoc->GetGrammar(), m_pDoc->GetGrammar());
    2793             : 
    2794           1 :     sal_uLong nIndex = m_pDoc->AddValidationEntry(aData);
    2795           2 :     SfxUInt32Item aItem(ATTR_VALIDDATA, nIndex);
    2796             : 
    2797             :     ScPatternAttr aNewAttrs(
    2798           2 :         new SfxItemSet(*m_pDoc->GetPool(), ATTR_PATTERN_START, ATTR_PATTERN_END));
    2799           1 :     aNewAttrs.GetItemSet().Put(aItem);
    2800             : 
    2801           1 :     m_pDoc->ApplyPattern(0, 1, 0, aNewAttrs);
    2802             : 
    2803           1 :     const ScValidationData* pData = m_pDoc->GetValidationEntry(nIndex);
    2804           1 :     CPPUNIT_ASSERT(pData);
    2805             : 
    2806             :     // Make sure the list is correct.
    2807           2 :     std::vector<ScTypedStrData> aList;
    2808           1 :     pData->FillSelectionList(aList, ScAddress(0,1,0));
    2809           1 :     bool bGood = aCheck.checkList(aList);
    2810           1 :     CPPUNIT_ASSERT_MESSAGE("Initial list is incorrect.", bGood);
    2811             : 
    2812           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    2813           2 :     ScMarkData aMark;
    2814           1 :     aMark.SelectOneTable(0);
    2815             : 
    2816             :     // Insert a new column at Column B, to move the list from C2:C4 to D2:D4.
    2817           1 :     bool bInserted = rFunc.InsertCells(ScRange(1,0,0,1,MAXROW,0), &aMark, INS_INSCOLS_BEFORE, true, true, false);
    2818           1 :     CPPUNIT_ASSERT_MESSAGE("Column insertion failed.", bInserted);
    2819           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(3,1,0)));
    2820           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(3,2,0)));
    2821           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(3,3,0)));
    2822             : 
    2823             :     // Check the list values again.
    2824           1 :     aList.clear();
    2825           1 :     pData->FillSelectionList(aList, ScAddress(0,1,0));
    2826           1 :     bGood = aCheck.checkList(aList);
    2827           1 :     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after column insertion.", bGood);
    2828             : 
    2829           1 :     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
    2830           1 :     CPPUNIT_ASSERT(pUndoMgr);
    2831             : 
    2832             :     // Undo and check the list content again.  The list moves back to C2:C4 after the undo.
    2833           1 :     pUndoMgr->Undo();
    2834           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    2835           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
    2836           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
    2837             : 
    2838           1 :     aList.clear();
    2839           1 :     pData->FillSelectionList(aList, ScAddress(0,1,0));
    2840           1 :     bGood = aCheck.checkList(aList);
    2841           1 :     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of column insertion.", bGood);
    2842             : 
    2843             :     // Move C2:C4 to E5:E7.
    2844           1 :     bool bMoved = rFunc.MoveBlock(ScRange(2,1,0,2,3,0), ScAddress(4,4,0), false, true, false, true);
    2845           1 :     CPPUNIT_ASSERT(bMoved);
    2846           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(4,4,0)));
    2847           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(4,5,0)));
    2848           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(4,6,0)));
    2849             : 
    2850             :     // Check the list again after the move.
    2851           1 :     aList.clear();
    2852           1 :     pData->FillSelectionList(aList, ScAddress(0,1,0));
    2853           1 :     bGood = aCheck.checkList(aList);
    2854           1 :     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after moving C2:C4 to E5:E7.", bGood);
    2855             : 
    2856             :     // Undo the move and check.  The list should be back to C2:C4.
    2857           1 :     pUndoMgr->Undo();
    2858           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    2859           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(2,2,0)));
    2860           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(2,3,0)));
    2861             : 
    2862           1 :     aList.clear();
    2863           1 :     pData->FillSelectionList(aList, ScAddress(0,1,0));
    2864           1 :     bGood = aCheck.checkList(aList);
    2865           1 :     CPPUNIT_ASSERT_MESSAGE("List content is incorrect after undo of the move.", bGood);
    2866             : 
    2867           2 :     m_pDoc->DeleteTab(0);
    2868           1 : }
    2869             : 
    2870           1 : void Test::testMultipleOperations()
    2871             : {
    2872           1 :     m_pDoc->InsertTab(0, "MultiOp");
    2873             : 
    2874           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2875             : 
    2876             :     // Insert the reference formula at top row.
    2877           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1);
    2878           1 :     m_pDoc->SetString(ScAddress(1,0,0), "=A1*10");
    2879           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(1,0,0)));
    2880             : 
    2881             :     // Insert variable inputs in A3:A5.
    2882           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 2);
    2883           1 :     m_pDoc->SetValue(ScAddress(0,3,0), 3);
    2884           1 :     m_pDoc->SetValue(ScAddress(0,4,0), 4);
    2885             : 
    2886             :     // Set multiple operations range.
    2887           1 :     ScTabOpParam aParam;
    2888           1 :     aParam.aRefFormulaCell = ScRefAddress(1,0,0,false,false,false);
    2889           1 :     aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
    2890           1 :     aParam.aRefColCell = ScRefAddress(0,0,0,false,false,false);
    2891           2 :     ScMarkData aMark;
    2892           1 :     aMark.SetMarkArea(ScRange(0,2,0,1,4,0)); // Select A3:B5.
    2893           1 :     m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
    2894           1 :     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(1,2,0));
    2895           1 :     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,3,0));
    2896           1 :     CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,4,0));
    2897             : 
    2898             :     // Clear A3:B5.
    2899           1 :     clearRange(m_pDoc, ScRange(0,2,0,1,4,0));
    2900             : 
    2901             :     // This time, use indirect reference formula cell.
    2902           1 :     m_pDoc->SetString(ScAddress(2,0,0), "=B1"); // C1 simply references B1.
    2903           1 :     CPPUNIT_ASSERT_EQUAL(10.0, m_pDoc->GetValue(ScAddress(2,0,0)));
    2904             : 
    2905             :     // Insert variable inputs in A3:A5.
    2906           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 3);
    2907           1 :     m_pDoc->SetValue(ScAddress(0,3,0), 4);
    2908           1 :     m_pDoc->SetValue(ScAddress(0,4,0), 5);
    2909             : 
    2910             :     // Set multiple operations range again, but this time, we'll use C1 as the reference formula.
    2911           1 :     aParam.aRefFormulaCell.Set(2,0,0,false,false,false);
    2912           1 :     aParam.aRefFormulaEnd = aParam.aRefFormulaCell;
    2913           1 :     m_pDoc->InsertTableOp(aParam, 0, 2, 1, 4, aMark);
    2914           1 :     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(1,2,0));
    2915           1 :     CPPUNIT_ASSERT_EQUAL(40.0, m_pDoc->GetValue(1,3,0));
    2916           1 :     CPPUNIT_ASSERT_EQUAL(50.0, m_pDoc->GetValue(1,4,0));
    2917             : 
    2918           2 :     m_pDoc->DeleteTab(0);
    2919           1 : }
    2920             : 
    2921           1 : void Test::testFuncCOLUMN()
    2922             : {
    2923           1 :     m_pDoc->InsertTab(0, "Formula");
    2924           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2925             : 
    2926           1 :     m_pDoc->SetString(ScAddress(5,10,0), "=COLUMN()");
    2927           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
    2928             : 
    2929           1 :     m_pDoc->SetString(ScAddress(0,1,0), "=F11");
    2930           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    2931             : 
    2932             :     // Move the formula cell with COLUMN() function to change its value.
    2933           1 :     m_pDoc->InsertCol(ScRange(5,0,0,5,MAXROW,0));
    2934           1 :     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(6,10,0)));
    2935             : 
    2936             :     // The cell that references the moved cell should update its value as well.
    2937           1 :     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    2938             : 
    2939             :     // Move the column in the other direction.
    2940           1 :     m_pDoc->DeleteCol(ScRange(5,0,0,5,MAXROW,0));
    2941             : 
    2942           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(5,10,0)));
    2943             : 
    2944             :     // The cell that references the moved cell should update its value as well.
    2945           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    2946             : 
    2947           1 :     m_pDoc->DeleteTab(0);
    2948           1 : }
    2949             : 
    2950           1 : void Test::testFuncCOUNT()
    2951             : {
    2952           1 :     m_pDoc->InsertTab(0, "Formula");
    2953           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2954             : 
    2955           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 2); // A1
    2956           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 4); // A2
    2957           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 6); // A3
    2958             : 
    2959           1 :     ScAddress aPos(1,0,0);
    2960           1 :     m_pDoc->SetString(aPos, "=COUNT(A1:A3)");
    2961           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(aPos));
    2962             : 
    2963           1 :     aPos.IncRow();
    2964           1 :     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2)");
    2965           1 :     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(aPos));
    2966             : 
    2967           1 :     aPos.IncRow();
    2968           1 :     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4)");
    2969           1 :     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(aPos));
    2970             : 
    2971           1 :     aPos.IncRow();
    2972           1 :     m_pDoc->SetString(aPos, "=COUNT(A1:A3;2;4;6)");
    2973           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(aPos));
    2974             : 
    2975           1 :     m_pDoc->DeleteTab(0);
    2976           1 : }
    2977             : 
    2978           1 : void Test::testFuncCOUNTBLANK()
    2979             : {
    2980           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    2981           1 :     m_pDoc->InsertTab(0, "Formula");
    2982             : 
    2983             :     const char* aData[][4] = {
    2984             :         { "1", 0, "=B1", "=\"\"" },
    2985             :         { "2", 0, "=B2", "=\"\"" },
    2986             :         { "A", 0, "=B3", "=\"\"" },
    2987             :         { "B", 0, "=B4", "=D3" },
    2988             :         {   0, 0, "=B5", "=D4" },
    2989             :         { "=COUNTBLANK(A1:A5)", "=COUNTBLANK(B1:B5)", "=COUNTBLANK(C1:C5)", "=COUNTBLANK(D1:D5)" }
    2990           1 :     };
    2991             : 
    2992           1 :     ScAddress aPos(0,0,0);
    2993           1 :     ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    2994           1 :     CPPUNIT_ASSERT(aRange.aStart == aPos);
    2995             : 
    2996           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    2997           1 :     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,5,0)));
    2998           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,5,0)));
    2999           1 :     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(3,5,0)));
    3000             : 
    3001             :     // Test single cell reference cases.
    3002             : 
    3003           1 :     clearSheet(m_pDoc, 0);
    3004             : 
    3005             :     const char* aData2[][2] = {
    3006             :         { "1",     "=COUNTBLANK(A1)" },
    3007             :         { "A",     "=COUNTBLANK(A2)" },
    3008             :         {   0,     "=COUNTBLANK(A3)" },
    3009             :         { "=\"\"", "=COUNTBLANK(A4)" },
    3010             :         { "=A4"  , "=COUNTBLANK(A5)" },
    3011           1 :     };
    3012             : 
    3013           1 :     aRange = insertRangeData(m_pDoc, aPos, aData2, SAL_N_ELEMENTS(aData2));
    3014           1 :     CPPUNIT_ASSERT(aRange.aStart == aPos);
    3015             : 
    3016           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
    3017           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
    3018           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,2,0)));
    3019           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,3,0)));
    3020           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
    3021             : 
    3022           1 :     m_pDoc->DeleteTab(0);
    3023           1 : }
    3024             : 
    3025           1 : void Test::testFuncROW()
    3026             : {
    3027           1 :     m_pDoc->InsertTab(0, "Formula");
    3028           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    3029             : 
    3030           1 :     m_pDoc->SetString(ScAddress(5,10,0), "=ROW()");
    3031           1 :     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
    3032             : 
    3033           1 :     m_pDoc->SetString(ScAddress(0,1,0), "=F11");
    3034           1 :     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    3035             : 
    3036             :     // Insert 2 new rows at row 4.
    3037           1 :     m_pDoc->InsertRow(ScRange(0,3,0,MAXCOL,4,0));
    3038           1 :     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(5,12,0)));
    3039             : 
    3040             :     // The cell that references the moved cell should update its value as well.
    3041           1 :     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    3042             : 
    3043             :     // Delete 2 rows to move it back.
    3044           1 :     m_pDoc->DeleteRow(ScRange(0,3,0,MAXCOL,4,0));
    3045             : 
    3046           1 :     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(5,10,0)));
    3047             : 
    3048             :     // The cell that references the moved cell should update its value as well.
    3049           1 :     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    3050             : 
    3051             :     // Clear sheet and start over.
    3052           1 :     clearSheet(m_pDoc, 0);
    3053             : 
    3054           1 :     m_pDoc->SetString(ScAddress(0,1,0), "=ROW(A5)");
    3055           1 :     m_pDoc->SetString(ScAddress(1,1,0), "=ROW(B5)");
    3056           1 :     m_pDoc->SetString(ScAddress(1,2,0), "=ROW(B6)");
    3057           1 :     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    3058           1 :     CPPUNIT_ASSERT_EQUAL(5.0, m_pDoc->GetValue(ScAddress(1,1,0)));
    3059           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,2,0)));
    3060             : 
    3061             :     // B2:B3 should be shared.
    3062           1 :     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,1,0));
    3063           1 :     CPPUNIT_ASSERT(pFC);
    3064           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(1), pFC->GetSharedTopRow());
    3065           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
    3066             : 
    3067             :     // Insert a new row at row 4.
    3068           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    3069           2 :     ScMarkData aMark;
    3070           1 :     aMark.SelectOneTable(0);
    3071           1 :     rFunc.InsertCells(ScRange(0,3,0,MAXCOL,3,0), &aMark, INS_INSROWS_BEFORE, false, true, false);
    3072           1 :     if (!checkFormula(*m_pDoc, ScAddress(0,1,0), "ROW(A6)"))
    3073           0 :         CPPUNIT_FAIL("Wrong formula!");
    3074           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,1,0), "ROW(B6)"))
    3075           0 :         CPPUNIT_FAIL("Wrong formula!");
    3076           1 :     if (!checkFormula(*m_pDoc, ScAddress(1,2,0), "ROW(B7)"))
    3077           0 :         CPPUNIT_FAIL("Wrong formula!");
    3078             : 
    3079           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,1,0)));
    3080           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(1,1,0)));
    3081           1 :     CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(1,2,0)));
    3082             : 
    3083           2 :     m_pDoc->DeleteTab(0);
    3084           1 : }
    3085             : 
    3086           1 : void Test::testFuncSUM()
    3087             : {
    3088           1 :     OUString aTabName("foo");
    3089           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3090           1 :                             m_pDoc->InsertTab (0, aTabName));
    3091             : 
    3092           2 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
    3093             : 
    3094             :     // Single argument case.
    3095           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1);
    3096           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 1);
    3097           1 :     m_pDoc->SetString(ScAddress(0,2,0), "=SUM(A1:A2)");
    3098           1 :     m_pDoc->CalcAll();
    3099           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(0,2,0)));
    3100             : 
    3101             :     // Multiple argument case.
    3102           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1);
    3103           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 22);
    3104           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 4);
    3105           1 :     m_pDoc->SetValue(ScAddress(0,3,0), 5);
    3106           1 :     m_pDoc->SetValue(ScAddress(0,4,0), 6);
    3107             : 
    3108           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 3);
    3109           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 4);
    3110           1 :     m_pDoc->SetValue(ScAddress(1,2,0), 5);
    3111           1 :     m_pDoc->SetValue(ScAddress(1,3,0), 6);
    3112           1 :     m_pDoc->SetValue(ScAddress(1,4,0), 7);
    3113             : 
    3114           1 :     m_pDoc->SetString(ScAddress(3,0,0), "=SUM(A1:A2;B1:B2)");
    3115           1 :     m_pDoc->SetString(ScAddress(3,1,0), "=SUM(A2:A3;B2:B3)");
    3116           1 :     m_pDoc->SetString(ScAddress(3,2,0), "=SUM(A3:A4;B3:B4)");
    3117           1 :     CPPUNIT_ASSERT_EQUAL(30.0, m_pDoc->GetValue(ScAddress(3,0,0)));
    3118           1 :     CPPUNIT_ASSERT_EQUAL(35.0, m_pDoc->GetValue(ScAddress(3,1,0)));
    3119           1 :     CPPUNIT_ASSERT_EQUAL(20.0, m_pDoc->GetValue(ScAddress(3,2,0)));
    3120             : 
    3121             :     // Clear and start over.
    3122           1 :     clearRange(m_pDoc, ScRange(0,0,0,3,MAXROW,0));
    3123             : 
    3124             :     // SUM needs to take the first error in case the range contains an error.
    3125           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
    3126           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 10.0);
    3127           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 100.0);
    3128           1 :     m_pDoc->SetString(ScAddress(0,3,0), "=SUM(A1:A3)");
    3129           1 :     CPPUNIT_ASSERT_EQUAL(111.0, m_pDoc->GetValue(ScAddress(0,3,0)));
    3130             : 
    3131             :     // Set #DIV/0! error to A3. A4 should also inherit this error.
    3132           1 :     m_pDoc->SetString(ScAddress(0,2,0), "=1/0");
    3133           1 :     sal_uInt16 nErr = m_pDoc->GetErrCode(ScAddress(0,2,0));
    3134           2 :     CPPUNIT_ASSERT_MESSAGE("Cell should have a division by zero error.",
    3135           1 :                            nErr == errDivisionByZero);
    3136           1 :     nErr = m_pDoc->GetErrCode(ScAddress(0,3,0));
    3137           2 :     CPPUNIT_ASSERT_MESSAGE("SUM should have also inherited a div-by-zero error.",
    3138           1 :                            nErr == errDivisionByZero);
    3139             : 
    3140             :     // Set #NA! to A2. A4 should now inherit this error.
    3141           1 :     m_pDoc->SetString(ScAddress(0,1,0), "=NA()");
    3142           1 :     nErr = m_pDoc->GetErrCode(ScAddress(0,1,0));
    3143           1 :     CPPUNIT_ASSERT_MESSAGE("A2 should be an error.", nErr);
    3144           2 :     CPPUNIT_ASSERT_MESSAGE("A4 should have inherited the same error as A2.",
    3145           1 :                            nErr == m_pDoc->GetErrCode(ScAddress(0,3,0)));
    3146             : 
    3147           2 :     m_pDoc->DeleteTab(0);
    3148           1 : }
    3149             : 
    3150           1 : void Test::testFuncPRODUCT()
    3151             : {
    3152           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
    3153             : 
    3154           2 :     OUString aTabName("foo");
    3155           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3156           1 :                             m_pDoc->InsertTab (0, aTabName));
    3157             : 
    3158           1 :     double val = 1;
    3159             :     double result;
    3160           1 :     m_pDoc->SetValue(0, 0, 0, val);
    3161           1 :     val = 2;
    3162           1 :     m_pDoc->SetValue(0, 1, 0, val);
    3163           1 :     val = 3;
    3164           1 :     m_pDoc->SetValue(0, 2, 0, val);
    3165           1 :     m_pDoc->SetString(0, 3, 0, OUString("=PRODUCT(A1:A3)"));
    3166           1 :     m_pDoc->GetValue(0, 3, 0, result);
    3167           1 :     CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT failed", result == 6.0);
    3168             : 
    3169           1 :     m_pDoc->SetString(0, 4, 0, OUString("=PRODUCT({2;3;4})"));
    3170           1 :     m_pDoc->GetValue(0, 4, 0, result);
    3171           1 :     CPPUNIT_ASSERT_MESSAGE("Calculation of PRODUCT with inline array failed", result == 24.0);
    3172             : 
    3173           2 :     m_pDoc->DeleteTab(0);
    3174           1 : }
    3175             : 
    3176           1 : void Test::testFuncSUMPRODUCT()
    3177             : {
    3178           1 :     m_pDoc->InsertTab(0, "Test");
    3179             : 
    3180           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
    3181             : 
    3182           1 :     ScAddress aPos(0,0,0);
    3183           1 :     m_pDoc->SetString(aPos, "=SUMPRODUCT(B1:B3;C1:C3)");
    3184           1 :     CPPUNIT_ASSERT_EQUAL(0.0,  m_pDoc->GetValue(aPos));
    3185           1 :     m_pDoc->SetValue(ScAddress(2,0,0),  1.0); // C1
    3186           1 :     CPPUNIT_ASSERT_EQUAL(0.0,  m_pDoc->GetValue(aPos));
    3187           1 :     m_pDoc->SetValue(ScAddress(1,0,0),  1.0); // B1
    3188           1 :     CPPUNIT_ASSERT_EQUAL(1.0,  m_pDoc->GetValue(aPos));
    3189           1 :     m_pDoc->SetValue(ScAddress(1,1,0),  2.0); // B2
    3190           1 :     CPPUNIT_ASSERT_EQUAL(1.0,  m_pDoc->GetValue(aPos));
    3191           1 :     m_pDoc->SetValue(ScAddress(2,1,0),  3.0); // C2
    3192           1 :     CPPUNIT_ASSERT_EQUAL(7.0,  m_pDoc->GetValue(aPos));
    3193           1 :     m_pDoc->SetValue(ScAddress(2,2,0), -2.0); // C3
    3194           1 :     CPPUNIT_ASSERT_EQUAL(7.0,  m_pDoc->GetValue(aPos));
    3195           1 :     m_pDoc->SetValue(ScAddress(1,2,0),  5.0); // B3
    3196           1 :     CPPUNIT_ASSERT_EQUAL(-3.0, m_pDoc->GetValue(aPos));
    3197             : 
    3198             :     // Force an error in C2 and test ForcedArray matrix error propagation.
    3199           1 :     m_pDoc->SetString( 2, 1, 0, "=1/0");
    3200           1 :     sal_uInt16 nError = m_pDoc->GetErrCode(aPos);
    3201           1 :     CPPUNIT_ASSERT_MESSAGE("Formula result should be a propagated error", nError);
    3202             : 
    3203           1 :     m_pDoc->DeleteTab(0);
    3204           1 : }
    3205             : 
    3206           1 : void Test::testFuncMIN()
    3207             : {
    3208           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto recalc.
    3209           1 :     m_pDoc->InsertTab(0, "Formula");
    3210             : 
    3211             :     // A1:A2
    3212           1 :     m_pDoc->SetString(ScAddress(0,0,0), "a");
    3213           1 :     m_pDoc->SetString(ScAddress(0,1,0), "b");
    3214             : 
    3215             :     // B1:B2
    3216           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
    3217           1 :     m_pDoc->SetValue(ScAddress(1,1,0), 2.0);
    3218             : 
    3219             :     // Matrix in C1:C2.
    3220           2 :     ScMarkData aMark;
    3221           1 :     aMark.SelectOneTable(0);
    3222           1 :     m_pDoc->InsertMatrixFormula(2, 0, 2, 1, aMark, "=MIN(IF(A1:A2=\"c\";B1:B2))");
    3223             : 
    3224             :     // Formula cell in C1:C2 should be a 1x2 matrix array.
    3225           1 :     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(2,0,0));
    3226           1 :     CPPUNIT_ASSERT(pFC);
    3227           1 :     CPPUNIT_ASSERT_MESSAGE("This formula should be an array.", pFC->GetMatrixFlag() == MM_FORMULA);
    3228             : 
    3229             :     SCCOL nCols;
    3230             :     SCROW nRows;
    3231           1 :     pFC->GetMatColsRows(nCols, nRows);
    3232           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
    3233           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), nRows);
    3234             : 
    3235           1 :     CPPUNIT_ASSERT_MESSAGE("Formula in C1 is invalid.", m_pDoc->GetErrCode(ScAddress(2,0,0)) == 0);
    3236           1 :     CPPUNIT_ASSERT_MESSAGE("Formula in C2 is invalid.", m_pDoc->GetErrCode(ScAddress(2,1,0)) == 0);
    3237             : 
    3238           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,0,0)));
    3239           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    3240             : 
    3241             :     // Inline array input (A4).
    3242           1 :     m_pDoc->SetString(ScAddress(0,3,0), "=MIN({-2;4;3})");
    3243           1 :     CPPUNIT_ASSERT_EQUAL(-2.0, m_pDoc->GetValue(ScAddress(0,3,0)));
    3244             : 
    3245             :     // Add more values to B3:B4.
    3246           1 :     m_pDoc->SetValue(ScAddress(1,2,0),  20.0);
    3247           1 :     m_pDoc->SetValue(ScAddress(1,3,0), -20.0);
    3248             : 
    3249             :     // Get the MIN of B1:B4.
    3250           1 :     m_pDoc->SetString(ScAddress(2,4,0), "=MIN(B1:B4)");
    3251           1 :     CPPUNIT_ASSERT_EQUAL(-20.0, m_pDoc->GetValue(ScAddress(2,4,0)));
    3252             : 
    3253           2 :     m_pDoc->DeleteTab(0);
    3254           1 : }
    3255             : 
    3256           1 : void Test::testFuncN()
    3257             : {
    3258           1 :     OUString aTabName("foo");
    3259           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3260           1 :                             m_pDoc->InsertTab (0, aTabName));
    3261             : 
    3262             :     double result;
    3263             : 
    3264             :     // Clear the area first.
    3265           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 1, 20, 0));
    3266             : 
    3267             :     // Put values to reference.
    3268           1 :     double val = 0;
    3269           1 :     m_pDoc->SetValue(0, 0, 0, val);
    3270           1 :     m_pDoc->SetString(0, 2, 0, OUString("Text"));
    3271           1 :     val = 1;
    3272           1 :     m_pDoc->SetValue(0, 3, 0, val);
    3273           1 :     val = -1;
    3274           1 :     m_pDoc->SetValue(0, 4, 0, val);
    3275           1 :     val = 12.3;
    3276           1 :     m_pDoc->SetValue(0, 5, 0, val);
    3277           1 :     m_pDoc->SetString(0, 6, 0, OUString("'12.3"));
    3278             : 
    3279             :     // Cell references
    3280           1 :     m_pDoc->SetString(1, 0, 0, OUString("=N(A1)"));
    3281           1 :     m_pDoc->SetString(1, 1, 0, OUString("=N(A2)"));
    3282           1 :     m_pDoc->SetString(1, 2, 0, OUString("=N(A3)"));
    3283           1 :     m_pDoc->SetString(1, 3, 0, OUString("=N(A4)"));
    3284           1 :     m_pDoc->SetString(1, 4, 0, OUString("=N(A5)"));
    3285           1 :     m_pDoc->SetString(1, 5, 0, OUString("=N(A6)"));
    3286           1 :     m_pDoc->SetString(1, 6, 0, OUString("=N(A9)"));
    3287             : 
    3288             :     // In-line values
    3289           1 :     m_pDoc->SetString(1, 7, 0, OUString("=N(0)"));
    3290           1 :     m_pDoc->SetString(1, 8, 0, OUString("=N(1)"));
    3291           1 :     m_pDoc->SetString(1, 9, 0, OUString("=N(-1)"));
    3292           1 :     m_pDoc->SetString(1, 10, 0, OUString("=N(123)"));
    3293           1 :     m_pDoc->SetString(1, 11, 0, OUString("=N(\"\")"));
    3294           1 :     m_pDoc->SetString(1, 12, 0, OUString("=N(\"12\")"));
    3295           1 :     m_pDoc->SetString(1, 13, 0, OUString("=N(\"foo\")"));
    3296             : 
    3297             :     // Range references
    3298           1 :     m_pDoc->SetString(2, 2, 0, OUString("=N(A1:A8)"));
    3299           1 :     m_pDoc->SetString(2, 3, 0, OUString("=N(A1:A8)"));
    3300           1 :     m_pDoc->SetString(2, 4, 0, OUString("=N(A1:A8)"));
    3301           1 :     m_pDoc->SetString(2, 5, 0, OUString("=N(A1:A8)"));
    3302             : 
    3303             :     // Calculate and check the results.
    3304           1 :     m_pDoc->CalcAll();
    3305             :     double checks1[] = {
    3306             :         0, 0,  0,    1, -1, 12.3, 0, // cell reference
    3307             :         0, 1, -1, 123,  0,    0, 0   // in-line values
    3308           1 :     };
    3309          15 :     for (size_t i = 0; i < SAL_N_ELEMENTS(checks1); ++i)
    3310             :     {
    3311          14 :         m_pDoc->GetValue(1, i, 0, result);
    3312          14 :         bool bGood = result == checks1[i];
    3313          14 :         if (!bGood)
    3314             :         {
    3315           0 :             cerr << "row " << (i+1) << ": expected=" << checks1[i] << " actual=" << result << endl;
    3316           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
    3317             :         }
    3318             :     }
    3319             :     double checks2[] = {
    3320             :         0, 1, -1, 12.3               // range references
    3321           1 :     };
    3322           5 :     for (size_t i = 0; i < SAL_N_ELEMENTS(checks2); ++i)
    3323             :     {
    3324           4 :         m_pDoc->GetValue(1, i+2, 0, result);
    3325           4 :         bool bGood = result == checks2[i];
    3326           4 :         if (!bGood)
    3327             :         {
    3328           0 :             cerr << "row " << (i+2+1) << ": expected=" << checks2[i] << " actual=" << result << endl;
    3329           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for N", false);
    3330             :         }
    3331             :     }
    3332             : 
    3333           1 :     m_pDoc->DeleteTab(0);
    3334           1 : }
    3335             : 
    3336           1 : void Test::testFuncCOUNTIF()
    3337             : {
    3338           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    3339             : 
    3340             :     // COUNTIF (test case adopted from OOo i#36381)
    3341             : 
    3342           2 :     OUString aTabName("foo");
    3343           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3344           1 :                             m_pDoc->InsertTab (0, aTabName));
    3345             : 
    3346             :     // Empty A1:A39 first.
    3347           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
    3348             : 
    3349             :     // Raw data (rows 1 through 9)
    3350             :     const char* aData[] = {
    3351             :         "1999",
    3352             :         "2000",
    3353             :         "0",
    3354             :         "0",
    3355             :         "0",
    3356             :         "2002",
    3357             :         "2001",
    3358             :         "X",
    3359             :         "2002"
    3360           1 :     };
    3361             : 
    3362           1 :     SCROW nRows = SAL_N_ELEMENTS(aData);
    3363          10 :     for (SCROW i = 0; i < nRows; ++i)
    3364           9 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
    3365             : 
    3366           1 :     printRange(m_pDoc, ScRange(0, 0, 0, 0, 8, 0), "data range for COUNTIF");
    3367             : 
    3368             :     // formulas and results
    3369             :     struct {
    3370             :         const char* pFormula; double fResult;
    3371             :     } aChecks[] = {
    3372             :         { "=COUNTIF(A1:A12;1999)",       1 },
    3373             :         { "=COUNTIF(A1:A12;2002)",       2 },
    3374             :         { "=COUNTIF(A1:A12;1998)",       0 },
    3375             :         { "=COUNTIF(A1:A12;\">=1999\")", 5 },
    3376             :         { "=COUNTIF(A1:A12;\">1999\")",  4 },
    3377             :         { "=COUNTIF(A1:A12;\"<2001\")",  5 },
    3378             :         { "=COUNTIF(A1:A12;\">0\")",     5 },
    3379             :         { "=COUNTIF(A1:A12;\">=0\")",    8 },
    3380             :         { "=COUNTIF(A1:A12;0)",          3 },
    3381             :         { "=COUNTIF(A1:A12;\"X\")",      1 },
    3382             :         { "=COUNTIF(A1:A12;)",           3 }
    3383           1 :     };
    3384             : 
    3385           1 :     nRows = SAL_N_ELEMENTS(aChecks);
    3386          12 :     for (SCROW i = 0; i < nRows; ++i)
    3387             :     {
    3388          11 :         SCROW nRow = 20 + i;
    3389          11 :         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
    3390             :     }
    3391             : 
    3392          12 :     for (SCROW i = 0; i < nRows; ++i)
    3393             :     {
    3394             :         double result;
    3395          11 :         SCROW nRow = 20 + i;
    3396          11 :         m_pDoc->GetValue(0, nRow, 0, result);
    3397          11 :         bool bGood = result == aChecks[i].fResult;
    3398          11 :         if (!bGood)
    3399             :         {
    3400           0 :             cerr << "row " << (nRow+1) << ": formula" << aChecks[i].pFormula
    3401           0 :                 << "  expected=" << aChecks[i].fResult << "  actual=" << result << endl;
    3402           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for COUNTIF", false);
    3403             :         }
    3404             :     }
    3405             : 
    3406             :     // Don't count empty strings when searching for a number.
    3407             : 
    3408             :     // Clear A1:A2.
    3409           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 1, 0));
    3410             : 
    3411           1 :     m_pDoc->SetString(0, 0, 0, OUString("=\"\""));
    3412           1 :     m_pDoc->SetString(0, 1, 0, OUString("=COUNTIF(A1;1)"));
    3413             : 
    3414           1 :     double result = m_pDoc->GetValue(0, 1, 0);
    3415           1 :     CPPUNIT_ASSERT_MESSAGE("We shouldn't count empty string as valid number.", result == 0.0);
    3416             : 
    3417             :     // Another test case adopted from fdo#77039.
    3418           1 :     clearSheet(m_pDoc, 0);
    3419             : 
    3420             :     // Set formula cells with blank results in A1:A4.
    3421           5 :     for (SCROW i = 0; i <=3; ++i)
    3422           4 :         m_pDoc->SetString(ScAddress(0,i,0), "=\"\"");
    3423             : 
    3424             :     // Insert formula into A5 to count all cells with empty strings.
    3425           1 :     m_pDoc->SetString(ScAddress(0,4,0), "=COUNTIF(A1:A4;\"\"");
    3426             : 
    3427             :     // We should correctly count with empty string key.
    3428           1 :     CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(0,4,0)));
    3429             : 
    3430           2 :     m_pDoc->DeleteTab(0);
    3431           1 : }
    3432             : 
    3433           1 : void Test::testFuncIF()
    3434             : {
    3435           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    3436             : 
    3437           1 :     m_pDoc->InsertTab(0, "Formula");
    3438             : 
    3439           1 :     m_pDoc->SetString(ScAddress(0,0,0), "=IF(B1=2;\"two\";\"not two\")");
    3440           1 :     CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc->GetString(ScAddress(0,0,0)));
    3441           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
    3442           1 :     CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc->GetString(ScAddress(0,0,0)));
    3443           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 3.0);
    3444           1 :     CPPUNIT_ASSERT_EQUAL(OUString("not two"), m_pDoc->GetString(ScAddress(0,0,0)));
    3445             : 
    3446           1 :     m_pDoc->DeleteTab(0);
    3447           1 : }
    3448             : 
    3449           1 : void Test::testFuncCHOOSE()
    3450             : {
    3451           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    3452             : 
    3453           1 :     m_pDoc->InsertTab(0, "Formula");
    3454             : 
    3455           1 :     m_pDoc->SetString(ScAddress(0,0,0), "=CHOOSE(B1;\"one\";\"two\";\"three\")");
    3456           1 :     sal_uInt16 nError = m_pDoc->GetErrCode(ScAddress(0,0,0));
    3457           1 :     CPPUNIT_ASSERT_MESSAGE("Formula result should be an error since B1 is still empty.", nError);
    3458           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 1.0);
    3459           1 :     CPPUNIT_ASSERT_EQUAL(OUString("one"), m_pDoc->GetString(ScAddress(0,0,0)));
    3460           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 2.0);
    3461           1 :     CPPUNIT_ASSERT_EQUAL(OUString("two"), m_pDoc->GetString(ScAddress(0,0,0)));
    3462           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 3.0);
    3463           1 :     CPPUNIT_ASSERT_EQUAL(OUString("three"), m_pDoc->GetString(ScAddress(0,0,0)));
    3464           1 :     m_pDoc->SetValue(ScAddress(1,0,0), 4.0);
    3465           1 :     nError = m_pDoc->GetErrCode(ScAddress(0,0,0));
    3466           1 :     CPPUNIT_ASSERT_MESSAGE("Formula result should be an error due to out-of-bound input..", nError);
    3467             : 
    3468           1 :     m_pDoc->DeleteTab(0);
    3469           1 : }
    3470             : 
    3471           1 : void Test::testFuncIFERROR()
    3472             : {
    3473             :     // IFERROR/IFNA (fdo#56124)
    3474             : 
    3475           1 :     OUString aTabName("foo");
    3476           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3477           1 :                             m_pDoc->InsertTab (0, aTabName));
    3478             : 
    3479             :     // Empty A1:A39 first.
    3480           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
    3481             : 
    3482             :     // Raw data (rows 1 through 12)
    3483             :     const char* aData[] = {
    3484             :         "1",
    3485             :         "e",
    3486             :         "=SQRT(4)",
    3487             :         "=SQRT(-2)",
    3488             :         "=A4",
    3489             :         "=1/0",
    3490             :         "=NA()",
    3491             :         "bar",
    3492             :         "4",
    3493             :         "gee",
    3494             :         "=1/0",
    3495             :         "23"
    3496           1 :     };
    3497             : 
    3498           1 :     SCROW nRows = SAL_N_ELEMENTS(aData);
    3499          13 :     for (SCROW i = 0; i < nRows; ++i)
    3500          12 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
    3501             : 
    3502           1 :     printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows-1, 0), "data range for IFERROR/IFNA");
    3503             : 
    3504             :     // formulas and results
    3505             :     struct {
    3506             :         const char* pFormula; const char* pResult;
    3507             :     } aChecks[] = {
    3508             :         { "=IFERROR(A1;9)",                         "1" },
    3509             :         { "=IFERROR(A2;9)",                         "e" },
    3510             :         { "=IFERROR(A3;9)",                         "2" },
    3511             :         { "=IFERROR(A4;-7)",                       "-7" },
    3512             :         { "=IFERROR(A5;-7)",                       "-7" },
    3513             :         { "=IFERROR(A6;-7)",                       "-7" },
    3514             :         { "=IFERROR(A7;-7)",                       "-7" },
    3515             :         { "=IFNA(A6;9)",                      "#DIV/0!" },
    3516             :         { "=IFNA(A7;-7)",                          "-7" },
    3517             :         { "=IFNA(VLOOKUP(\"4\";A8:A10;1;0);-2)",    "4" },
    3518             :         { "=IFNA(VLOOKUP(\"fop\";A8:A10;1;0);-2)", "-2" },
    3519             :         { "{=IFERROR(3*A11:A12;1998)}[0]",       "1998" },  // um.. this is not the correct way to insert a
    3520             :         { "{=IFERROR(3*A11:A12;1998)}[1]",         "69" }   // matrix formula, just a place holder, see below
    3521           1 :     };
    3522             : 
    3523           1 :     nRows = SAL_N_ELEMENTS(aChecks);
    3524          12 :     for (SCROW i = 0; i < nRows-2; ++i)
    3525             :     {
    3526          11 :         SCROW nRow = 20 + i;
    3527          11 :         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
    3528             :     }
    3529             : 
    3530             :     // Create a matrix range in last two rows of the range above, actual data
    3531             :     // of the placeholders.
    3532           2 :     ScMarkData aMark;
    3533           1 :     aMark.SelectOneTable(0);
    3534           1 :     m_pDoc->InsertMatrixFormula(0, 20 + nRows-2, 0, 20 + nRows-1, aMark, "=IFERROR(3*A11:A12;1998)", NULL);
    3535             : 
    3536           1 :     m_pDoc->CalcAll();
    3537             : 
    3538          14 :     for (SCROW i = 0; i < nRows; ++i)
    3539             :     {
    3540          13 :         SCROW nRow = 20 + i;
    3541          13 :         OUString aResult = m_pDoc->GetString(0, nRow, 0);
    3542          26 :         CPPUNIT_ASSERT_EQUAL_MESSAGE(
    3543          13 :             aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
    3544          13 :     }
    3545             : 
    3546           2 :     m_pDoc->DeleteTab(0);
    3547           1 : }
    3548             : 
    3549           1 : void Test::testFuncSHEET()
    3550             : {
    3551           1 :     OUString aTabName1("test1");
    3552           2 :     OUString aTabName2("test2");
    3553           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3554           1 :                             m_pDoc->InsertTab (SC_TAB_APPEND, aTabName1));
    3555             : 
    3556           1 :     m_pDoc->SetString(0, 0, 0, OUString("=SHEETS()"));
    3557           1 :     m_pDoc->CalcFormulaTree(false, false);
    3558             :     double original;
    3559           1 :     m_pDoc->GetValue(0, 0, 0, original);
    3560             : 
    3561           2 :     CPPUNIT_ASSERT_MESSAGE("result of SHEETS() should equal the number of sheets, but doesn't.",
    3562           1 :                            static_cast<SCTAB>(original) == m_pDoc->GetTableCount());
    3563             : 
    3564           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3565           1 :                             m_pDoc->InsertTab (SC_TAB_APPEND, aTabName2));
    3566             : 
    3567             :     double modified;
    3568           1 :     m_pDoc->GetValue(0, 0, 0, modified);
    3569           2 :     CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet insertion.",
    3570           1 :                            modified - original == 1.0);
    3571             : 
    3572           1 :     SCTAB nTabCount = m_pDoc->GetTableCount();
    3573           1 :     m_pDoc->DeleteTab(--nTabCount);
    3574             : 
    3575           1 :     m_pDoc->GetValue(0, 0, 0, modified);
    3576           2 :     CPPUNIT_ASSERT_MESSAGE("result of SHEETS() did not get updated after sheet removal.",
    3577           1 :                            modified - original == 0.0);
    3578             : 
    3579           2 :     m_pDoc->DeleteTab(--nTabCount);
    3580           1 : }
    3581             : 
    3582           1 : void Test::testFuncNOW()
    3583             : {
    3584           1 :     OUString aTabName("foo");
    3585           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3586           1 :                             m_pDoc->InsertTab (0, aTabName));
    3587             : 
    3588           1 :     double val = 1;
    3589           1 :     m_pDoc->SetValue(0, 0, 0, val);
    3590           1 :     m_pDoc->SetString(0, 1, 0, OUString("=IF(A1>0;NOW();0"));
    3591             :     double now1;
    3592           1 :     m_pDoc->GetValue(0, 1, 0, now1);
    3593           1 :     CPPUNIT_ASSERT_MESSAGE("Value of NOW() should be positive.", now1 > 0.0);
    3594             : 
    3595           1 :     val = 0;
    3596           1 :     m_pDoc->SetValue(0, 0, 0, val);
    3597           1 :     m_pDoc->CalcFormulaTree(false, false);
    3598             :     double zero;
    3599           1 :     m_pDoc->GetValue(0, 1, 0, zero);
    3600           1 :     CPPUNIT_ASSERT_MESSAGE("Result should equal the 3rd parameter of IF, which is zero.", zero == 0.0);
    3601             : 
    3602           1 :     val = 1;
    3603           1 :     m_pDoc->SetValue(0, 0, 0, val);
    3604           1 :     m_pDoc->CalcFormulaTree(false, false);
    3605             :     double now2;
    3606           1 :     m_pDoc->GetValue(0, 1, 0, now2);
    3607           1 :     CPPUNIT_ASSERT_MESSAGE("Result should be the value of NOW() again.", (now2 - now1) >= 0.0);
    3608             : 
    3609           1 :     m_pDoc->DeleteTab(0);
    3610           1 : }
    3611             : 
    3612           1 : void Test::testFuncNUMBERVALUE()
    3613             : {
    3614             :     // NUMBERVALUE fdo#57180
    3615             : 
    3616           1 :     OUString aTabName("foo");
    3617           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3618           1 :                             m_pDoc->InsertTab (0, aTabName));
    3619             : 
    3620             :     // Empty A1:A39 first.
    3621           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 40, 0));
    3622             : 
    3623             :     // Raw data (rows 1 through 6)
    3624             :     const char* aData[] = {
    3625             :         "1ag9a9b9",
    3626             :         "1ag34 5g g6  78b9%%",
    3627             :         "1 234d56E-2",
    3628             :         "d4",
    3629             :         "54.4",
    3630             :         "1a2b3e1%"
    3631           1 :     };
    3632             : 
    3633           1 :     SCROW nRows = SAL_N_ELEMENTS(aData);
    3634           7 :     for (SCROW i = 0; i < nRows; ++i)
    3635           6 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
    3636             : 
    3637           1 :     printRange(m_pDoc, ScRange(0, 0, 0, 0, nRows - 1, 0), "data range for NUMBERVALUE");
    3638             : 
    3639             :     // formulas and results
    3640             :     struct {
    3641             :         const char* pFormula; const char* pResult;
    3642             :     } aChecks[] = {
    3643             :         { "=NUMBERVALUE(A1;\"b\";\"ag\")",  "199.9" },
    3644             :         { "=NUMBERVALUE(A2;\"b\";\"ag\")",  "134.56789" },
    3645             :         { "=NUMBERVALUE(A2;\"b\";\"g\")",   "#VALUE!" },
    3646             :         { "=NUMBERVALUE(A3;\"d\")",         "12.3456" },
    3647             :         { "=NUMBERVALUE(A4;\"d\";\"foo\")", "0.4" },
    3648             :         { "=NUMBERVALUE(A4;)",              "Err:502" },
    3649             :         { "=NUMBERVALUE(A5;)",              "Err:502" },
    3650             :         { "=NUMBERVALUE(A6;\"b\";\"a\")",   "1.23" }
    3651           1 :     };
    3652             : 
    3653           1 :     nRows = SAL_N_ELEMENTS(aChecks);
    3654           9 :     for (SCROW i = 0; i < nRows; ++i)
    3655             :     {
    3656           8 :         SCROW nRow = 20 + i;
    3657           8 :         m_pDoc->SetString(0, nRow, 0, OUString::createFromAscii(aChecks[i].pFormula));
    3658             :     }
    3659           1 :     m_pDoc->CalcAll();
    3660             : 
    3661           9 :     for (SCROW i = 0; i < nRows; ++i)
    3662             :     {
    3663           8 :         SCROW nRow = 20 + i;
    3664           8 :         OUString aResult = m_pDoc->GetString(0, nRow, 0);
    3665          16 :         CPPUNIT_ASSERT_EQUAL_MESSAGE(
    3666           8 :             aChecks[i].pFormula, OUString::createFromAscii( aChecks[i].pResult), aResult);
    3667           8 :     }
    3668             : 
    3669           1 :     m_pDoc->DeleteTab(0);
    3670           1 : }
    3671             : 
    3672           1 : void Test::testFuncLEN()
    3673             : {
    3674           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn auto calc on.
    3675             : 
    3676           1 :     m_pDoc->InsertTab(0, "Formula");
    3677             : 
    3678             :     // Leave A1:A3 empty, and insert an array of LEN in B1:B3 that references
    3679             :     // these empty cells.
    3680             : 
    3681           2 :     ScMarkData aMark;
    3682           1 :     aMark.SelectOneTable(0);
    3683           1 :     m_pDoc->InsertMatrixFormula(1, 0, 1, 2, aMark, "=LEN(A1:A3)", NULL);
    3684             : 
    3685           1 :     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
    3686           1 :     CPPUNIT_ASSERT(pFC);
    3687           2 :     CPPUNIT_ASSERT_MESSAGE("This formulashould be a matrix origin.",
    3688           1 :                            pFC->GetMatrixFlag() == MM_FORMULA);
    3689             : 
    3690             :     // This should be a 1x3 matrix.
    3691           1 :     SCCOL nCols = -1;
    3692           1 :     SCROW nRows = -1;
    3693           1 :     pFC->GetMatColsRows(nCols, nRows);
    3694           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCCOL>(1), nCols);
    3695           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(3), nRows);
    3696             : 
    3697             :     // LEN value should be 0 for an empty cell.
    3698           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,0,0)));
    3699           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,1,0)));
    3700           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(1,2,0)));
    3701             : 
    3702           2 :     m_pDoc->DeleteTab(0);
    3703           1 : }
    3704             : 
    3705           1 : void Test::testFuncLOOKUP()
    3706             : {
    3707           1 :     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
    3708             : 
    3709           1 :     m_pDoc->InsertTab(0, "Test");
    3710             : 
    3711             :     // Raw data
    3712             :     const char* aData[][2] = {
    3713             :         { "=CONCATENATE(\"A\")", "1" },
    3714             :         { "=CONCATENATE(\"B\")", "2" },
    3715             :         { "=CONCATENATE(\"C\")", "3" },
    3716             :         { 0, 0 } // terminator
    3717           1 :     };
    3718             : 
    3719             :     // Insert raw data into A1:B3.
    3720           4 :     for (SCROW i = 0; aData[i][0]; ++i)
    3721             :     {
    3722           3 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
    3723           3 :         m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
    3724             :     }
    3725             : 
    3726             :     const char* aData2[][2] = {
    3727             :         { "A", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
    3728             :         { "B", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
    3729             :         { "C", "=LOOKUP(RC[-1];R1C1:R3C1;R1C2:R3C2)" },
    3730             :         { 0, 0 } // terminator
    3731           1 :     };
    3732             : 
    3733             :     // Insert check formulas into A5:B7.
    3734           4 :     for (SCROW i = 0; aData2[i][0]; ++i)
    3735             :     {
    3736           3 :         m_pDoc->SetString(0, i+4, 0, OUString::createFromAscii(aData2[i][0]));
    3737           3 :         m_pDoc->SetString(1, i+4, 0, OUString::createFromAscii(aData2[i][1]));
    3738             :     }
    3739             : 
    3740           1 :     printRange(m_pDoc, ScRange(0,4,0,1,6,0), "Data range for LOOKUP.");
    3741             : 
    3742             :     // Values for B5:B7 should be 1, 2, and 3.
    3743           1 :     CPPUNIT_ASSERT_MESSAGE("This formula should not have an error code.", m_pDoc->GetErrCode(ScAddress(1,4,0)) == 0);
    3744           1 :     CPPUNIT_ASSERT_MESSAGE("This formula should not have an error code.", m_pDoc->GetErrCode(ScAddress(1,5,0)) == 0);
    3745           1 :     CPPUNIT_ASSERT_MESSAGE("This formula should not have an error code.", m_pDoc->GetErrCode(ScAddress(1,6,0)) == 0);
    3746             : 
    3747           1 :     CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(1,4,0)));
    3748           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(1,5,0)));
    3749           1 :     CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(1,6,0)));
    3750             : 
    3751           1 :     m_pDoc->DeleteTab(0);
    3752           1 : }
    3753             : 
    3754           1 : void Test::testFuncVLOOKUP()
    3755             : {
    3756             :     // VLOOKUP
    3757             : 
    3758           1 :     OUString aTabName("foo");
    3759           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3760           1 :                             m_pDoc->InsertTab (0, aTabName));
    3761             : 
    3762             :     // Clear A1:F40.
    3763           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 5, 39, 0));
    3764             : 
    3765             :     // Raw data
    3766             :     const char* aData[][2] = {
    3767             :         { "Key", "Val" },
    3768             :         {  "10",   "3" },
    3769             :         {  "20",   "4" },
    3770             :         {  "30",   "5" },
    3771             :         {  "40",   "6" },
    3772             :         {  "50",   "7" },
    3773             :         {  "60",   "8" },
    3774             :         {  "70",   "9" },
    3775             :         {   "B",  "10" },
    3776             :         {   "B",  "11" },
    3777             :         {   "C",  "12" },
    3778             :         {   "D",  "13" },
    3779             :         {   "E",  "14" },
    3780             :         {   "F",  "15" },
    3781             :         { 0, 0 } // terminator
    3782           1 :     };
    3783             : 
    3784             :     // Insert raw data into A1:B14.
    3785          15 :     for (SCROW i = 0; aData[i][0]; ++i)
    3786             :     {
    3787          14 :         m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i][0]));
    3788          14 :         m_pDoc->SetString(1, i, 0, OUString::createFromAscii(aData[i][1]));
    3789             :     }
    3790             : 
    3791           1 :     printRange(m_pDoc, ScRange(0, 0, 0, 1, 13, 0), "raw data for VLOOKUP");
    3792             : 
    3793             :     // Formula data
    3794             :     struct {
    3795             :         const char* pLookup; const char* pFormula; const char* pRes;
    3796             :     } aChecks[] = {
    3797             :         { "Lookup",  "Formula", 0 },
    3798             :         { "12",      "=VLOOKUP(D2;A2:B14;2;1)",     "3" },
    3799             :         { "29",      "=VLOOKUP(D3;A2:B14;2;1)",     "4" },
    3800             :         { "31",      "=VLOOKUP(D4;A2:B14;2;1)",     "5" },
    3801             :         { "45",      "=VLOOKUP(D5;A2:B14;2;1)",     "6" },
    3802             :         { "56",      "=VLOOKUP(D6;A2:B14;2;1)",     "7" },
    3803             :         { "65",      "=VLOOKUP(D7;A2:B14;2;1)",     "8" },
    3804             :         { "78",      "=VLOOKUP(D8;A2:B14;2;1)",     "9" },
    3805             :         { "Andy",    "=VLOOKUP(D9;A2:B14;2;1)",  "#N/A" },
    3806             :         { "Bruce",   "=VLOOKUP(D10;A2:B14;2;1)",   "11" },
    3807             :         { "Charlie", "=VLOOKUP(D11;A2:B14;2;1)",   "12" },
    3808             :         { "David",   "=VLOOKUP(D12;A2:B14;2;1)",   "13" },
    3809             :         { "Edward",  "=VLOOKUP(D13;A2:B14;2;1)",   "14" },
    3810             :         { "Frank",   "=VLOOKUP(D14;A2:B14;2;1)",   "15" },
    3811             :         { "Henry",   "=VLOOKUP(D15;A2:B14;2;1)",   "15" },
    3812             :         { "100",     "=VLOOKUP(D16;A2:B14;2;1)",    "9" },
    3813             :         { "1000",    "=VLOOKUP(D17;A2:B14;2;1)",    "9" },
    3814             :         { "Zena",    "=VLOOKUP(D18;A2:B14;2;1)",   "15" }
    3815           1 :     };
    3816             : 
    3817             :     // Insert formula data into D1:E18.
    3818          19 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    3819             :     {
    3820          18 :         m_pDoc->SetString(3, i, 0, OUString::createFromAscii(aChecks[i].pLookup));
    3821          18 :         m_pDoc->SetString(4, i, 0, OUString::createFromAscii(aChecks[i].pFormula));
    3822             :     }
    3823           1 :     m_pDoc->CalcAll();
    3824           1 :     printRange(m_pDoc, ScRange(3, 0, 0, 4, 17, 0), "formula data for VLOOKUP");
    3825             : 
    3826             :     // Verify results.
    3827          19 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    3828             :     {
    3829          18 :         if (i == 0)
    3830             :             // Skip the header row.
    3831           1 :             continue;
    3832             : 
    3833          17 :         OUString aRes = m_pDoc->GetString(4, i, 0);
    3834          17 :         bool bGood = aRes.equalsAscii(aChecks[i].pRes);
    3835          17 :         if (!bGood)
    3836             :         {
    3837           0 :             cerr << "row " << (i+1) << ": lookup value='" << aChecks[i].pLookup
    3838           0 :                 << "'  expected='" << aChecks[i].pRes << "' actual='" << aRes << "'" << endl;
    3839           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for VLOOKUP", false);
    3840             :         }
    3841          17 :     }
    3842             : 
    3843             :     // Clear the sheet and start over.
    3844           1 :     clearSheet(m_pDoc, 0);
    3845             : 
    3846             :     // Lookup on sorted data intersparsed with empty cells.
    3847             : 
    3848             :     // A1:B8 is the search range.
    3849           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 1.0);
    3850           1 :     m_pDoc->SetValue(ScAddress(0,4,0), 2.0);
    3851           1 :     m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
    3852           1 :     m_pDoc->SetString(ScAddress(1,2,0), "One");
    3853           1 :     m_pDoc->SetString(ScAddress(1,4,0), "Two");
    3854           1 :     m_pDoc->SetString(ScAddress(1,7,0), "Four");
    3855             : 
    3856             :     // D1:D5 contain match values.
    3857           1 :     m_pDoc->SetValue(ScAddress(3,0,0), 1.0);
    3858           1 :     m_pDoc->SetValue(ScAddress(3,1,0), 2.0);
    3859           1 :     m_pDoc->SetValue(ScAddress(3,2,0), 3.0);
    3860           1 :     m_pDoc->SetValue(ScAddress(3,3,0), 4.0);
    3861           1 :     m_pDoc->SetValue(ScAddress(3,4,0), 5.0);
    3862             : 
    3863             :     // E1:E5 contain formulas.
    3864           1 :     m_pDoc->SetString(ScAddress(4,0,0), "=VLOOKUP(D1;$A$1:$B$8;2)");
    3865           1 :     m_pDoc->SetString(ScAddress(4,1,0), "=VLOOKUP(D2;$A$1:$B$8;2)");
    3866           1 :     m_pDoc->SetString(ScAddress(4,2,0), "=VLOOKUP(D3;$A$1:$B$8;2)");
    3867           1 :     m_pDoc->SetString(ScAddress(4,3,0), "=VLOOKUP(D4;$A$1:$B$8;2)");
    3868           1 :     m_pDoc->SetString(ScAddress(4,4,0), "=VLOOKUP(D5;$A$1:$B$8;2)");
    3869           1 :     m_pDoc->CalcAll();
    3870             : 
    3871             :     // Check the formula results in E1:E5.
    3872           1 :     CPPUNIT_ASSERT_EQUAL(OUString("One"), m_pDoc->GetString(ScAddress(4,0,0)));
    3873           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,1,0)));
    3874           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Two"), m_pDoc->GetString(ScAddress(4,2,0)));
    3875           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,3,0)));
    3876           1 :     CPPUNIT_ASSERT_EQUAL(OUString("Four"), m_pDoc->GetString(ScAddress(4,4,0)));
    3877             : 
    3878             :     // Start over again.
    3879           1 :     clearSheet(m_pDoc, 0);
    3880             : 
    3881             :     // Set A,B,....,G to A1:A7.
    3882           1 :     m_pDoc->SetString(ScAddress(0,0,0), "A");
    3883           1 :     m_pDoc->SetString(ScAddress(0,1,0), "B");
    3884           1 :     m_pDoc->SetString(ScAddress(0,2,0), "C");
    3885           1 :     m_pDoc->SetString(ScAddress(0,3,0), "D");
    3886           1 :     m_pDoc->SetString(ScAddress(0,4,0), "E");
    3887           1 :     m_pDoc->SetString(ScAddress(0,5,0), "F");
    3888           1 :     m_pDoc->SetString(ScAddress(0,6,0), "G");
    3889             : 
    3890             :     // Set the formula in C1.
    3891           1 :     m_pDoc->SetString(ScAddress(2,0,0), "=VLOOKUP(\"C\";A1:A16;1)");
    3892           1 :     CPPUNIT_ASSERT_EQUAL(OUString("C"), m_pDoc->GetString(ScAddress(2,0,0)));
    3893             : 
    3894           1 :     m_pDoc->DeleteTab(0);
    3895           1 : }
    3896             : 
    3897             : struct NumStrCheck {
    3898             :     double fVal;
    3899             :     const char* pRes;
    3900             : };
    3901             : 
    3902             : struct StrStrCheck {
    3903             :     const char* pVal;
    3904             :     const char* pRes;
    3905             : };
    3906             : 
    3907             : template<size_t _DataSize, size_t _FormulaSize, int _Type>
    3908           2 : void runTestMATCH(ScDocument* pDoc, const char* aData[_DataSize], StrStrCheck aChecks[_FormulaSize])
    3909             : {
    3910           2 :     size_t nDataSize = _DataSize;
    3911          26 :     for (size_t i = 0; i < nDataSize; ++i)
    3912          24 :         pDoc->SetString(0, i, 0, OUString::createFromAscii(aData[i]));
    3913             : 
    3914          33 :     for (size_t i = 0; i < _FormulaSize; ++i)
    3915             :     {
    3916          31 :         pDoc->SetString(1, i, 0, OUString::createFromAscii(aChecks[i].pVal));
    3917             : 
    3918          31 :         OUStringBuffer aBuf;
    3919          31 :         aBuf.appendAscii("=MATCH(B");
    3920          31 :         aBuf.append(static_cast<sal_Int32>(i+1));
    3921          31 :         aBuf.appendAscii(";A1:A");
    3922          31 :         aBuf.append(static_cast<sal_Int32>(nDataSize));
    3923          31 :         aBuf.appendAscii(";");
    3924          31 :         aBuf.append(static_cast<sal_Int32>(_Type));
    3925          31 :         aBuf.appendAscii(")");
    3926          62 :         OUString aFormula = aBuf.makeStringAndClear();
    3927          31 :         pDoc->SetString(2, i, 0, aFormula);
    3928             :     }
    3929             : 
    3930           2 :     pDoc->CalcAll();
    3931           2 :     Test::printRange(pDoc, ScRange(0, 0, 0, 2, _FormulaSize-1, 0), "MATCH");
    3932             : 
    3933             :     // verify the results.
    3934          33 :     for (size_t i = 0; i < _FormulaSize; ++i)
    3935             :     {
    3936          31 :         OUString aStr = pDoc->GetString(2, i, 0);
    3937          31 :         if (!aStr.equalsAscii(aChecks[i].pRes))
    3938             :         {
    3939           0 :             cerr << "row " << (i+1) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
    3940           0 :                 " criterion='" << aChecks[i].pVal << "'" << endl;
    3941           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for MATCH", false);
    3942             :         }
    3943             :     }
    3944           2 : }
    3945             : 
    3946             : template<size_t _DataSize, size_t _FormulaSize, int _Type>
    3947           2 : void runTestHorizontalMATCH(ScDocument* pDoc, const char* aData[_DataSize], StrStrCheck aChecks[_FormulaSize])
    3948             : {
    3949           2 :     size_t nDataSize = _DataSize;
    3950          26 :     for (size_t i = 0; i < nDataSize; ++i)
    3951          24 :         pDoc->SetString(i, 0, 0, OUString::createFromAscii(aData[i]));
    3952             : 
    3953          33 :     for (size_t i = 0; i < _FormulaSize; ++i)
    3954             :     {
    3955          31 :         pDoc->SetString(i, 1, 0, OUString::createFromAscii(aChecks[i].pVal));
    3956             : 
    3957             :         // Assume we don't have more than 26 data columns..
    3958          31 :         OUStringBuffer aBuf;
    3959          31 :         aBuf.appendAscii("=MATCH(");
    3960          31 :         aBuf.append(static_cast<sal_Unicode>('A'+i));
    3961          31 :         aBuf.appendAscii("2;A1:");
    3962          31 :         aBuf.append(static_cast<sal_Unicode>('A'+nDataSize));
    3963          31 :         aBuf.appendAscii("1;");
    3964          31 :         aBuf.append(static_cast<sal_Int32>(_Type));
    3965          31 :         aBuf.appendAscii(")");
    3966          62 :         OUString aFormula = aBuf.makeStringAndClear();
    3967          31 :         pDoc->SetString(i, 2, 0, aFormula);
    3968             :     }
    3969             : 
    3970           2 :     pDoc->CalcAll();
    3971           2 :     Test::printRange(pDoc, ScRange(0, 0, 0, _FormulaSize-1, 2, 0), "MATCH");
    3972             : 
    3973             :     // verify the results.
    3974          33 :     for (size_t i = 0; i < _FormulaSize; ++i)
    3975             :     {
    3976          31 :         OUString aStr = pDoc->GetString(i, 2, 0);
    3977          31 :         if (!aStr.equalsAscii(aChecks[i].pRes))
    3978             :         {
    3979           0 :             cerr << "column " << char('A'+i) << ": expected='" << aChecks[i].pRes << "' actual='" << aStr << "'"
    3980           0 :                 " criterion='" << aChecks[i].pVal << "'" << endl;
    3981           0 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for horizontal MATCH", false);
    3982             :         }
    3983             :     }
    3984           2 : }
    3985             : 
    3986           1 : void Test::testFuncMATCH()
    3987             : {
    3988           1 :     OUString aTabName("foo");
    3989           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    3990           1 :                             m_pDoc->InsertTab (0, aTabName));
    3991             : 
    3992           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 40, 40, 0));
    3993             :     {
    3994             :         // Ascending in-exact match
    3995             : 
    3996             :         // data range (A1:A9)
    3997             :         const char* aData[] = {
    3998             :             "1",
    3999             :             "2",
    4000             :             "3",
    4001             :             "4",
    4002             :             "5",
    4003             :             "6",
    4004             :             "7",
    4005             :             "8",
    4006             :             "9",
    4007             :             "B",
    4008             :             "B",
    4009             :             "C",
    4010           1 :         };
    4011             : 
    4012             :         // formula (B1:C12)
    4013             :         StrStrCheck aChecks[] = {
    4014             :             { "0.8",   "#N/A" },
    4015             :             { "1.2",      "1" },
    4016             :             { "2.3",      "2" },
    4017             :             { "3.9",      "3" },
    4018             :             { "4.1",      "4" },
    4019             :             { "5.99",     "5" },
    4020             :             { "6.1",      "6" },
    4021             :             { "7.2",      "7" },
    4022             :             { "8.569",    "8" },
    4023             :             { "9.59",     "9" },
    4024             :             { "10",       "9" },
    4025             :             { "100",      "9" },
    4026             :             { "Andy",  "#N/A" },
    4027             :             { "Bruce",   "11" },
    4028             :             { "Charlie", "12" }
    4029           1 :         };
    4030             : 
    4031           1 :         runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
    4032           1 :         clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
    4033           1 :         runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),1>(m_pDoc, aData, aChecks);
    4034           1 :         clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
    4035             :     }
    4036             : 
    4037             :     {
    4038             :         // Descending in-exact match
    4039             : 
    4040             :         // data range (A1:A9)
    4041             :         const char* aData[] = {
    4042             :             "D",
    4043             :             "C",
    4044             :             "B",
    4045             :             "9",
    4046             :             "8",
    4047             :             "7",
    4048             :             "6",
    4049             :             "5",
    4050             :             "4",
    4051             :             "3",
    4052             :             "2",
    4053             :             "1"
    4054           1 :         };
    4055             : 
    4056             :         // formula (B1:C12)
    4057             :         StrStrCheck aChecks[] = {
    4058             :             { "10",      "#N/A" },
    4059             :             { "8.9",     "4" },
    4060             :             { "7.8",     "5" },
    4061             :             { "6.7",     "6" },
    4062             :             { "5.5",     "7" },
    4063             :             { "4.6",     "8" },
    4064             :             { "3.3",     "9" },
    4065             :             { "2.2",     "10" },
    4066             :             { "1.1",     "11" },
    4067             :             { "0.8",     "12" },
    4068             :             { "0",       "12" },
    4069             :             { "-2",      "12" },
    4070             :             { "Andy",    "3" },
    4071             :             { "Bruce",   "2" },
    4072             :             { "Charlie", "1" },
    4073             :             { "David", "#N/A" }
    4074           1 :         };
    4075             : 
    4076           1 :         runTestMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
    4077           1 :         clearRange(m_pDoc, ScRange(0, 0, 0, 4, 40, 0));
    4078           1 :         runTestHorizontalMATCH<SAL_N_ELEMENTS(aData),SAL_N_ELEMENTS(aChecks),-1>(m_pDoc, aData, aChecks);
    4079           1 :         clearRange(m_pDoc, ScRange(0, 0, 0, 40, 4, 0));
    4080             :     }
    4081             : 
    4082             :     {
    4083             :         // search range contains leading and trailing empty cell ranges.
    4084             : 
    4085           1 :         clearRange(m_pDoc, ScRange(0,0,0,2,100,0));
    4086             : 
    4087             :         // A5:A8 contains sorted values.
    4088           1 :         m_pDoc->SetValue(ScAddress(0,4,0), 1.0);
    4089           1 :         m_pDoc->SetValue(ScAddress(0,5,0), 2.0);
    4090           1 :         m_pDoc->SetValue(ScAddress(0,6,0), 3.0);
    4091           1 :         m_pDoc->SetValue(ScAddress(0,7,0), 4.0);
    4092             : 
    4093             :         // Find value 2 which is in A6.
    4094           1 :         m_pDoc->SetString(ScAddress(1,0,0), "=MATCH(2;A1:A20)");
    4095           1 :         m_pDoc->CalcAll();
    4096             : 
    4097           1 :         CPPUNIT_ASSERT_EQUAL(OUString("6"), m_pDoc->GetString(ScAddress(1,0,0)));
    4098             :     }
    4099             : 
    4100           1 :     m_pDoc->DeleteTab(0);
    4101           1 : }
    4102             : 
    4103           1 : void Test::testFuncCELL()
    4104             : {
    4105           1 :     OUString aTabName("foo");
    4106           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    4107           1 :                             m_pDoc->InsertTab (0, aTabName));
    4108             : 
    4109           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 2, 20, 0)); // Clear A1:C21.
    4110             : 
    4111             :     {
    4112           1 :         const char* pContent = "Some random text";
    4113           1 :         m_pDoc->SetString(2, 9, 0, OUString::createFromAscii(pContent)); // Set this value to C10.
    4114           1 :         double val = 1.2;
    4115           1 :         m_pDoc->SetValue(2, 0, 0, val); // Set numeric value to C1;
    4116             : 
    4117             :         // We don't test: FILENAME, FORMAT, WIDTH, PROTECT, PREFIX
    4118             :         StrStrCheck aChecks[] = {
    4119             :             { "=CELL(\"COL\";C10)",           "3" },
    4120             :             { "=CELL(\"ROW\";C10)",          "10" },
    4121             :             { "=CELL(\"SHEET\";C10)",         "1" },
    4122             :             { "=CELL(\"ADDRESS\";C10)",   "$C$10" },
    4123             :             { "=CELL(\"CONTENTS\";C10)", pContent },
    4124             :             { "=CELL(\"COLOR\";C10)",         "0" },
    4125             :             { "=CELL(\"TYPE\";C9)",           "b" },
    4126             :             { "=CELL(\"TYPE\";C10)",          "l" },
    4127             :             { "=CELL(\"TYPE\";C1)",           "v" },
    4128             :             { "=CELL(\"PARENTHESES\";C10)",   "0" }
    4129           1 :         };
    4130             : 
    4131          11 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    4132          10 :             m_pDoc->SetString(0, i, 0, OUString::createFromAscii(aChecks[i].pVal));
    4133           1 :         m_pDoc->CalcAll();
    4134             : 
    4135          11 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    4136             :         {
    4137          10 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    4138          10 :             CPPUNIT_ASSERT_MESSAGE("Unexpected result for CELL", aVal.equalsAscii(aChecks[i].pRes));
    4139          10 :         }
    4140             :     }
    4141             : 
    4142           1 :     m_pDoc->DeleteTab(0);
    4143           1 : }
    4144             : 
    4145             : /** See also test case document fdo#44456 sheet cpearson */
    4146           1 : void Test::testFuncDATEDIF()
    4147             : {
    4148           1 :     OUString aTabName("foo");
    4149           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    4150           1 :                             m_pDoc->InsertTab (0, aTabName));
    4151             : 
    4152             :     const char* aData[][5] = {
    4153             :         { "2007-01-01", "2007-01-10",  "d",   "9", "=DATEDIF(A1;B1;C1)" } ,
    4154             :         { "2007-01-01", "2007-01-31",  "m",   "0", "=DATEDIF(A2;B2;C2)" } ,
    4155             :         { "2007-01-01", "2007-02-01",  "m",   "1", "=DATEDIF(A3;B3;C3)" } ,
    4156             :         { "2007-01-01", "2007-02-28",  "m",   "1", "=DATEDIF(A4;B4;C4)" } ,
    4157             :         { "2007-01-01", "2007-12-31",  "d", "364", "=DATEDIF(A5;B5;C5)" } ,
    4158             :         { "2007-01-01", "2007-01-31",  "y",   "0", "=DATEDIF(A6;B6;C6)" } ,
    4159             :         { "2007-01-01", "2008-07-01",  "d", "547", "=DATEDIF(A7;B7;C7)" } ,
    4160             :         { "2007-01-01", "2008-07-01",  "m",  "18", "=DATEDIF(A8;B8;C8)" } ,
    4161             :         { "2007-01-01", "2008-07-01", "ym",   "6", "=DATEDIF(A9;B9;C9)" } ,
    4162             :         { "2007-01-01", "2008-07-01", "yd", "182", "=DATEDIF(A10;B10;C10)" } ,
    4163             :         { "2008-01-01", "2009-07-01", "yd", "181", "=DATEDIF(A11;B11;C11)" } ,
    4164             :         { "2007-01-01", "2007-01-31", "md",  "30", "=DATEDIF(A12;B12;C12)" } ,
    4165             :         { "2007-02-01", "2009-03-01", "md",   "0", "=DATEDIF(A13;B13;C13)" } ,
    4166             :         { "2008-02-01", "2009-03-01", "md",   "0", "=DATEDIF(A14;B14;C14)" } ,
    4167             :         { "2007-01-02", "2007-01-01", "md", "Err:502", "=DATEDIF(A15;B15;C15)" }    // fail date1 > date2
    4168           1 :     };
    4169             : 
    4170           1 :     clearRange( m_pDoc, ScRange(0, 0, 0, 4, SAL_N_ELEMENTS(aData), 0));
    4171           1 :     ScAddress aPos(0,0,0);
    4172           1 :     ScRange aDataRange = insertRangeData( m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    4173           1 :     CPPUNIT_ASSERT_MESSAGE("failed to insert range data at correct position", aDataRange.aStart == aPos);
    4174             : 
    4175           1 :     m_pDoc->CalcAll();
    4176             : 
    4177          16 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aData); ++i)
    4178             :     {
    4179          15 :         OUString aVal = m_pDoc->GetString( 4, i, 0);
    4180             :         //std::cout << "row "<< i << ": " << OUStringToOString( aVal, RTL_TEXTENCODING_UTF8).getStr() << ", expected " << aData[i][3] << std::endl;
    4181          15 :         CPPUNIT_ASSERT_MESSAGE("Unexpected result for DATEDIF", aVal.equalsAscii( aData[i][3]));
    4182          15 :     }
    4183             : 
    4184           1 :     m_pDoc->DeleteTab(0);
    4185           1 : }
    4186             : 
    4187           1 : void Test::testFuncINDIRECT()
    4188             : {
    4189           1 :     OUString aTabName("foo");
    4190           2 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet",
    4191           1 :                             m_pDoc->InsertTab (0, aTabName));
    4192           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 0, 10, 0)); // Clear A1:A11
    4193             : 
    4194           1 :     bool bGood = m_pDoc->GetName(0, aTabName);
    4195           1 :     CPPUNIT_ASSERT_MESSAGE("failed to get sheet name.", bGood);
    4196             : 
    4197           2 :     OUString aTest = "Test", aRefErr = "#REF!";
    4198           1 :     m_pDoc->SetString(0, 10, 0, aTest);
    4199           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", m_pDoc->GetString(0,10,0) == aTest);
    4200             : 
    4201           2 :     OUString aPrefix = "=INDIRECT(\"";
    4202             : 
    4203           2 :     OUString aFormula = aPrefix + aTabName + ".A11\")"; // Calc A1
    4204           1 :     m_pDoc->SetString(0, 0, 0, aFormula);
    4205           1 :     aFormula = aPrefix + aTabName + "!A11\")"; // Excel A1
    4206           1 :     m_pDoc->SetString(0, 1, 0, aFormula);
    4207           1 :     aFormula = aPrefix + aTabName + "!R11C1\")"; // Excel R1C1
    4208           1 :     m_pDoc->SetString(0, 2, 0, aFormula);
    4209           1 :     aFormula = aPrefix + aTabName + "!R11C1\";0)"; // Excel R1C1 (forced)
    4210           1 :     m_pDoc->SetString(0, 3, 0, aFormula);
    4211             : 
    4212           1 :     m_pDoc->CalcAll();
    4213             :     {
    4214             :         // Default is to use the current formula syntax, which is Calc A1.
    4215             :         const OUString* aChecks[] = {
    4216             :             &aTest, &aRefErr, &aRefErr, &aTest
    4217           1 :         };
    4218             : 
    4219           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    4220             :         {
    4221           4 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    4222           4 :             CPPUNIT_ASSERT_EQUAL_MESSAGE("Wrong value!", *aChecks[i], aVal);
    4223           4 :         }
    4224             :     }
    4225             : 
    4226           2 :     ScCalcConfig aConfig;
    4227           1 :     aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_OOO;
    4228           1 :     m_pDoc->SetCalcConfig(aConfig);
    4229           1 :     m_pDoc->CalcAll();
    4230             :     {
    4231             :         // Explicit Calc A1 syntax
    4232             :         const OUString* aChecks[] = {
    4233             :             &aTest, &aRefErr, &aRefErr, &aTest
    4234           1 :         };
    4235             : 
    4236           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    4237             :         {
    4238           4 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    4239           4 :             CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
    4240           4 :         }
    4241             :     }
    4242             : 
    4243           1 :     aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_A1;
    4244           1 :     m_pDoc->SetCalcConfig(aConfig);
    4245           1 :     m_pDoc->CalcAll();
    4246             :     {
    4247             :         // Excel A1 syntax
    4248             :         const OUString* aChecks[] = {
    4249             :             &aRefErr, &aTest, &aRefErr, &aTest
    4250           1 :         };
    4251             : 
    4252           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    4253             :         {
    4254           4 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    4255           4 :             CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
    4256           4 :         }
    4257             :     }
    4258             : 
    4259           1 :     aConfig.meStringRefAddressSyntax = formula::FormulaGrammar::CONV_XL_R1C1;
    4260           1 :     m_pDoc->SetCalcConfig(aConfig);
    4261           1 :     m_pDoc->CalcAll();
    4262             :     {
    4263             :         // Excel R1C1 syntax
    4264             :         const OUString* aChecks[] = {
    4265             :             &aRefErr, &aRefErr, &aTest, &aTest
    4266           1 :         };
    4267             : 
    4268           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    4269             :         {
    4270           4 :             OUString aVal = m_pDoc->GetString(0, i, 0);
    4271           4 :             CPPUNIT_ASSERT_MESSAGE("Wrong value!", aVal == *aChecks[i]);
    4272           4 :         }
    4273             :     }
    4274             : 
    4275           2 :     m_pDoc->DeleteTab(0);
    4276           1 : }
    4277             : 
    4278           1 : void Test::testFormulaDepTracking()
    4279             : {
    4280           1 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
    4281             : 
    4282           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    4283             : 
    4284             :     // B2 listens on D2.
    4285           1 :     m_pDoc->SetString(1, 1, 0, "=D2");
    4286           1 :     double val = -999.0; // dummy initial value
    4287           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4288           1 :     CPPUNIT_ASSERT_MESSAGE("Referencing an empty cell should yield zero.", val == 0.0);
    4289             : 
    4290             :     // Changing the value of D2 should trigger recalculation of B2.
    4291           1 :     m_pDoc->SetValue(3, 1, 0, 1.1);
    4292           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4293           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 1.1);
    4294             : 
    4295             :     // And again.
    4296           1 :     m_pDoc->SetValue(3, 1, 0, 2.2);
    4297           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4298           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on value change.", val == 2.2);
    4299             : 
    4300           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
    4301             : 
    4302             :     // Now, let's test the range dependency tracking.
    4303             : 
    4304             :     // B2 listens on D2:E6.
    4305           1 :     m_pDoc->SetString(1, 1, 0, "=SUM(D2:E6)");
    4306           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4307           1 :     CPPUNIT_ASSERT_MESSAGE("Summing an empty range should yield zero.", val == 0.0);
    4308             : 
    4309             :     // Set value to E3. This should trigger recalc on B2.
    4310           1 :     m_pDoc->SetValue(4, 2, 0, 2.4);
    4311           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4312           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", val == 2.4);
    4313             : 
    4314             :     // Set value to D5 to trigger recalc again.  Note that this causes an
    4315             :     // addition of 1.2 + 2.4 which is subject to binary floating point
    4316             :     // rounding error.  We need to use approxEqual to assess its value.
    4317             : 
    4318           1 :     m_pDoc->SetValue(3, 4, 0, 1.2);
    4319           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4320           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 3.6));
    4321             : 
    4322             :     // Change the value of D2 (boundary case).
    4323           1 :     m_pDoc->SetValue(3, 1, 0, 1.0);
    4324           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4325           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 4.6));
    4326             : 
    4327             :     // Change the value of E6 (another boundary case).
    4328           1 :     m_pDoc->SetValue(4, 5, 0, 2.0);
    4329           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4330           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 6.6));
    4331             : 
    4332             :     // Change the value of D6 (another boundary case).
    4333           1 :     m_pDoc->SetValue(3, 5, 0, 3.0);
    4334           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4335           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 9.6));
    4336             : 
    4337             :     // Change the value of E2 (another boundary case).
    4338           1 :     m_pDoc->SetValue(4, 1, 0, 0.4);
    4339           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4340           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 10.0));
    4341             : 
    4342             :     // Change the existing non-empty value cell (E2).
    4343           1 :     m_pDoc->SetValue(4, 1, 0, 2.4);
    4344           1 :     m_pDoc->GetValue(1, 1, 0, val);
    4345           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to recalculate on single value change.", rtl::math::approxEqual(val, 12.0));
    4346             : 
    4347           1 :     clearRange(m_pDoc, ScRange(0, 0, 0, 10, 10, 0));
    4348             : 
    4349             :     // Now, column-based dependency tracking.  We now switch to the R1C1
    4350             :     // syntax which is easier to use for repeated relative references.
    4351             : 
    4352           2 :     FormulaGrammarSwitch aFGSwitch(m_pDoc, formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
    4353             : 
    4354           1 :     val = 0.0;
    4355          10 :     for (SCROW nRow = 1; nRow <= 9; ++nRow)
    4356             :     {
    4357             :         // Static value in column 1.
    4358           9 :         m_pDoc->SetValue(0, nRow, 0, ++val);
    4359             : 
    4360             :         // Formula in column 2 that references cell to the left.
    4361           9 :         m_pDoc->SetString(1, nRow, 0, "=RC[-1]");
    4362             : 
    4363             :         // Formula in column 3 that references cell to the left.
    4364           9 :         m_pDoc->SetString(2, nRow, 0, "=RC[-1]*2");
    4365             :     }
    4366             : 
    4367             :     // Check formula values.
    4368           1 :     val = 0.0;
    4369          10 :     for (SCROW nRow = 1; nRow <= 9; ++nRow)
    4370             :     {
    4371           9 :         ++val;
    4372           9 :         CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, nRow, 0) == val);
    4373           9 :         CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, nRow, 0) == val*2.0);
    4374             :     }
    4375             : 
    4376             :     // Intentionally insert a formula in column 1. This will break column 1's
    4377             :     // uniformity of consisting only of static value cells.
    4378           1 :     m_pDoc->SetString(0, 4, 0, "=R2C3");
    4379           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(0, 4, 0) == 2.0);
    4380           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(1, 4, 0) == 2.0);
    4381           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected formula value.", m_pDoc->GetValue(2, 4, 0) == 4.0);
    4382             : 
    4383           2 :     m_pDoc->DeleteTab(0);
    4384           1 : }
    4385             : 
    4386           1 : void Test::testFormulaDepTracking2()
    4387             : {
    4388           1 :     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
    4389             : 
    4390           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    4391             : 
    4392           1 :     double val = 2.0;
    4393           1 :     m_pDoc->SetValue(0, 0, 0, val);
    4394           1 :     val = 4.0;
    4395           1 :     m_pDoc->SetValue(1, 0, 0, val);
    4396           1 :     val = 5.0;
    4397           1 :     m_pDoc->SetValue(0, 1, 0, val);
    4398           1 :     m_pDoc->SetString(2, 0, 0, "=A1/B1");
    4399           1 :     m_pDoc->SetString(1, 1, 0, "=B1*C1");
    4400             : 
    4401           1 :     CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(1, 1, 0)); // B2 should equal 2.
    4402             : 
    4403           1 :     clearRange(m_pDoc, ScAddress(2, 0, 0)); // Delete C1.
    4404             : 
    4405           1 :     CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(1, 1, 0)); // B2 should now equal 0.
    4406             : 
    4407           1 :     m_pDoc->DeleteTab(0);
    4408           1 : }
    4409             : 
    4410           1 : void Test::testFormulaDepTracking3()
    4411             : {
    4412           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    4413             : 
    4414           1 :     m_pDoc->InsertTab(0, "Formula");
    4415             : 
    4416             :     const char* pData[][4] = {
    4417             :         { "1", "2", "=SUM(A1:B1)", "=SUM(C1:C3)" },
    4418             :         { "3", "4", "=SUM(A2:B2)", 0 },
    4419             :         { "5", "6", "=SUM(A3:B3)", 0 },
    4420           1 :     };
    4421             : 
    4422           1 :     insertRangeData(m_pDoc, ScAddress(0,0,0), pData, SAL_N_ELEMENTS(pData));
    4423             : 
    4424             :     // Check the initial formula results.
    4425           1 :     CPPUNIT_ASSERT_EQUAL( 3.0, m_pDoc->GetValue(ScAddress(2,0,0)));
    4426           1 :     CPPUNIT_ASSERT_EQUAL( 7.0, m_pDoc->GetValue(ScAddress(2,1,0)));
    4427           1 :     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(2,2,0)));
    4428           1 :     CPPUNIT_ASSERT_EQUAL(21.0, m_pDoc->GetValue(ScAddress(3,0,0)));
    4429             : 
    4430             :     // Change B3 and make sure the change gets propagated to D1.
    4431           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    4432           1 :     rFunc.SetValueCell(ScAddress(1,2,0), 60.0, false);
    4433           1 :     CPPUNIT_ASSERT_EQUAL(65.0, m_pDoc->GetValue(ScAddress(2,2,0)));
    4434           1 :     CPPUNIT_ASSERT_EQUAL(75.0, m_pDoc->GetValue(ScAddress(3,0,0)));
    4435             : 
    4436           1 :     m_pDoc->DeleteTab(0);
    4437           1 : }
    4438             : 
    4439           1 : void Test::testFormulaDepTrackingDeleteRow()
    4440             : {
    4441           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    4442             : 
    4443           1 :     m_pDoc->InsertTab(0, "Test");
    4444             : 
    4445             :     // Values in A1:A3.
    4446           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 1.0);
    4447           1 :     m_pDoc->SetValue(ScAddress(0,1,0), 3.0);
    4448           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 5.0);
    4449             : 
    4450             :     // SUM(A1:A3) in A5.
    4451           1 :     m_pDoc->SetString(ScAddress(0,4,0), "=SUM(A1:A3)");
    4452             : 
    4453             :     // A6 to reference A5.
    4454           1 :     m_pDoc->SetString(ScAddress(0,5,0), "=A5*10");
    4455           1 :     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(0,5,0));
    4456           1 :     CPPUNIT_ASSERT(pFC);
    4457             : 
    4458             :     // A4 should have a broadcaster with A5 listening to it.
    4459           1 :     SvtBroadcaster* pBC = m_pDoc->GetBroadcaster(ScAddress(0,4,0));
    4460           1 :     CPPUNIT_ASSERT(pBC);
    4461           1 :     SvtBroadcaster::ListenersType* pListeners = &pBC->GetAllListeners();
    4462           1 :     CPPUNIT_ASSERT_MESSAGE("A5 should have one listener.", pListeners->size() == 1);
    4463           1 :     SvtListener* pListener = pListeners->at(0);
    4464           1 :     CPPUNIT_ASSERT_MESSAGE("A6 should be listening to A5.", pListener == pFC);
    4465             : 
    4466             :     // Check initial values.
    4467           1 :     CPPUNIT_ASSERT_EQUAL(9.0, m_pDoc->GetValue(ScAddress(0,4,0)));
    4468           1 :     CPPUNIT_ASSERT_EQUAL(90.0, m_pDoc->GetValue(ScAddress(0,5,0)));
    4469             : 
    4470             :     // Delete row 2.
    4471           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    4472           2 :     ScMarkData aMark;
    4473           1 :     aMark.SelectOneTable(0);
    4474           1 :     rFunc.DeleteCells(ScRange(0,1,0,MAXCOL,1,0), &aMark, DEL_CELLSUP, true, true);
    4475             : 
    4476           1 :     pBC = m_pDoc->GetBroadcaster(ScAddress(0,3,0));
    4477           1 :     CPPUNIT_ASSERT_MESSAGE("Broadcaster at A5 should have shifted to A4.", pBC);
    4478           1 :     pListeners = &pBC->GetAllListeners();
    4479           1 :     CPPUNIT_ASSERT_MESSAGE("A3 should have one listener.", pListeners->size() == 1);
    4480           1 :     pFC = m_pDoc->GetFormulaCell(ScAddress(0,4,0));
    4481           1 :     CPPUNIT_ASSERT(pFC);
    4482           1 :     pListener = pListeners->at(0);
    4483           1 :     CPPUNIT_ASSERT_MESSAGE("A5 should be listening to A4.", pFC == pListener);
    4484             : 
    4485             :     // Check values after row deletion.
    4486           1 :     CPPUNIT_ASSERT_EQUAL(6.0, m_pDoc->GetValue(ScAddress(0,3,0)));
    4487           1 :     CPPUNIT_ASSERT_EQUAL(60.0, m_pDoc->GetValue(ScAddress(0,4,0)));
    4488             : 
    4489           2 :     m_pDoc->DeleteTab(0);
    4490           1 : }
    4491             : 
    4492           1 : void Test::testFormulaDepTrackingDeleteCol()
    4493             : {
    4494           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    4495             : 
    4496           1 :     m_pDoc->InsertTab(0, "Formula");
    4497             : 
    4498             :     const char* aData[][3] = {
    4499             :         { "2", "=A1", "=B1" }, // not grouped
    4500             :         { 0, 0, 0 },           // empty row to separate the formula groups.
    4501             :         { "3", "=A3", "=B3" }, // grouped
    4502             :         { "4", "=A4", "=B4" }, // grouped
    4503           1 :     };
    4504             : 
    4505           1 :     ScAddress aPos(0,0,0);
    4506           1 :     ScRange aRange = insertRangeData(m_pDoc, aPos, aData, SAL_N_ELEMENTS(aData));
    4507           1 :     CPPUNIT_ASSERT(aRange.aStart == aPos);
    4508             : 
    4509             :     // Check the initial values.
    4510           4 :     for (SCCOL i = 0; i <= 2; ++i)
    4511             :     {
    4512           3 :         CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(ScAddress(i,0,0)));
    4513           3 :         CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(ScAddress(i,2,0)));
    4514           3 :         CPPUNIT_ASSERT_EQUAL(4.0, m_pDoc->GetValue(ScAddress(i,3,0)));
    4515             :     }
    4516             : 
    4517             :     // Make sure B3:B4 and C3:C4 are grouped.
    4518           1 :     const ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,2,0));
    4519           1 :     CPPUNIT_ASSERT(pFC);
    4520           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
    4521           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
    4522             : 
    4523           1 :     pFC = m_pDoc->GetFormulaCell(ScAddress(2,2,0));
    4524           1 :     CPPUNIT_ASSERT(pFC);
    4525           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedTopRow());
    4526           1 :     CPPUNIT_ASSERT_EQUAL(static_cast<SCROW>(2), pFC->GetSharedLength());
    4527             : 
    4528             :     // Delete column A.  A1, B1, A3:A4 and B3:B4 should all show #REF!.
    4529           1 :     ScDocFunc& rFunc = getDocShell().GetDocFunc();
    4530           2 :     ScMarkData aMark;
    4531           1 :     aMark.SelectOneTable(0);
    4532           1 :     rFunc.DeleteCells(ScRange(0,0,0,0,MAXROW,0), &aMark, DEL_CELLSLEFT, true, true);
    4533             : 
    4534             :     {
    4535             :         // Expected output table content.  0 = empty cell
    4536             :         const char* aOutputCheck[][2] = {
    4537             :             { "#REF!", "#REF!" },
    4538             :             { 0,  0 },
    4539             :             { "#REF!", "#REF!" },
    4540             :             { "#REF!", "#REF!" },
    4541           1 :         };
    4542             : 
    4543           1 :         ScRange aCheckRange(0,0,0,1,3,0);
    4544           1 :         bool bSuccess = checkOutput<2>(m_pDoc, aCheckRange, aOutputCheck, "Check after deleting column A");
    4545           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4546             :     }
    4547             : 
    4548             :     // Undo and check the result.
    4549           1 :     SfxUndoManager* pUndoMgr = m_pDoc->GetUndoManager();
    4550           1 :     CPPUNIT_ASSERT(pUndoMgr);
    4551           1 :     pUndoMgr->Undo();
    4552             : 
    4553             :     {
    4554             :         // Expected output table content.  0 = empty cell
    4555             :         const char* aOutputCheck[][3] = {
    4556             :             { "2", "2", "2" },
    4557             :             { 0,  0, 0 },
    4558             :             { "3", "3", "3" },
    4559             :             { "4", "4", "4" },
    4560           1 :         };
    4561             : 
    4562           1 :         ScRange aCheckRange(0,0,0,2,3,0);
    4563           1 :         bool bSuccess = checkOutput<3>(m_pDoc, aCheckRange, aOutputCheck, "Check after undo");
    4564           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4565             :     }
    4566             : 
    4567             :     // Redo and check.
    4568           1 :     pUndoMgr->Redo();
    4569             :     {
    4570             :         // Expected output table content.  0 = empty cell
    4571             :         const char* aOutputCheck[][2] = {
    4572             :             { "#REF!", "#REF!" },
    4573             :             { 0, 0 },
    4574             :             { "#REF!", "#REF!" },
    4575             :             { "#REF!", "#REF!" },
    4576           1 :         };
    4577             : 
    4578           1 :         ScRange aCheckRange(0,0,0,1,3,0);
    4579           1 :         bool bSuccess = checkOutput<2>(m_pDoc, aCheckRange, aOutputCheck, "Check after redo");
    4580           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4581             :     }
    4582             : 
    4583             :     // Undo and change the values in column A.
    4584           1 :     pUndoMgr->Undo();
    4585           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 22.0);
    4586           1 :     m_pDoc->SetValue(ScAddress(0,2,0), 23.0);
    4587           1 :     m_pDoc->SetValue(ScAddress(0,3,0), 24.0);
    4588             : 
    4589             :     {
    4590             :         // Expected output table content.  0 = empty cell
    4591             :         const char* aOutputCheck[][3] = {
    4592             :             { "22", "22", "22" },
    4593             :             { 0, 0, 0 },
    4594             :             { "23", "23", "23" },
    4595             :             { "24", "24", "24" },
    4596           1 :         };
    4597             : 
    4598           1 :         ScRange aCheckRange(0,0,0,2,3,0);
    4599           1 :         bool bSuccess = checkOutput<3>(m_pDoc, aCheckRange, aOutputCheck, "Check after undo & value change in column A");
    4600           1 :         CPPUNIT_ASSERT_MESSAGE("Table output check failed", bSuccess);
    4601             :     }
    4602             : 
    4603           2 :     m_pDoc->DeleteTab(0);
    4604           1 : }
    4605             : 
    4606           1 : void Test::testFormulaMatrixResultUpdate()
    4607             : {
    4608           1 :     m_pDoc->InsertTab(0, "Test");
    4609             : 
    4610           1 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calculation.
    4611             : 
    4612             :     // Set a numeric value to A1.
    4613           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 11.0);
    4614             : 
    4615           2 :     ScMarkData aMark;
    4616           1 :     aMark.SelectOneTable(0);
    4617           1 :     m_pDoc->InsertMatrixFormula(1, 0, 1, 0, aMark, "=A1", NULL);
    4618           1 :     CPPUNIT_ASSERT_EQUAL(11.0, m_pDoc->GetValue(ScAddress(1,0,0)));
    4619           1 :     ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1,0,0));
    4620           1 :     CPPUNIT_ASSERT_MESSAGE("Failed to get formula cell.", pFC);
    4621           1 :     pFC->SetChanged(false); // Clear this flag to simulate displaying of formula cell value on screen.
    4622             : 
    4623           1 :     m_pDoc->SetString(ScAddress(0,0,0), "ABC");
    4624           1 :     CPPUNIT_ASSERT_EQUAL(OUString("ABC"), m_pDoc->GetString(ScAddress(1,0,0)));
    4625           1 :     pFC->SetChanged(false);
    4626             : 
    4627             :     // Put a new value into A1. The formula should update.
    4628           1 :     m_pDoc->SetValue(ScAddress(0,0,0), 13.0);
    4629           1 :     CPPUNIT_ASSERT_EQUAL(13.0, m_pDoc->GetValue(ScAddress(1,0,0)));
    4630             : 
    4631           2 :     m_pDoc->DeleteTab(0);
    4632           1 : }
    4633             : 
    4634           1 : void Test::testExternalRef()
    4635             : {
    4636           1 :     ScDocShellRef xExtDocSh = new ScDocShell;
    4637           2 :     OUString aExtDocName("file:///extdata.fake");
    4638           2 :     OUString aExtSh1Name("Data1");
    4639           2 :     OUString aExtSh2Name("Data2");
    4640           2 :     OUString aExtSh3Name("Data3");
    4641           1 :     SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
    4642           1 :     xExtDocSh->DoInitNew(pMed);
    4643           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
    4644           1 :                            findLoadedDocShellByName(aExtDocName) != NULL);
    4645             : 
    4646             :     // Populate the external source document.
    4647           1 :     ScDocument& rExtDoc = xExtDocSh->GetDocument();
    4648           1 :     rExtDoc.InsertTab(0, aExtSh1Name);
    4649           1 :     rExtDoc.InsertTab(1, aExtSh2Name);
    4650           1 :     rExtDoc.InsertTab(2, aExtSh3Name);
    4651             : 
    4652           2 :     OUString name("Name");
    4653           2 :     OUString value("Value");
    4654           2 :     OUString andy("Andy");
    4655           2 :     OUString bruce("Bruce");
    4656           2 :     OUString charlie("Charlie");
    4657           2 :     OUString david("David");
    4658           2 :     OUString edward("Edward");
    4659           2 :     OUString frank("Frank");
    4660           2 :     OUString george("George");
    4661           2 :     OUString henry("Henry");
    4662             : 
    4663             :     // Sheet 1
    4664           1 :     rExtDoc.SetString(0, 0, 0, name);
    4665           1 :     rExtDoc.SetString(0, 1, 0, andy);
    4666           1 :     rExtDoc.SetString(0, 2, 0, bruce);
    4667           1 :     rExtDoc.SetString(0, 3, 0, charlie);
    4668           1 :     rExtDoc.SetString(0, 4, 0, david);
    4669           1 :     rExtDoc.SetString(1, 0, 0, value);
    4670           1 :     double val = 10;
    4671           1 :     rExtDoc.SetValue(1, 1, 0, val);
    4672           1 :     val = 11;
    4673           1 :     rExtDoc.SetValue(1, 2, 0, val);
    4674           1 :     val = 12;
    4675           1 :     rExtDoc.SetValue(1, 3, 0, val);
    4676           1 :     val = 13;
    4677           1 :     rExtDoc.SetValue(1, 4, 0, val);
    4678             : 
    4679             :     // Sheet 2 remains empty.
    4680             : 
    4681             :     // Sheet 3
    4682           1 :     rExtDoc.SetString(0, 0, 2, name);
    4683           1 :     rExtDoc.SetString(0, 1, 2, edward);
    4684           1 :     rExtDoc.SetString(0, 2, 2, frank);
    4685           1 :     rExtDoc.SetString(0, 3, 2, george);
    4686           1 :     rExtDoc.SetString(0, 4, 2, henry);
    4687           1 :     rExtDoc.SetString(1, 0, 2, value);
    4688           1 :     val = 99;
    4689           1 :     rExtDoc.SetValue(1, 1, 2, val);
    4690           1 :     val = 98;
    4691           1 :     rExtDoc.SetValue(1, 2, 2, val);
    4692           1 :     val = 97;
    4693           1 :     rExtDoc.SetValue(1, 3, 2, val);
    4694           1 :     val = 96;
    4695           1 :     rExtDoc.SetValue(1, 4, 2, val);
    4696             : 
    4697             :     // Test external references on the main document while the external
    4698             :     // document is still in memory.
    4699           1 :     m_pDoc->InsertTab(0, OUString("Test Sheet"));
    4700           1 :     m_pDoc->SetString(0, 0, 0, OUString("='file:///extdata.fake'#Data1.A1"));
    4701           2 :     OUString test = m_pDoc->GetString(0, 0, 0);
    4702           1 :     CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(name));
    4703             : 
    4704             :     // After the initial access to the external document, the external ref
    4705             :     // manager should create sheet cache entries for *all* sheets from that
    4706             :     // document.  Note that the doc may have more than 3 sheets but ensure
    4707             :     // that the first 3 are what we expect.
    4708           1 :     ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
    4709           1 :     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
    4710           2 :     vector<OUString> aTabNames;
    4711           1 :     pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
    4712           1 :     CPPUNIT_ASSERT_MESSAGE("There should be at least 3 sheets.", aTabNames.size() >= 3);
    4713           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[0].equals(aExtSh1Name));
    4714           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[1].equals(aExtSh2Name));
    4715           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected sheet name.", aTabNames[2].equals(aExtSh3Name));
    4716             : 
    4717           1 :     m_pDoc->SetString(1, 0, 0, OUString("='file:///extdata.fake'#Data1.B1"));
    4718           1 :     test = m_pDoc->GetString(1, 0, 0);
    4719           1 :     CPPUNIT_ASSERT_MESSAGE("Value is different from the original", test.equals(value));
    4720             : 
    4721           1 :     m_pDoc->SetString(0, 1, 0, OUString("='file:///extdata.fake'#Data1.A2"));
    4722           1 :     m_pDoc->SetString(0, 2, 0, OUString("='file:///extdata.fake'#Data1.A3"));
    4723           1 :     m_pDoc->SetString(0, 3, 0, OUString("='file:///extdata.fake'#Data1.A4"));
    4724           1 :     m_pDoc->SetString(0, 4, 0, OUString("='file:///extdata.fake'#Data1.A5"));
    4725           1 :     m_pDoc->SetString(0, 5, 0, OUString("='file:///extdata.fake'#Data1.A6"));
    4726             : 
    4727             :     {
    4728             :         // Referencing an empty cell should display '0'.
    4729           1 :         const char* pChecks[] = { "Andy", "Bruce", "Charlie", "David", "0" };
    4730           6 :         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
    4731             :         {
    4732           5 :             test = m_pDoc->GetString(0, static_cast<SCROW>(i+1), 0);
    4733           5 :             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
    4734             :         }
    4735             :     }
    4736           1 :     m_pDoc->SetString(1, 1, 0, OUString("='file:///extdata.fake'#Data1.B2"));
    4737           1 :     m_pDoc->SetString(1, 2, 0, OUString("='file:///extdata.fake'#Data1.B3"));
    4738           1 :     m_pDoc->SetString(1, 3, 0, OUString("='file:///extdata.fake'#Data1.B4"));
    4739           1 :     m_pDoc->SetString(1, 4, 0, OUString("='file:///extdata.fake'#Data1.B5"));
    4740           1 :     m_pDoc->SetString(1, 5, 0, OUString("='file:///extdata.fake'#Data1.B6"));
    4741             :     {
    4742           1 :         double pChecks[] = { 10, 11, 12, 13, 0 };
    4743           6 :         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
    4744             :         {
    4745           5 :             m_pDoc->GetValue(1, static_cast<SCROW>(i+1), 0, val);
    4746           5 :             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", val == pChecks[i]);
    4747             :         }
    4748             :     }
    4749             : 
    4750           1 :     m_pDoc->SetString(2, 0, 0, OUString("='file:///extdata.fake'#Data3.A1"));
    4751           1 :     m_pDoc->SetString(2, 1, 0, OUString("='file:///extdata.fake'#Data3.A2"));
    4752           1 :     m_pDoc->SetString(2, 2, 0, OUString("='file:///extdata.fake'#Data3.A3"));
    4753           1 :     m_pDoc->SetString(2, 3, 0, OUString("='file:///extdata.fake'#Data3.A4"));
    4754             :     {
    4755           1 :         const char* pChecks[] = { "Name", "Edward", "Frank", "George" };
    4756           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
    4757             :         {
    4758           4 :             test = m_pDoc->GetString(2, static_cast<SCROW>(i), 0);
    4759           4 :             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
    4760             :         }
    4761             :     }
    4762             : 
    4763           1 :     m_pDoc->SetString(3, 0, 0, OUString("='file:///extdata.fake'#Data3.B1"));
    4764           1 :     m_pDoc->SetString(3, 1, 0, OUString("='file:///extdata.fake'#Data3.B2"));
    4765           1 :     m_pDoc->SetString(3, 2, 0, OUString("='file:///extdata.fake'#Data3.B3"));
    4766           1 :     m_pDoc->SetString(3, 3, 0, OUString("='file:///extdata.fake'#Data3.B4"));
    4767             :     {
    4768           1 :         const char* pChecks[] = { "Value", "99", "98", "97" };
    4769           5 :         for (size_t i = 0; i < SAL_N_ELEMENTS(pChecks); ++i)
    4770             :         {
    4771           4 :             test = m_pDoc->GetString(3, static_cast<SCROW>(i), 0);
    4772           4 :             CPPUNIT_ASSERT_MESSAGE("Unexpected cell value.", test.equalsAscii(pChecks[i]));
    4773             :         }
    4774             :     }
    4775             : 
    4776             :     // At this point, all accessed cell data from the external document should
    4777             :     // have been cached.
    4778             :     ScExternalRefCache::TableTypeRef pCacheTab = pRefMgr->getCacheTable(
    4779           2 :         nFileId, aExtSh1Name, false);
    4780           1 :     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 1 should exist.", pCacheTab.get() != NULL);
    4781           1 :     ScRange aCachedRange = getCachedRange(pCacheTab);
    4782           2 :     CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
    4783             :                            aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
    4784           1 :                            aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 4);
    4785             : 
    4786             :     // Sheet2 is not referenced at all; the cache table shouldn't even exist.
    4787           1 :     pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh2Name, false);
    4788           1 :     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 2 should *not* exist.", pCacheTab.get() == NULL);
    4789             : 
    4790             :     // Sheet3's row 5 is not referenced; it should not be cached.
    4791           1 :     pCacheTab = pRefMgr->getCacheTable(nFileId, aExtSh3Name, false);
    4792           1 :     CPPUNIT_ASSERT_MESSAGE("Cache table for sheet 3 should exist.", pCacheTab.get() != NULL);
    4793           1 :     aCachedRange = getCachedRange(pCacheTab);
    4794           2 :     CPPUNIT_ASSERT_MESSAGE("Unexpected cached data range.",
    4795             :                            aCachedRange.aStart.Col() == 0 && aCachedRange.aEnd.Col() == 1 &&
    4796           1 :                            aCachedRange.aStart.Row() == 0 && aCachedRange.aEnd.Row() == 3);
    4797             : 
    4798             :     // Unload the external document shell.
    4799           1 :     xExtDocSh->DoClose();
    4800           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
    4801           1 :                            findLoadedDocShellByName(aExtDocName) == NULL);
    4802             : 
    4803           2 :     m_pDoc->DeleteTab(0);
    4804           1 : }
    4805             : 
    4806           1 : void testExtRefFuncT(ScDocument* pDoc, ScDocument& rExtDoc)
    4807             : {
    4808           1 :     Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
    4809           1 :     Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
    4810             : 
    4811           1 :     rExtDoc.SetString(0, 0, 0, OUString("'1.2"));
    4812           1 :     rExtDoc.SetString(0, 1, 0, OUString("Foo"));
    4813           1 :     rExtDoc.SetValue(0, 2, 0, 12.3);
    4814           1 :     pDoc->SetString(0, 0, 0, OUString("=T('file:///extdata.fake'#Data.A1)"));
    4815           1 :     pDoc->SetString(0, 1, 0, OUString("=T('file:///extdata.fake'#Data.A2)"));
    4816           1 :     pDoc->SetString(0, 2, 0, OUString("=T('file:///extdata.fake'#Data.A3)"));
    4817           1 :     pDoc->CalcAll();
    4818             : 
    4819           1 :     OUString aRes = pDoc->GetString(0, 0, 0);
    4820           1 :     CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "1.2" );
    4821           1 :     aRes = pDoc->GetString(0, 1, 0);
    4822           1 :     CPPUNIT_ASSERT_MESSAGE( "Unexpected result with T.", aRes == "Foo" );
    4823           1 :     aRes = pDoc->GetString(0, 2, 0);
    4824           1 :     CPPUNIT_ASSERT_MESSAGE("Unexpected result with T.", aRes.isEmpty());
    4825           1 : }
    4826             : 
    4827           1 : void testExtRefFuncOFFSET(ScDocument* pDoc, ScDocument& rExtDoc)
    4828             : {
    4829           1 :     Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
    4830           1 :     Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
    4831             : 
    4832           1 :     sc::AutoCalcSwitch aACSwitch(*pDoc, true);
    4833             : 
    4834             :     // External document has sheet named 'Data', and the internal doc has sheet named 'Test'.
    4835           1 :     rExtDoc.SetValue(ScAddress(0,1,0), 1.2); // Set 1.2 to A2.
    4836           1 :     pDoc->SetString(ScAddress(0,0,0), "=OFFSET('file:///extdata.fake'#Data.$A$1;1;0;1;1)");
    4837           1 :     CPPUNIT_ASSERT_EQUAL(1.2, pDoc->GetValue(ScAddress(0,0,0)));
    4838           1 : }
    4839             : 
    4840           1 : void testExtRefFuncVLOOKUP(ScDocument* pDoc, ScDocument& rExtDoc)
    4841             : {
    4842           1 :     Test::clearRange(pDoc, ScRange(0, 0, 0, 1, 9, 0));
    4843           1 :     Test::clearRange(&rExtDoc, ScRange(0, 0, 0, 1, 9, 0));
    4844             : 
    4845             :     // Populate the external document.
    4846           1 :     rExtDoc.SetString(ScAddress(0,0,0), "A1");
    4847           1 :     rExtDoc.SetString(ScAddress(0,1,0), "A2");
    4848           1 :     rExtDoc.SetString(ScAddress(0,2,0), "A3");
    4849           1 :     rExtDoc.SetString(ScAddress(0,3,0), "A4");
    4850           1 :     rExtDoc.SetString(ScAddress(0,4,0), "A5");
    4851             : 
    4852           1 :     rExtDoc.SetString(ScAddress(1,0,0), "B1");
    4853           1 :     rExtDoc.SetString(ScAddress(1,1,0), "B2");
    4854           1 :     rExtDoc.SetString(ScAddress(1,2,0), "B3");
    4855           1 :     rExtDoc.SetString(ScAddress(1,3,0), "B4");
    4856           1 :     rExtDoc.SetString(ScAddress(1,4,0), "B5");
    4857             : 
    4858             :     // Put formula in the source document.
    4859             : 
    4860           1 :     pDoc->SetString(ScAddress(0,0,0), "A2");
    4861             : 
    4862             :     // Sort order TRUE
    4863           1 :     pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;1)");
    4864           1 :     CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
    4865             : 
    4866             :     // Sort order FALSE. It should return the same result.
    4867           1 :     pDoc->SetString(ScAddress(1,0,0), "=VLOOKUP(A1;'file:///extdata.fake'#Data.A1:B5;2;0)");
    4868           1 :     CPPUNIT_ASSERT_EQUAL(OUString("B2"), pDoc->GetString(ScAddress(1,0,0)));
    4869           1 : }
    4870             : 
    4871           1 : void Test::testExternalRefFunctions()
    4872             : {
    4873           1 :     ScDocShellRef xExtDocSh = new ScDocShell;
    4874           2 :     OUString aExtDocName("file:///extdata.fake");
    4875           1 :     SfxMedium* pMed = new SfxMedium(aExtDocName, STREAM_STD_READWRITE);
    4876           1 :     xExtDocSh->DoInitNew(pMed);
    4877           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance not loaded.",
    4878           1 :                            findLoadedDocShellByName(aExtDocName) != NULL);
    4879             : 
    4880           1 :     ScExternalRefManager* pRefMgr = m_pDoc->GetExternalRefManager();
    4881           1 :     CPPUNIT_ASSERT_MESSAGE("external reference manager doesn't exist.", pRefMgr);
    4882           1 :     sal_uInt16 nFileId = pRefMgr->getExternalFileId(aExtDocName);
    4883           1 :     const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
    4884           2 :     CPPUNIT_ASSERT_MESSAGE("file name registration has somehow failed.",
    4885           1 :                            pFileName && pFileName->equals(aExtDocName));
    4886             : 
    4887           2 :     sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // turn on auto calc.
    4888             : 
    4889             :     // Populate the external source document.
    4890           1 :     ScDocument& rExtDoc = xExtDocSh->GetDocument();
    4891           1 :     rExtDoc.InsertTab(0, OUString("Data"));
    4892           1 :     double val = 1;
    4893           1 :     rExtDoc.SetValue(0, 0, 0, val);
    4894             :     // leave cell B1 empty.
    4895           1 :     val = 2;
    4896           1 :     rExtDoc.SetValue(0, 1, 0, val);
    4897           1 :     rExtDoc.SetValue(1, 1, 0, val);
    4898           1 :     val = 3;
    4899           1 :     rExtDoc.SetValue(0, 2, 0, val);
    4900           1 :     rExtDoc.SetValue(1, 2, 0, val);
    4901           1 :     val = 4;
    4902           1 :     rExtDoc.SetValue(0, 3, 0, val);
    4903           1 :     rExtDoc.SetValue(1, 3, 0, val);
    4904             : 
    4905           1 :     m_pDoc->InsertTab(0, OUString("Test"));
    4906             : 
    4907             :     struct {
    4908             :         const char* pFormula; double fResult;
    4909             :     } aChecks[] = {
    4910             :         { "=SUM('file:///extdata.fake'#Data.A1:A4)",     10 },
    4911             :         { "=SUM('file:///extdata.fake'#Data.B1:B4)",     9 },
    4912             :         { "=AVERAGE('file:///extdata.fake'#Data.A1:A4)", 2.5 },
    4913             :         { "=AVERAGE('file:///extdata.fake'#Data.B1:B4)", 3 },
    4914             :         { "=COUNT('file:///extdata.fake'#Data.A1:A4)",   4 },
    4915             :         { "=COUNT('file:///extdata.fake'#Data.B1:B4)",   3 }
    4916           1 :     };
    4917             : 
    4918           7 :     for (size_t i = 0; i < SAL_N_ELEMENTS(aChecks); ++i)
    4919             :     {
    4920           6 :         m_pDoc->SetString(0, 0, 0, OUString::createFromAscii(aChecks[i].pFormula));
    4921           6 :         m_pDoc->GetValue(0, 0, 0, val);
    4922           6 :         CPPUNIT_ASSERT_MESSAGE("unexpected result involving external ranges.", val == aChecks[i].fResult);
    4923             :     }
    4924             : 
    4925           1 :     pRefMgr->clearCache(nFileId);
    4926           1 :     testExtRefFuncT(m_pDoc, rExtDoc);
    4927           1 :     testExtRefFuncOFFSET(m_pDoc, rExtDoc);
    4928           1 :     testExtRefFuncVLOOKUP(m_pDoc, rExtDoc);
    4929             : 
    4930             :     // Unload the external document shell.
    4931           1 :     xExtDocSh->DoClose();
    4932           2 :     CPPUNIT_ASSERT_MESSAGE("external document instance should have been unloaded.",
    4933           1 :                            findLoadedDocShellByName(aExtDocName) == NULL);
    4934             : 
    4935           2 :     m_pDoc->DeleteTab(0);
    4936           1 : }
    4937             : 
    4938           1 : void Test::testMatrixOp()
    4939             : {
    4940           1 :     m_pDoc->InsertTab(0, "Test");
    4941             : 
    4942           5 :     for (SCROW nRow = 0; nRow < 4; ++nRow)
    4943             :     {
    4944           4 :         m_pDoc->SetValue(0, nRow, 0, nRow);
    4945             :     }
    4946           1 :     m_pDoc->SetValue(1, 0, 0, 2.0);
    4947           1 :     m_pDoc->SetValue(3, 0, 0, 1.0);
    4948           1 :     m_pDoc->SetValue(3, 1, 0, 2.0);
    4949           1 :     m_pDoc->SetString(2, 0, 0, "=SUMPRODUCT((A1:A4)*B1+D1)");
    4950           1 :     m_pDoc->SetString(2, 1, 0, "=SUMPRODUCT((A1:A4)*B1-D2)");
    4951             : 
    4952           1 :     double nVal = m_pDoc->GetValue(2, 0, 0);
    4953           1 :     CPPUNIT_ASSERT_EQUAL(16.0, nVal);
    4954             : 
    4955           1 :     nVal = m_pDoc->GetValue(2, 1, 0);
    4956           1 :     CPPUNIT_ASSERT_EQUAL(4.0, nVal);
    4957             : 
    4958           1 :     m_pDoc->SetString(4, 0, 0, "=SUMPRODUCT({1;2;4}+8)");
    4959           1 :     m_pDoc->SetString(4, 1, 0, "=SUMPRODUCT(8+{1;2;4})");
    4960           1 :     m_pDoc->SetString(4, 2, 0, "=SUMPRODUCT({1;2;4}-8)");
    4961           1 :     m_pDoc->SetString(4, 3, 0, "=SUMPRODUCT(8-{1;2;4})");
    4962           1 :     m_pDoc->SetString(4, 4, 0, "=SUMPRODUCT({1;2;4}+{8;16;32})");
    4963           1 :     m_pDoc->SetString(4, 5, 0, "=SUMPRODUCT({8;16;32}+{1;2;4})");
    4964           1 :     m_pDoc->SetString(4, 6, 0, "=SUMPRODUCT({1;2;4}-{8;16;32})");
    4965           1 :     m_pDoc->SetString(4, 7, 0, "=SUMPRODUCT({8;16;32}-{1;2;4})");
    4966           1 :     double fResult[8] = { 31.0, 31.0, -17.0, 17.0, 63.0, 63.0, -49.0, 49.0 };
    4967           9 :     for (size_t i = 0; i < SAL_N_ELEMENTS(fResult); ++i)
    4968             :     {
    4969           8 :         CPPUNIT_ASSERT_EQUAL( fResult[i], m_pDoc->GetValue(4, i, 0));
    4970             :     }
    4971             : 
    4972           1 :     m_pDoc->DeleteTab(0);
    4973           4 : }
    4974             : 
    4975             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11